From f8668028646cbabefa5652969aa90ce7b8f332e3 Mon Sep 17 00:00:00 2001 From: Cesar Mendivil Date: Sun, 1 Mar 2026 22:22:20 -0700 Subject: [PATCH] Add configuration files and enhance OAuth integration for YouTube and Facebook --- .env.local | 3 + .idea/.gitignore | 10 + .npmrc | 3 + .yarnrc | 2 + Caddyfile | 30 +- docker-compose.yml | 4 + docker-entrypoint.sh | 7 +- package.json | 10 +- public/oauth/facebook/callback.html | 10 +- server/index.js | 844 +++++++++----- src/setupProxy.js | 3 + src/utils/fbOAuth.js | 59 +- src/utils/ytOAuth.js | 359 ++++-- src/views/Edit/Sources/Network.js | 21 +- src/views/Edit/Wizard/Sources/Network.js | 20 +- src/views/Publication/Edit.js | 2 + src/views/Publication/Services/Facebook.js | 12 +- src/views/Publication/Services/Youtube.js | 123 +- src/views/Settings.js | 21 +- yarn.lock | 1180 ++++---------------- 20 files changed, 1314 insertions(+), 1409 deletions(-) create mode 100644 .env.local create mode 100644 .idea/.gitignore create mode 100644 .npmrc create mode 100644 .yarnrc diff --git a/.env.local b/.env.local new file mode 100644 index 0000000..dfe57dd --- /dev/null +++ b/.env.local @@ -0,0 +1,3 @@ +REACT_APP_CORE_URL=https://restreamer.nextream.sytes.net +REACT_APP_YTDLP_URL=http://192.168.1.20:8282 +REACT_APP_FB_SERVER_URL=http://localhost:3002 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..ab1f416 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..14c1de7 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +# Aumentar el límite de memoria de Node.js para evitar "heap out of memory" +node-options=--max-old-space-size=4096 + diff --git a/.yarnrc b/.yarnrc new file mode 100644 index 0000000..7e11b88 --- /dev/null +++ b/.yarnrc @@ -0,0 +1,2 @@ +--max-old-space-size 4096 + diff --git a/Caddyfile b/Caddyfile index 1a5430f..568d9d8 100644 --- a/Caddyfile +++ b/Caddyfile @@ -9,9 +9,17 @@ handle /fb-server/* { } # ── yt-dlp stream extractor (servicio externo configurable via env) ─────────── -handle /yt-stream/* { - uri strip_prefix /yt-stream - reverse_proxy {env.YTDLP_HOST} +# /yt-stream/{VIDEO_ID} → http://YTDLP_HOST/stream/{VIDEO_ID} +# yt-dlp puede tardar 20-30s — timeouts extendidos a 120s +handle_path /yt-stream/* { + rewrite * /stream{path} + reverse_proxy {env.YTDLP_HOST} { + transport http { + dial_timeout 10s + response_header_timeout 120s + read_timeout 120s + } + } } # OAuth2 callback page — must be served as a static HTML (not the SPA index) @@ -22,13 +30,27 @@ handle /oauth2callback { } } -# Facebook OAuth2 callback popup +# Facebook OAuth2 callback popup — soporta tanto .html como .htm +# .html → servir directamente handle /oauth/facebook/callback.html { file_server { root /ui/build } } +# .htm → reescribir internamente a .html (misma página, misma URL visible para Facebook) +handle /oauth/facebook/callback.htm { + rewrite * /oauth/facebook/callback.html + file_server { + root /ui/build + } +} + +# Sin extensión → redirigir a .html +handle /oauth/facebook/callback { + redir /oauth/facebook/callback.html{query} 302 +} + # SPA — serve static files, fallback to index.html for client-side routing handle { root * /ui/build diff --git a/docker-compose.yml b/docker-compose.yml index 9154652..ec7d2e2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,6 +27,10 @@ services: # Dejar vacío → Caddy proxy /fb-server → localhost:3002 (sin CORS) FB_SERVER_URL: "" + # URL EXACTA registrada en Facebook como "Valid OAuth Redirect URI" + # Debe coincidir con lo que tienes en developers.facebook.com + FB_OAUTH_CALLBACK_URL: "https://djmaster.nextream.sytes.net/oauth/facebook/callback.htm" + # Clave de cifrado para tokens almacenados (cámbiala en producción) FB_ENCRYPTION_SECRET: "restreamer-ui-fb-secret-key-32x!" diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 2ef33f0..13dfd6f 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -9,9 +9,10 @@ cat > "$CONFIG_FILE" < + @@ -126,11 +126,15 @@ show('🔄', 'Intercambiando código…', 'Obteniendo token de larga duración (60 días)…', 'success'); + // redirect_uri debe coincidir EXACTAMENTE con la URL registrada en Facebook + // Usamos window.location.href sin query string para obtener la URL exacta + var thisUrl = window.location.origin + window.location.pathname; + // Enviar el code a la ventana principal; ella llamará al backend if (window.opener) { window.opener.postMessage( { type: 'fb_oauth_result', flow: 'code', code: code, state: state, - redirect_uri: window.location.origin + '/oauth/facebook/callback.html' }, + redirect_uri: thisUrl }, TARGET_ORIGIN ); show('✅', '¡Código enviado!', 'Procesando token de larga duración…', 'success'); @@ -138,7 +142,7 @@ } else { show('⚠️', 'Ventana sin opener', 'Esta página debe abrirse como popup desde Restreamer.', 'warn'); - showDebug('code recibido pero window.opener es null.\ncode: ' + code.slice(0, 20) + '…'); + showDebug('code recibido pero window.opener es null.\ncode: ' + code.slice(0, 20) + '…\nredirect_uri: ' + thisUrl); } return; } diff --git a/server/index.js b/server/index.js index 3cea7de..5e5abfc 100644 --- a/server/index.js +++ b/server/index.js @@ -9,68 +9,97 @@ const http = require('http'); const crypto = require('crypto'); const PORT = parseInt(process.env.FB_SERVER_PORT || '3002', 10); -// FB_DATA_DIR env var allows Docker to mount a persistent volume for tokens -// Default: /data (development) const DATA_DIR = process.env.FB_DATA_DIR ? path.resolve(process.env.FB_DATA_DIR) : path.resolve(__dirname, 'data'); const CFG_PATH = path.join(DATA_DIR, 'config.json'); -// ── Encryption helpers (AES-256-GCM) ───────────────────────────────────────── -// Key is derived from a secret stored in config; if none, tokens are stored as-is. const ENCRYPTION_SECRET = process.env.FB_ENCRYPTION_SECRET || 'restreamer-ui-fb-secret-key-32x!'; +// ───────────────────────────────────────────────────────────────────────────── +// Schema unificado de config.json +// ───────────────────────────────────────────────────────────────────────────── +/** + * { + * "__fb_config": { + * "app_id": string, + * "app_secret": string ← AES-256-GCM encrypted + * }, + * "__yt_config": { + * "client_id": string, + * "client_secret": string ← AES-256-GCM encrypted + * }, + * "fb__": { + * "fb_user_id": string, // PK + * "name": string, + * "token_type": "USER", + * "access_token": string, // AES-256-GCM encrypted long-lived token (60 días) + * "expires_at": number, // Unix ms + * "scope_granted": string[], + * "pages": [{ + * "id": string, // Page ID (PK) + * "name": string, + * "category": string, + * "token_type": "PAGE", + * "access_token": string, // AES-256-GCM encrypted long-lived page token + * "tasks": string[] + * }], + * "updated_at": number + * }, + * "yt__": { + * "account_key": string, // PK = channel_id o "yt_" + * "label": string, // Nombre del canal + * "channel_title": string, + * "channel_id": string, + * "access_token": string, // AES-256-GCM encrypted + * "refresh_token": string, // AES-256-GCM encrypted + * "token_expiry": number, // Unix ms + * "updated_at": number + * } + * } + */ + +// ── Encryption helpers (AES-256-GCM) ───────────────────────────────────────── + function deriveKey(secret) { - return crypto.createHash('sha256').update(secret).digest(); // 32 bytes + return crypto.createHash('sha256').update(secret).digest(); } function encrypt(text) { + if (!text) return ''; try { - const key = deriveKey(ENCRYPTION_SECRET); - const iv = crypto.randomBytes(12); + const key = deriveKey(ENCRYPTION_SECRET); + const iv = crypto.randomBytes(12); const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); - const enc = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]); - const tag = cipher.getAuthTag(); + const enc = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]); + const tag = cipher.getAuthTag(); return iv.toString('hex') + ':' + tag.toString('hex') + ':' + enc.toString('hex'); } catch (_) { - return text; // fallback: store plain if crypto fails + return text; } } function decrypt(data) { + if (!data) return ''; try { - if (!data || !data.includes(':')) return data; - const [ivHex, tagHex, encHex] = data.split(':'); - const key = deriveKey(ENCRYPTION_SECRET); - const iv = Buffer.from(ivHex, 'hex'); - const tag = Buffer.from(tagHex, 'hex'); - const encBuf = Buffer.from(encHex, 'hex'); + if (!data.includes(':')) return data; + const parts = data.split(':'); + if (parts.length < 3) return data; + const [ivHex, tagHex, ...encParts] = parts; + const encHex = encParts.join(':'); + const key = deriveKey(ENCRYPTION_SECRET); + const iv = Buffer.from(ivHex, 'hex'); + const tag = Buffer.from(tagHex, 'hex'); + const encBuf = Buffer.from(encHex, 'hex'); const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv); decipher.setAuthTag(tag); return decipher.update(encBuf, undefined, 'utf8') + decipher.final('utf8'); } catch (_) { - return data; // fallback: return as-is if decryption fails + return data; } } -// ── config.json persistence ─────────────────────────────────────────────────── - -/** - * Schema in config.json: - * { - * "__config": { "app_id": "...", "app_secret": "..." }, - * "": { - * "fb_user_id": string, // PK — Facebook User ID - * "name": string, // Display name - * "token_type": "USER"|"PAGE", // Token type - * "access_token": string, // AES-256-GCM encrypted long-lived token - * "expires_at": number, // Unix ms — when the token expires - * "scope_granted": string[], // Scopes accepted by the user - * "pages": Page[], // List of managed pages (each has its own long-lived token) - * "updated_at": number // Last update Unix ms - * } - * } - */ +// ── config.json I/O ─────────────────────────────────────────────────────────── function loadCfg() { if (!fs.existsSync(CFG_PATH)) return {}; @@ -83,11 +112,73 @@ function saveCfg(data) { fs.writeFileSync(CFG_PATH, JSON.stringify(data, null, 2), 'utf8'); } +// ── Facebook serialization ──────────────────────────────────────────────────── + +function serializeFbAccount(acc) { + return { + ...acc, + access_token: encrypt(acc.access_token || ''), + pages: (acc.pages || []).map((p) => ({ + ...p, + access_token: encrypt(p.access_token || ''), + })), + }; +} + +function deserializeFbAccount(acc) { + if (!acc) return null; + return { + ...acc, + access_token: decrypt(acc.access_token || ''), + pages: (acc.pages || []).map((p) => ({ + ...p, + access_token: decrypt(p.access_token || ''), + })), + }; +} + +function publicFbAccount(acc) { + const { access_token, pages, ...rest } = acc; + return { + ...rest, + pages: (pages || []).map(({ access_token: _t, ...p }) => p), + restreamer_channel_id: acc.restreamer_channel_id || '', + restreamer_publication_id: acc.restreamer_publication_id || '', + }; +} + +// ── YouTube serialization ───────────────────────────────────────────────────── + +function serializeYtAccount(acc) { + return { + ...acc, + access_token: encrypt(acc.access_token || ''), + refresh_token: encrypt(acc.refresh_token || ''), + }; +} + +function deserializeYtAccount(acc) { + if (!acc) return null; + return { + ...acc, + access_token: decrypt(acc.access_token || ''), + refresh_token: decrypt(acc.refresh_token || ''), + }; +} + +function publicYtAccount(acc) { + const { access_token, refresh_token, ...rest } = acc; + return { + ...rest, + has_refresh_token: !!(refresh_token), + has_access_token: !!(access_token), + restreamer_channel_id: acc.restreamer_channel_id || '', + restreamer_publication_id: acc.restreamer_publication_id || '', + }; +} + // ── Facebook Graph API helpers ──────────────────────────────────────────────── -/** - * Simple GET to Facebook Graph API returning parsed JSON. - */ function fbGet(url) { return new Promise((resolve, reject) => { const lib = url.startsWith('https') ? https : http; @@ -102,40 +193,8 @@ function fbGet(url) { }); } -/** - * POST form-encoded data to Facebook Graph API. - */ -function fbPost(url, params) { - return new Promise((resolve, reject) => { - const body = new URLSearchParams(params).toString(); - const urlObj = new URL(url); - const options = { - hostname: urlObj.hostname, - path: urlObj.pathname + urlObj.search, - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': Buffer.byteLength(body), - }, - }; - const req = https.request(options, (res) => { - let buf = ''; - res.on('data', (c) => { buf += c; }); - res.on('end', () => { - try { resolve(JSON.parse(buf)); } - catch (e) { reject(new Error('Invalid JSON: ' + buf.slice(0, 200))); } - }); - }); - req.on('error', reject); - req.write(body); - req.end(); - }); -} -/** - * Exchange short-lived user token → long-lived user token (~60 days). - */ -async function exchangeToLongLived(appId, appSecret, shortToken) { +async function fbExchangeToLongLived(appId, appSecret, shortToken) { const data = await fbGet( `https://graph.facebook.com/v19.0/oauth/access_token` + `?grant_type=fb_exchange_token` + @@ -147,10 +206,7 @@ async function exchangeToLongLived(appId, appSecret, shortToken) { return data; // { access_token, token_type, expires_in } } -/** - * Exchange auth code → short-lived token → long-lived token. - */ -async function exchangeCodeToLongLived(appId, appSecret, code, redirectUri) { +async function fbExchangeCodeToLongLived(appId, appSecret, code, redirectUri) { // Step 1: code → short-lived user token const step1 = await fbGet( `https://graph.facebook.com/v19.0/oauth/access_token` + @@ -160,18 +216,13 @@ async function exchangeCodeToLongLived(appId, appSecret, code, redirectUri) { `&code=${encodeURIComponent(code)}` ); if (step1.error) throw new Error(`Code exchange error: ${step1.error.message}`); - const shortToken = step1.access_token; // Step 2: short-lived → long-lived (~60 days) - const step2 = await exchangeToLongLived(appId, appSecret, shortToken); - return { shortToken, ...step2 }; + const step2 = await fbExchangeToLongLived(appId, appSecret, step1.access_token); + return { shortToken: step1.access_token, ...step2 }; } -/** - * Fetch long-lived tokens for all pages managed by the user. - * Page tokens from /me/accounts with a long-lived user token are already long-lived. - */ -async function fetchPages(longLivedUserToken) { +async function fbFetchPages(longLivedUserToken) { const data = await fbGet( `https://graph.facebook.com/v19.0/me/accounts` + `?fields=id,name,access_token,category,tasks` + @@ -183,28 +234,20 @@ async function fetchPages(longLivedUserToken) { name: p.name, category: p.category || '', tasks: p.tasks || [], - access_token: p.access_token, // long-lived page token + access_token: p.access_token, token_type: 'PAGE', })); } -/** - * Fetch basic user info. - */ -async function fetchUserInfo(token) { +async function fbFetchUserInfo(token) { const data = await fbGet( - `https://graph.facebook.com/v19.0/me` + - `?fields=id,name` + - `&access_token=${encodeURIComponent(token)}` + `https://graph.facebook.com/v19.0/me?fields=id,name&access_token=${encodeURIComponent(token)}` ); if (data.error) throw new Error(data.error.message); return data; } -/** - * Parse granted scopes from token debug info. - */ -async function debugToken(appId, appSecret, token) { +async function fbDebugToken(appId, appSecret, token) { const data = await fbGet( `https://graph.facebook.com/v19.0/debug_token` + `?input_token=${encodeURIComponent(token)}` + @@ -213,50 +256,71 @@ async function debugToken(appId, appSecret, token) { if (data.error) return { scopes: [], expires_at: 0 }; const d = data.data || {}; return { - scopes: d.scopes || [], - expires_at: d.expires_at ? d.expires_at * 1000 : 0, // convert to ms + scopes: d.scopes || [], + expires_at: d.expires_at ? d.expires_at * 1000 : 0, }; } -// ── Serialize / deserialize account (encrypts access_token) ────────────────── +// ── YouTube OAuth2 helpers ──────────────────────────────────────────────────── -function serializeAccount(acc) { - return { - ...acc, - access_token: encrypt(acc.access_token || ''), - pages: (acc.pages || []).map((p) => ({ - ...p, - access_token: encrypt(p.access_token || ''), - })), - }; +async function ytHttpsPost(url, body) { + return new Promise((resolve, reject) => { + const bodyStr = typeof body === 'string' ? body : new URLSearchParams(body).toString(); + const urlObj = new URL(url); + const options = { + hostname: urlObj.hostname, + path: urlObj.pathname + urlObj.search, + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': Buffer.byteLength(bodyStr), + }, + }; + const req = https.request(options, (res) => { + let buf = ''; + res.on('data', (c) => { buf += c; }); + res.on('end', () => { + try { resolve(JSON.parse(buf)); } + catch (e) { reject(new Error('Invalid JSON from Google: ' + buf.slice(0, 200))); } + }); + }); + req.on('error', reject); + req.write(bodyStr); + req.end(); + }); } -function deserializeAccount(acc) { - if (!acc) return null; - return { - ...acc, - access_token: decrypt(acc.access_token || ''), - pages: (acc.pages || []).map((p) => ({ - ...p, - access_token: decrypt(p.access_token || ''), - })), - }; -} - -/** Strip access_token from account before sending to client UI */ -function publicAccount(acc) { - // eslint-disable-next-line no-unused-vars - const { access_token, pages, ...rest } = acc; - return { - ...rest, - pages: (pages || []).map(({ access_token: _t, ...p }) => p), - }; +async function ytFetchChannelInfo(accessToken) { + return new Promise((resolve, reject) => { + const url = 'https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true'; + const req = https.request(url, { + headers: { Authorization: 'Bearer ' + accessToken }, + }, (res) => { + let buf = ''; + res.on('data', (c) => { buf += c; }); + res.on('end', () => { + try { + const data = JSON.parse(buf); + if (data.items && data.items.length > 0) { + resolve({ + channel_title: data.items[0].snippet.title, + channel_id: data.items[0].id, + }); + } else { + resolve({}); + } + } catch (e) { reject(e); } + }); + }); + req.on('error', reject); + req.end(); + }); } // ── Express app ─────────────────────────────────────────────────────────────── const app = express(); -app.use(cors({ origin: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type'] })); +app.use(cors({ origin: true, methods: ['GET','POST','PUT','DELETE','OPTIONS'], allowedHeaders: ['Content-Type'] })); app.use(express.json()); // ── Health ──────────────────────────────────────────────────────────────────── @@ -264,42 +328,53 @@ app.get('/health', (_, res) => { res.json({ ok: true, config: CFG_PATH, port: PORT, ts: new Date().toISOString() }); }); -// ── App config (app_id + app_secret) ───────────────────────────────────────── +// ═════════════════════════════════════════════════════════════════════════════ +// FACEBOOK +// ═════════════════════════════════════════════════════════════════════════════ + +// ── FB App config (app_id + app_secret) ────────────────────────────────────── app.get('/fb/config', (_, res) => { const cfg = loadCfg(); - const c = cfg.__config || {}; - res.json({ app_id: c.app_id || '', has_secret: !!(c.app_secret) }); + const c = cfg.__fb_config || {}; + // Soporte retrocompatibilidad con clave vieja __config + const old = cfg.__config || {}; + res.json({ + app_id: c.app_id || old.app_id || '', + has_secret: !!(c.app_secret || old.app_secret), + }); }); app.put('/fb/config', (req, res) => { const { app_id, app_secret } = req.body || {}; const cfg = loadCfg(); - cfg.__config = { - ...(cfg.__config || {}), - ...(app_id !== undefined ? { app_id: String(app_id) } : {}), - ...(app_secret !== undefined ? { app_secret: String(app_secret) } : {}), + // Migrar de __config a __fb_config si es necesario + if (cfg.__config && !cfg.__fb_config) { + cfg.__fb_config = { ...cfg.__config }; + delete cfg.__config; + } + cfg.__fb_config = { + ...(cfg.__fb_config || {}), + ...(app_id !== undefined ? { app_id: String(app_id) } : {}), + ...(app_secret !== undefined ? { app_secret: encrypt(String(app_secret)) } : {}), }; saveCfg(cfg); res.json({ ok: true }); }); -// ── List accounts (no tokens exposed) ──────────────────────────────────────── +// ── FB Accounts ─────────────────────────────────────────────────────────────── app.get('/fb/accounts', (_, res) => { const cfg = loadCfg(); - const accounts = Object.values(cfg) - .filter((v) => v && v.fb_user_id && v.fb_user_id !== '__config') - .map(deserializeAccount) - .map(publicAccount); + const accounts = Object.entries(cfg) + .filter(([k, v]) => k.startsWith('fb__') && v && v.fb_user_id) + .map(([, v]) => publicFbAccount(deserializeFbAccount(v))); res.json(accounts); }); -// ── Get single account token (for internal use — stream key generation) ─────── -// Returns full account including decrypted token app.get('/fb/accounts/:id/token', (req, res) => { const cfg = loadCfg(); - const raw = cfg[req.params.id]; + const raw = cfg['fb__' + req.params.id]; if (!raw) return res.status(404).json({ error: 'Account not found' }); - const acc = deserializeAccount(raw); + const acc = deserializeFbAccount(raw); res.json({ fb_user_id: acc.fb_user_id, name: acc.name, @@ -311,199 +386,442 @@ app.get('/fb/accounts/:id/token', (req, res) => { }); }); -// ── Delete account ──────────────────────────────────────────────────────────── app.delete('/fb/accounts/:id', (req, res) => { const cfg = loadCfg(); - if (!cfg[req.params.id]) return res.status(404).json({ error: 'Account not found' }); - delete cfg[req.params.id]; + const key = 'fb__' + req.params.id; + if (!cfg[key]) return res.status(404).json({ error: 'Account not found' }); + delete cfg[key]; saveCfg(cfg); res.json({ ok: true }); }); -// ── MAIN FLOW: Exchange auth code → short-lived → long-lived token ───────────── +// Asociar una cuenta FB existente con un canal/publicación de Restreamer +app.put('/fb/accounts/:id/context', (req, res) => { + const { restreamer_channel_id, restreamer_publication_id } = req.body || {}; + const cfg = loadCfg(); + const key = 'fb__' + req.params.id; + if (!cfg[key]) return res.status(404).json({ error: 'Account not found' }); + cfg[key] = { + ...cfg[key], + restreamer_channel_id: restreamer_channel_id || '', + restreamer_publication_id: restreamer_publication_id || '', + }; + saveCfg(cfg); + console.log(`[fb/context] ✅ ${req.params.id} → channel:${restreamer_channel_id || '-'} pub:${restreamer_publication_id || '-'}`); + res.json({ ok: true }); +}); + +// ── FB Exchange: Auth Code → Short-lived → Long-lived (60 días) ────────────── /** * POST /fb/exchange - * Body: { code: string, redirect_uri: string } + * Body: { code, redirect_uri } * - * Flow: - * 1. code → short-lived user token (Graph /oauth/access_token) - * 2. short → long-lived user token (Graph /oauth/access_token?grant_type=fb_exchange_token) + * Flujo completo: + * 1. code → short-lived user token (Graph API /oauth/access_token) + * 2. short-lived → long-lived token (~60 días) (grant_type=fb_exchange_token) * 3. Fetch user info (id, name) - * 4. Debug token (scopes, expires_at) - * 5. Fetch pages with long-lived page tokens - * 6. Persist encrypted in config.json - * 7. Return public account (no tokens) + * 4. Debug token → scopes, expires_at + * 5. Fetch páginas del usuario (tokens de página ya son long-lived) + * 6. Guardar todo cifrado en config.json + * 7. Retornar cuenta pública (sin tokens) */ app.post('/fb/exchange', async (req, res) => { - const { code, redirect_uri } = req.body || {}; + const { code, redirect_uri, restreamer_channel_id, restreamer_publication_id } = req.body || {}; if (!code || !redirect_uri) { return res.status(400).json({ error: 'code and redirect_uri are required' }); } const cfg = loadCfg(); - const c = cfg.__config || {}; - if (!c.app_id || !c.app_secret) { - return res.status(400).json({ error: 'Facebook App ID and App Secret are not configured. Go to Settings → Integrations.' }); + const c = cfg.__fb_config || cfg.__config || {}; + if (!c.app_id) { + return res.status(400).json({ error: 'Facebook App ID not configured. Go to Settings → Integrations.' }); + } + const appSecret = c.app_secret ? decrypt(c.app_secret) : ''; + if (!appSecret) { + return res.status(400).json({ error: 'Facebook App Secret not configured. Go to Settings → Integrations.' }); } try { - // Step 1 + 2: code → long-lived user token - const { access_token: longToken, expires_in } = await exchangeCodeToLongLived( - c.app_id, c.app_secret, code, redirect_uri + const { access_token: longToken, expires_in } = await fbExchangeCodeToLongLived( + c.app_id, appSecret, code, redirect_uri ); - - // expires_in is in seconds; Facebook long-lived tokens expire in ~60 days const expires_at = expires_in ? Date.now() + parseInt(expires_in, 10) * 1000 - : Date.now() + 60 * 24 * 60 * 60 * 1000; // fallback: 60 days + : Date.now() + 60 * 24 * 60 * 60 * 1000; - // Step 3: user info - const userInfo = await fetchUserInfo(longToken); + const userInfo = await fbFetchUserInfo(longToken); + const { scopes } = await fbDebugToken(c.app_id, appSecret, longToken); - // Step 4: scopes - const { scopes } = await debugToken(c.app_id, c.app_secret, longToken); - - // Step 5: pages (already long-lived when fetched with long-lived user token) let pages = []; - try { pages = await fetchPages(longToken); } catch (_) { /* user may have no pages */ } + try { pages = await fbFetchPages(longToken); } catch (_) {} - // Step 6: persist const account = { - fb_user_id: userInfo.id, - name: userInfo.name, - token_type: 'USER', - access_token: longToken, // will be encrypted by serializeAccount + fb_user_id: userInfo.id, + name: userInfo.name, + token_type: 'USER', + access_token: longToken, expires_at, - scope_granted: scopes, + scope_granted: scopes, pages, - updated_at: Date.now(), + restreamer_channel_id: restreamer_channel_id || '', + restreamer_publication_id: restreamer_publication_id || '', + updated_at: Date.now(), }; - - cfg[userInfo.id] = serializeAccount(account); + cfg['fb__' + userInfo.id] = serializeFbAccount(account); saveCfg(cfg); - // Step 7: respond with public info - res.json({ ok: true, account: publicAccount(account) }); + console.log(`[fb/exchange] ✅ ${userInfo.name} (${userInfo.id}) — expires ${new Date(expires_at).toISOString()} → channel:${restreamer_channel_id || '-'} pub:${restreamer_publication_id || '-'}`); + res.json({ ok: true, account: publicFbAccount(account) }); } catch (err) { console.error('[fb/exchange] Error:', err.message); res.status(500).json({ error: err.message }); } }); -// ── Refresh: exchange a still-valid token for a new long-lived one ──────────── -/** - * POST /fb/refresh/:id - * Re-exchanges the stored long-lived token for a fresh one. - * Facebook supports this while the token is still valid. - */ -app.post('/fb/refresh/:id', async (req, res) => { - const cfg = loadCfg(); - const raw = cfg[req.params.id]; - if (!raw) return res.status(404).json({ error: 'Account not found' }); - - const c = cfg.__config || {}; - if (!c.app_id || !c.app_secret) { - return res.status(400).json({ error: 'App ID / Secret not configured' }); - } - - const acc = deserializeAccount(raw); - try { - const { access_token: newToken, expires_in } = await exchangeToLongLived( - c.app_id, c.app_secret, acc.access_token - ); - const expires_at = expires_in - ? Date.now() + parseInt(expires_in, 10) * 1000 - : Date.now() + 60 * 24 * 60 * 60 * 1000; - - const { scopes } = await debugToken(c.app_id, c.app_secret, newToken); - - let pages = acc.pages || []; - try { pages = await fetchPages(newToken); } catch (_) { /* keep old */ } - - const updated = { - ...acc, - access_token: newToken, - expires_at, - scope_granted: scopes, - pages, - updated_at: Date.now(), - }; - - cfg[req.params.id] = serializeAccount(updated); - saveCfg(cfg); - - res.json({ ok: true, account: publicAccount(updated) }); - } catch (err) { - console.error('[fb/refresh] Error:', err.message); - res.status(500).json({ error: err.message }); - } -}); - -// ── Manually save a short-lived token and upgrade it ───────────────────────── -/** - * POST /fb/upgrade - * Body: { access_token: string } (short-lived token from implicit flow) - * Upgrades to long-lived and saves. - */ +// ── FB Upgrade: short-lived token → long-lived (implicit flow fallback) ─────── app.post('/fb/upgrade', async (req, res) => { - const { access_token: shortToken } = req.body || {}; + const { access_token: shortToken, restreamer_channel_id, restreamer_publication_id } = req.body || {}; if (!shortToken) return res.status(400).json({ error: 'access_token is required' }); const cfg = loadCfg(); - const c = cfg.__config || {}; - if (!c.app_id || !c.app_secret) { + const c = cfg.__fb_config || cfg.__config || {}; + const appSecret = c.app_secret ? decrypt(c.app_secret) : c.app_secret_plain || ''; + if (!c.app_id || !appSecret) { return res.status(400).json({ error: 'App ID / Secret not configured. Cannot upgrade token.' }); } try { - const { access_token: longToken, expires_in } = await exchangeToLongLived( - c.app_id, c.app_secret, shortToken - ); + const { access_token: longToken, expires_in } = await fbExchangeToLongLived(c.app_id, appSecret, shortToken); const expires_at = expires_in ? Date.now() + parseInt(expires_in, 10) * 1000 : Date.now() + 60 * 24 * 60 * 60 * 1000; - const userInfo = await fetchUserInfo(longToken); - const { scopes } = await debugToken(c.app_id, c.app_secret, longToken); + const userInfo = await fbFetchUserInfo(longToken); + const { scopes } = await fbDebugToken(c.app_id, appSecret, longToken); let pages = []; - try { pages = await fetchPages(longToken); } catch (_) { /* no pages */ } + try { pages = await fbFetchPages(longToken); } catch (_) {} const account = { - fb_user_id: userInfo.id, - name: userInfo.name, - token_type: 'USER', - access_token: longToken, + fb_user_id: userInfo.id, + name: userInfo.name, + token_type: 'USER', + access_token: longToken, expires_at, - scope_granted: scopes, + scope_granted: scopes, pages, - updated_at: Date.now(), + restreamer_channel_id: restreamer_channel_id || '', + restreamer_publication_id: restreamer_publication_id || '', + updated_at: Date.now(), }; - - cfg[userInfo.id] = serializeAccount(account); + cfg['fb__' + userInfo.id] = serializeFbAccount(account); saveCfg(cfg); - res.json({ ok: true, account: publicAccount(account) }); + console.log(`[fb/upgrade] ✅ Token upgraded: ${userInfo.name} (${userInfo.id})`); + res.json({ ok: true, account: publicFbAccount(account) }); } catch (err) { console.error('[fb/upgrade] Error:', err.message); res.status(500).json({ error: err.message }); } }); +// ── FB Refresh: renovar long-lived token existente ──────────────────────────── +app.post('/fb/refresh/:id', async (req, res) => { + const cfg = loadCfg(); + const raw = cfg['fb__' + req.params.id]; + if (!raw) return res.status(404).json({ error: 'Account not found' }); + + const c = cfg.__fb_config || cfg.__config || {}; + const appSecret = c.app_secret ? decrypt(c.app_secret) : ''; + if (!c.app_id || !appSecret) { + return res.status(400).json({ error: 'App ID / Secret not configured' }); + } + + const acc = deserializeFbAccount(raw); + try { + const { access_token: newToken, expires_in } = await fbExchangeToLongLived(c.app_id, appSecret, acc.access_token); + const expires_at = expires_in + ? Date.now() + parseInt(expires_in, 10) * 1000 + : Date.now() + 60 * 24 * 60 * 60 * 1000; + + const { scopes } = await fbDebugToken(c.app_id, appSecret, newToken); + let pages = acc.pages || []; + try { pages = await fbFetchPages(newToken); } catch (_) {} + + const updated = { ...acc, access_token: newToken, expires_at, scope_granted: scopes, pages, updated_at: Date.now() }; + cfg['fb__' + req.params.id] = serializeFbAccount(updated); + saveCfg(cfg); + + console.log(`[fb/refresh] ✅ Token refreshed: ${acc.name} (${req.params.id})`); + res.json({ ok: true, account: publicFbAccount(updated) }); + } catch (err) { + console.error('[fb/refresh] Error:', err.message); + res.status(500).json({ error: err.message }); + } +}); + +// ═════════════════════════════════════════════════════════════════════════════ +// YOUTUBE +// ═════════════════════════════════════════════════════════════════════════════ + +// ── YT Credentials (client_id + client_secret) ─────────────────────────────── +app.get('/yt/config', (_, res) => { + const cfg = loadCfg(); + const c = cfg.__yt_config || {}; + res.json({ client_id: c.client_id || '', has_secret: !!(c.client_secret) }); +}); + +app.put('/yt/config', (req, res) => { + const { client_id, client_secret } = req.body || {}; + const cfg = loadCfg(); + cfg.__yt_config = { + ...(cfg.__yt_config || {}), + ...(client_id !== undefined ? { client_id: String(client_id) } : {}), + ...(client_secret !== undefined ? { client_secret: encrypt(String(client_secret)) } : {}), + }; + saveCfg(cfg); + console.log(`[yt/config] ✅ Credentials saved (client_id: ${client_id || '(unchanged)'})`); + res.json({ ok: true }); +}); + +// Versión completa con secreto descifrado (uso interno — intercambio de tokens) +app.get('/yt/config/full', (_, res) => { + const cfg = loadCfg(); + const c = cfg.__yt_config || {}; + res.json({ + client_id: c.client_id || '', + client_secret: c.client_secret ? decrypt(c.client_secret) : '', + }); +}); + +// ── YT Accounts ─────────────────────────────────────────────────────────────── +app.get('/yt/accounts', (_, res) => { + const cfg = loadCfg(); + const accounts = Object.entries(cfg) + .filter(([k, v]) => k.startsWith('yt__') && v && v.account_key) + .map(([, v]) => publicYtAccount(deserializeYtAccount(v))); + res.json(accounts); +}); + +app.get('/yt/accounts/:key/token', (req, res) => { + const cfg = loadCfg(); + const raw = cfg['yt__' + req.params.key]; + if (!raw) return res.status(404).json({ error: 'YT account not found' }); + const acc = deserializeYtAccount(raw); + res.json({ + account_key: acc.account_key, + label: acc.label, + channel_title: acc.channel_title || '', + channel_id: acc.channel_id || '', + access_token: acc.access_token, + refresh_token: acc.refresh_token, + token_expiry: acc.token_expiry, + }); +}); + +app.delete('/yt/accounts/:key', (req, res) => { + const cfg = loadCfg(); + const key = 'yt__' + req.params.key; + if (!cfg[key]) return res.status(404).json({ error: 'YT account not found' }); + delete cfg[key]; + saveCfg(cfg); + res.json({ ok: true }); +}); + +// Asociar una cuenta YT existente con un canal/publicación de Restreamer +app.put('/yt/accounts/:key/context', (req, res) => { + const { restreamer_channel_id, restreamer_publication_id } = req.body || {}; + const cfg = loadCfg(); + const key = 'yt__' + req.params.key; + if (!cfg[key]) return res.status(404).json({ error: 'YT account not found' }); + cfg[key] = { + ...cfg[key], + restreamer_channel_id: restreamer_channel_id || '', + restreamer_publication_id: restreamer_publication_id || '', + }; + saveCfg(cfg); + console.log(`[yt/context] ✅ ${req.params.key} → channel:${restreamer_channel_id || '-'} pub:${restreamer_publication_id || '-'}`); + res.json({ ok: true }); +}); + +// ── YT Save account (tras el intercambio de código en el browser) ───────────── +/** + * POST /yt/accounts + * Body: { account_key, label, channel_title, channel_id, access_token, refresh_token, token_expiry } + * + * El browser ya hizo el intercambio code→token con Google. + * Este endpoint solo persiste el resultado cifrado en config.json. + */ +app.post('/yt/accounts', (req, res) => { + const { + account_key, label, channel_title, channel_id, + access_token, refresh_token, token_expiry, + restreamer_channel_id, restreamer_publication_id, + } = req.body || {}; + if (!account_key || !access_token) { + return res.status(400).json({ error: 'account_key and access_token are required' }); + } + + const cfg = loadCfg(); + const existing = cfg['yt__' + account_key] ? deserializeYtAccount(cfg['yt__' + account_key]) : {}; + + const account = { + account_key, + label: label || existing.label || account_key, + channel_title: channel_title || existing.channel_title || '', + channel_id: channel_id || existing.channel_id || '', + access_token, + refresh_token: refresh_token || existing.refresh_token || '', + token_expiry: token_expiry || existing.token_expiry || 0, + restreamer_channel_id: restreamer_channel_id || existing.restreamer_channel_id || '', + restreamer_publication_id: restreamer_publication_id || existing.restreamer_publication_id || '', + updated_at: Date.now(), + }; + + cfg['yt__' + account_key] = serializeYtAccount(account); + saveCfg(cfg); + + console.log(`[yt/accounts] ✅ Account saved: ${label || account_key} (${channel_id || account_key}) → channel:${restreamer_channel_id || '-'} pub:${restreamer_publication_id || '-'}`); + res.json({ ok: true, account: publicYtAccount(account) }); +}); + +// ── YT Refresh token: usar refresh_token para obtener nuevo access_token ────── +/** + * POST /yt/accounts/:key/refresh + * Usa el refresh_token almacenado + credentials para obtener un nuevo access_token. + */ +app.post('/yt/accounts/:key/refresh', async (req, res) => { + const cfg = loadCfg(); + const raw = cfg['yt__' + req.params.key]; + if (!raw) return res.status(404).json({ error: 'YT account not found' }); + + const c = cfg.__yt_config || {}; + const clientSecret = c.client_secret ? decrypt(c.client_secret) : ''; + if (!c.client_id || !clientSecret) { + return res.status(400).json({ error: 'YouTube OAuth2 credentials not configured in Settings → Integrations' }); + } + + const acc = deserializeYtAccount(raw); + if (!acc.refresh_token) { + return res.status(400).json({ error: 'No refresh token stored for this account' }); + } + + try { + const data = await ytHttpsPost('https://oauth2.googleapis.com/token', { + client_id: c.client_id, + client_secret: clientSecret, + refresh_token: acc.refresh_token, + grant_type: 'refresh_token', + }); + + if (data.error) throw new Error(data.error_description || data.error); + + const updated = { + ...acc, + access_token: data.access_token, + token_expiry: Date.now() + (data.expires_in || 3600) * 1000, + updated_at: Date.now(), + }; + cfg['yt__' + req.params.key] = serializeYtAccount(updated); + saveCfg(cfg); + + console.log(`[yt/refresh] ✅ Token refreshed: ${acc.label || req.params.key}`); + res.json({ ok: true, access_token: data.access_token, token_expiry: updated.token_expiry }); + } catch (err) { + console.error('[yt/refresh] Error:', err.message); + res.status(500).json({ error: err.message }); + } +}); + +// ── YT Exchange: Auth Code → tokens (server-side, sin exponer client_secret) ── +/** + * POST /yt/exchange + * Body: { code, redirect_uri } + * + * Flujo: + * 1. Intercambia code → access_token + refresh_token (Google /token) + * 2. Obtiene info del canal + * 3. Guarda cifrado en config.json + */ +app.post('/yt/exchange', async (req, res) => { + const { code, redirect_uri, restreamer_channel_id, restreamer_publication_id } = req.body || {}; + if (!code || !redirect_uri) { + return res.status(400).json({ error: 'code and redirect_uri are required' }); + } + + const cfg = loadCfg(); + const c = cfg.__yt_config || {}; + const clientSecret = c.client_secret ? decrypt(c.client_secret) : ''; + if (!c.client_id || !clientSecret) { + return res.status(400).json({ error: 'YouTube OAuth2 credentials not configured in Settings → Integrations' }); + } + + try { + const tokenData = await ytHttpsPost('https://oauth2.googleapis.com/token', { + code, + client_id: c.client_id, + client_secret: clientSecret, + redirect_uri, + grant_type: 'authorization_code', + }); + if (tokenData.error) throw new Error(tokenData.error_description || tokenData.error); + + let channelInfo = {}; + try { channelInfo = await ytFetchChannelInfo(tokenData.access_token); } catch (_) {} + + const accountKey = channelInfo.channel_id || ('yt_' + Date.now()); + const channelName = channelInfo.channel_title || accountKey; + + const account = { + account_key: accountKey, + label: channelName, + channel_title: channelInfo.channel_title || '', + channel_id: channelInfo.channel_id || '', + access_token: tokenData.access_token, + refresh_token: tokenData.refresh_token || '', + token_expiry: Date.now() + (tokenData.expires_in || 3600) * 1000, + restreamer_channel_id: restreamer_channel_id || '', + restreamer_publication_id: restreamer_publication_id || '', + updated_at: Date.now(), + }; + cfg['yt__' + accountKey] = serializeYtAccount(account); + saveCfg(cfg); + + console.log(`[yt/exchange] ✅ Account saved: ${channelName} (${accountKey}) → channel:${restreamer_channel_id || '-'} pub:${restreamer_publication_id || '-'}`); + res.json({ ok: true, account: publicYtAccount(account) }); + } catch (err) { + console.error('[yt/exchange] Error:', err.message); + res.status(500).json({ error: err.message }); + } +}); + // ── Start server ────────────────────────────────────────────────────────────── app.listen(PORT, '0.0.0.0', () => { - console.log(`\n[fb-server] ✅ http://0.0.0.0:${PORT}`); - console.log(`[fb-server] 💾 Config: ${CFG_PATH}`); - console.log(`[fb-server] 🔐 Encryption: AES-256-GCM\n`); + console.log(`\n[server] ✅ http://0.0.0.0:${PORT}`); + console.log(`[server] 💾 Config: ${CFG_PATH}`); + console.log(`[server] 🔐 Encryption: AES-256-GCM\n`); console.log(' GET /health'); + console.log(' ── Facebook ──────────────────────────────────────────'); console.log(' GET /fb/config'); console.log(' PUT /fb/config { app_id, app_secret }'); console.log(' GET /fb/accounts'); console.log(' GET /fb/accounts/:id/token'); console.log(' DELETE /fb/accounts/:id'); - console.log(' POST /fb/exchange { code, redirect_uri } ← Auth Code flow'); - console.log(' POST /fb/refresh/:id ← Renew long-lived token'); - console.log(' POST /fb/upgrade { access_token } ← Upgrade short-lived token\n'); + console.log(' PUT /fb/accounts/:id/context { restreamer_channel_id, restreamer_publication_id }'); + console.log(' POST /fb/exchange { code, redirect_uri, restreamer_channel_id, restreamer_publication_id } ← Auth Code→Long-lived'); + console.log(' POST /fb/upgrade { access_token, restreamer_channel_id, restreamer_publication_id } ← Short-lived→Long-lived'); + console.log(' POST /fb/refresh/:id ← Renew long-lived token'); + console.log(' ── YouTube ───────────────────────────────────────────'); + console.log(' GET /yt/config'); + console.log(' PUT /yt/config { client_id, client_secret }'); + console.log(' GET /yt/config/full'); + console.log(' GET /yt/accounts'); + console.log(' GET /yt/accounts/:key/token'); + console.log(' DELETE /yt/accounts/:key'); + console.log(' PUT /yt/accounts/:key/context { restreamer_channel_id, restreamer_publication_id }'); + console.log(' POST /yt/accounts { account_key, access_token, restreamer_channel_id, restreamer_publication_id, ... }'); + console.log(' POST /yt/exchange { code, redirect_uri, restreamer_channel_id, restreamer_publication_id } ← Auth Code flow'); + console.log(' POST /yt/accounts/:key/refresh ← Refresh access_token\n'); }); process.on('SIGINT', () => process.exit(0)); diff --git a/src/setupProxy.js b/src/setupProxy.js index 03267b5..4ea3397 100644 --- a/src/setupProxy.js +++ b/src/setupProxy.js @@ -57,6 +57,7 @@ module.exports = function (app) { app.use('/diskfs', coreProxy); // yt-dlp stream extractor: /yt-stream/{VIDEO_ID} → /stream/{VIDEO_ID} + // yt-dlp puede tardar hasta 30-60s en extraer la URL — timeout extendido app.use( '/yt-stream', createProxyMiddleware({ @@ -64,6 +65,8 @@ module.exports = function (app) { changeOrigin: true, secure: false, ws: false, + proxyTimeout: 120000, // 120s — yt-dlp puede tardar bastante + timeout: 120000, pathRewrite: { '^/yt-stream': '/stream' }, onError: (err, req, res) => { console.error(`[setupProxy] yt-dlp proxy error: ${err.code} — ${err.message}`); diff --git a/src/utils/fbOAuth.js b/src/utils/fbOAuth.js index 9c136af..1ff3e40 100644 --- a/src/utils/fbOAuth.js +++ b/src/utils/fbOAuth.js @@ -85,14 +85,13 @@ async function _serverDelete(path) { const getConfig = async () => { try { const data = await _serverGet('/fb/config'); - // cache app_id locally const c = _loadCache(); - c.__config = { app_id: data.app_id || '', has_secret: !!data.has_secret }; + c.__fb_config = { app_id: data.app_id || '', has_secret: !!data.has_secret }; _saveCache(c); - return c.__config; + return c.__fb_config; } catch (_) { const c = _loadCache(); - return c.__config || { app_id: '', has_secret: false }; + return c.__fb_config || c.__config || { app_id: '', has_secret: false }; } }; @@ -101,7 +100,7 @@ const getConfig = async () => { */ const getAppId = () => { const c = _loadCache(); - return c.__config?.app_id || ''; + return c.__fb_config?.app_id || c.__config?.app_id || ''; }; /** @@ -114,7 +113,11 @@ const saveConfig = async ({ app_id, app_secret }) => { const data = await _serverPut('/fb/config', body); if (data.ok) { const c = _loadCache(); - c.__config = { ...(c.__config || {}), app_id: app_id ?? c.__config?.app_id ?? '', has_secret: true }; + c.__fb_config = { + ...(c.__fb_config || c.__config || {}), + app_id: app_id ?? c.__fb_config?.app_id ?? '', + has_secret: app_secret ? true : !!(c.__fb_config?.has_secret), + }; _saveCache(c); } return data; @@ -208,7 +211,13 @@ const authorizeWithPopup = (appId, hasSecret = false) => { } return new Promise((resolve, reject) => { - const redirectUri = window.location.origin + '/oauth/facebook/callback.html'; + // Usar FB_OAUTH_CALLBACK_URL si está definida (permite usar .htm o cualquier dominio) + // De lo contrario construir desde window.location.origin + const _rtCfg = (typeof window !== 'undefined' && window.__RESTREAMER_CONFIG__) || {}; + const redirectUri = _rtCfg.FB_OAUTH_CALLBACK_URL + ? _rtCfg.FB_OAUTH_CALLBACK_URL + : window.location.origin + '/oauth/facebook/callback.html'; + const scope = 'publish_video,pages_manage_posts,pages_read_engagement,pages_show_list,public_profile'; // Prefer Auth Code flow when App Secret is available (gives 60-day tokens) @@ -273,16 +282,19 @@ const authorizeWithPopup = (appId, hasSecret = false) => { * * @param {string} appId * @param {boolean} hasSecret + * @param {{ restreamer_channel_id?: string, restreamer_publication_id?: string }} [context] * @returns {Promise<{ account: object, tokenType: 'long'|'short', expiresAt: number }>} */ -const connectAccount = async (appId, hasSecret) => { +const connectAccount = async (appId, hasSecret, context = {}) => { const result = await authorizeWithPopup(appId, hasSecret); if (result.flow === 'code' && result.code) { // ── Auth Code flow → server does everything ────────────────────────── const data = await _serverPost('/fb/exchange', { - code: result.code, - redirect_uri: result.redirect_uri, + code: result.code, + redirect_uri: result.redirect_uri, + restreamer_channel_id: context.restreamer_channel_id || '', + restreamer_publication_id: context.restreamer_publication_id || '', }); if (data.error) throw new Error(data.error); // refresh cache @@ -293,7 +305,11 @@ const connectAccount = async (appId, hasSecret) => { if (result.flow === 'token' && result.access_token) { // ── Implicit flow → try to upgrade via server ──────────────────────── try { - const data = await _serverPost('/fb/upgrade', { access_token: result.access_token }); + const data = await _serverPost('/fb/upgrade', { + access_token: result.access_token, + restreamer_channel_id: context.restreamer_channel_id || '', + restreamer_publication_id: context.restreamer_publication_id || '', + }); if (data.ok) { await listAccounts(); return { account: data.account, tokenType: 'long', expiresAt: data.account.expires_at }; @@ -304,14 +320,16 @@ const connectAccount = async (appId, hasSecret) => { const userInfo = await fetchUserInfo(result.access_token); const pages = await fetchUserPages(result.access_token).catch(() => []); const shortAcc = { - fb_user_id: userInfo.id, - name: userInfo.name, - token_type: 'USER', - access_token: result.access_token, - expires_at: result.expires_in ? Date.now() + result.expires_in * 1000 : 0, - scope_granted: [], + fb_user_id: userInfo.id, + name: userInfo.name, + token_type: 'USER', + access_token: result.access_token, + expires_at: result.expires_in ? Date.now() + result.expires_in * 1000 : 0, + scope_granted: [], pages, - updated_at: Date.now(), + restreamer_channel_id: context.restreamer_channel_id || '', + restreamer_publication_id: context.restreamer_publication_id || '', + updated_at: Date.now(), }; // cache in localStorage (no server persistence) const c = _loadCache(); @@ -319,7 +337,6 @@ const connectAccount = async (appId, hasSecret) => { const idx = c.__accounts.findIndex((a) => a.fb_user_id === userInfo.id); const pub = { ...shortAcc }; delete pub.access_token; if (idx >= 0) c.__accounts[idx] = pub; else c.__accounts.push(pub); - // store token separately for local use c[userInfo.id] = shortAcc; _saveCache(c); @@ -382,10 +399,10 @@ const getTokenForEntry = async (fbUserId, pageId) => { } }; -// ── Legacy compatibility (localStorage-only, for components not yet migrated) ─ +// ── Legacy compatibility ────────────────────────────────────────────────────── const setAppId = (app_id) => { const c = _loadCache(); - c.__config = { ...(c.__config || {}), app_id }; + c.__fb_config = { ...(c.__fb_config || c.__config || {}), app_id }; _saveCache(c); }; diff --git a/src/utils/ytOAuth.js b/src/utils/ytOAuth.js index df77c24..81bd7bb 100644 --- a/src/utils/ytOAuth.js +++ b/src/utils/ytOAuth.js @@ -1,129 +1,269 @@ /** - * ytOAuth.js — Almacenamiento global de credenciales OAuth2 de YouTube en localStorage. + * ytOAuth.js — YouTube OAuth2 client utility para Restreamer UI. * - * Las claves se guardan bajo el prefijo @@restreamer-ui@@yt_oauth_ - * de modo que persisten entre sesiones y son compartidas por todas - * las publicaciones de YouTube del mismo canal. + * Persistencia: + * - Credenciales (client_id, client_secret) y cuentas (tokens) se guardan + * en el servidor → /data/fb/config.json (volumen Docker persistente). + * - localStorage se usa solo como caché para render inmediato (sin round-trip). * - * Estructura almacenada: - * { - * client_id: string, - * client_secret: string, - * accounts: { - * [accountKey]: { - * label: string, // nombre amigable ej. "Mi Canal" - * access_token: string, - * refresh_token: string, - * token_expiry: number, // timestamp ms - * email: string, // email de la cuenta Google (opcional) - * channel_title: string, // nombre del canal YouTube (opcional) - * channel_id: string, // UCxxxx - * } - * } - * } + * Estructura en config.json (gestionado por server/index.js): + * __yt_config: { client_id, client_secret (cifrado AES-256-GCM) } + * yt__: { + * account_key, label, channel_title, channel_id, + * access_token (cifrado), refresh_token (cifrado), + * token_expiry (Unix ms), updated_at + * } */ -const STORAGE_KEY = 'yt_oauth'; -const PREFIX = '@@restreamer-ui@@'; +const LS_KEY = '@@restreamer-ui@@yt_oauth_v2'; -const _read = () => { +// Mismo base URL que fbOAuth — Caddy proxea /fb-server → Node :3002 +const _rtCfg = (typeof window !== 'undefined' && window.__RESTREAMER_CONFIG__) || {}; +const SERVER_BASE = _rtCfg.FB_SERVER_URL + ? _rtCfg.FB_SERVER_URL.replace(/\/$/, '') + : '/fb-server'; + +// ── localStorage cache ──────────────────────────────────────────────────────── +function _readCache() { try { - const raw = window.localStorage.getItem(PREFIX + STORAGE_KEY); + const raw = localStorage.getItem(LS_KEY); return raw ? JSON.parse(raw) : { client_id: '', client_secret: '', accounts: {} }; } catch { return { client_id: '', client_secret: '', accounts: {} }; } -}; +} -const _write = (data) => { - try { - window.localStorage.setItem(PREFIX + STORAGE_KEY, JSON.stringify(data)); - } catch (e) { - console.warn('[ytOAuth] Cannot write to localStorage:', e); - } -}; +function _writeCache(data) { + try { localStorage.setItem(LS_KEY, JSON.stringify(data)); } + catch (_) {} +} -/** Obtener toda la config */ -const getAll = () => _read(); +// ── Server helpers ──────────────────────────────────────────────────────────── +async function _get(path) { + const res = await fetch(SERVER_BASE + path); + return res.json(); +} -/** Guardar client_id y client_secret globales */ +async function _put(path, body) { + const res = await fetch(SERVER_BASE + path, { + method: 'PUT', headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + return res.json(); +} + +async function _post(path, body) { + const res = await fetch(SERVER_BASE + path, { + method: 'POST', headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + return res.json(); +} + +async function _del(path) { + const res = await fetch(SERVER_BASE + path, { method: 'DELETE' }); + return res.json(); +} + +// ── Credentials ─────────────────────────────────────────────────────────────── + +/** Guardar en localStorage (sync, para compatibilidad legacy) */ const setCredentials = (client_id, client_secret) => { - const data = _read(); - data.client_id = client_id || ''; - data.client_secret = client_secret || ''; - _write(data); + const cache = _readCache(); + cache.client_id = client_id || ''; + cache.client_secret = client_secret || ''; + _writeCache(cache); }; -/** Obtener credenciales globales */ -const getCredentials = () => { - const data = _read(); - return { client_id: data.client_id || '', client_secret: data.client_secret || '' }; -}; - -/** Guardar / actualizar una cuenta conectada */ -const saveAccount = (accountKey, accountData) => { - const data = _read(); - if (!data.accounts) data.accounts = {}; - data.accounts[accountKey] = { - ...(data.accounts[accountKey] || {}), - ...accountData, - updated_at: Date.now(), - }; - _write(data); -}; - -/** Obtener todas las cuentas conectadas */ -const getAccounts = () => { - const data = _read(); - return data.accounts || {}; -}; - -/** Obtener una cuenta por key */ -const getAccount = (accountKey) => { - const data = _read(); - return (data.accounts || {})[accountKey] || null; -}; - -/** Eliminar una cuenta */ -const removeAccount = (accountKey) => { - const data = _read(); - if (data.accounts && data.accounts[accountKey]) { - delete data.accounts[accountKey]; - _write(data); +/** + * Guardar credenciales → servidor (config.json) + localStorage. + * @returns {Promise<{ok: boolean}>} + */ +const saveCredentials = async (client_id, client_secret) => { + setCredentials(client_id, client_secret); // caché inmediata + try { + return await _put('/yt/config', { client_id: client_id || '', client_secret: client_secret || '' }); + } catch (err) { + console.warn('[ytOAuth] saveCredentials server error:', err.message); + return { ok: false, error: err.message }; } }; -/** Listar cuentas como array [{key, label, email, channel_title, ...}] */ -const listAccounts = () => { - const data = _read(); - return Object.entries(data.accounts || {}).map(([key, val]) => ({ key, ...val })); +/** Obtener credenciales desde localStorage (sync) */ +const getCredentials = () => { + const cache = _readCache(); + return { client_id: cache.client_id || '', client_secret: cache.client_secret || '' }; }; -/** Refrescar access_token usando el refresh_token almacenado */ +/** + * Sincronizar credenciales desde el servidor → localStorage. + * Llamar al montar Settings para cargar lo que está en config.json. + */ +const loadCredentialsFromServer = async () => { + try { + // /yt/config/full incluye el client_secret descifrado + const data = await _get('/yt/config/full'); + if (data.client_id) { + setCredentials(data.client_id, data.client_secret || ''); + } + return { client_id: data.client_id || '', client_secret: data.client_secret || '' }; + } catch (_) { + return getCredentials(); + } +}; + +// ── Accounts ────────────────────────────────────────────────────────────────── + +/** + * Guardar cuenta en servidor (persiste en config.json cifrado) + caché. + */ +const saveAccount = async (accountKey, accountData) => { + // 1. Actualizar caché inmediatamente + const cache = _readCache(); + if (!cache.accounts) cache.accounts = {}; + cache.accounts[accountKey] = { ...(cache.accounts[accountKey] || {}), ...accountData, updated_at: Date.now() }; + _writeCache(cache); + + // 2. Persistir en servidor + try { + return await _post('/yt/accounts', { + account_key: accountKey, + label: accountData.label || accountData.channel_title || accountKey, + channel_title: accountData.channel_title || '', + channel_id: accountData.channel_id || '', + access_token: accountData.access_token || '', + refresh_token: accountData.refresh_token || '', + token_expiry: accountData.token_expiry || 0, + restreamer_channel_id: accountData.restreamer_channel_id || '', + restreamer_publication_id: accountData.restreamer_publication_id || '', + }); + } catch (err) { + console.warn('[ytOAuth] saveAccount server error:', err.message); + return { ok: false, error: err.message }; + } +}; + +/** + * Guardar cuenta sync en localStorage (legacy — para flujos que no son async). + */ +const saveAccountSync = (accountKey, accountData) => { + const cache = _readCache(); + if (!cache.accounts) cache.accounts = {}; + cache.accounts[accountKey] = { ...(cache.accounts[accountKey] || {}), ...accountData, updated_at: Date.now() }; + _writeCache(cache); +}; + +/** + * Cargar lista de cuentas desde el servidor → actualiza caché. + */ +const loadAccountsFromServer = async () => { + try { + const accounts = await _get('/yt/accounts'); + if (Array.isArray(accounts)) { + const cache = _readCache(); + // Convertir array a mapa { [account_key]: data } + cache.accounts = {}; + accounts.forEach((a) => { cache.accounts[a.account_key] = a; }); + _writeCache(cache); + return cache.accounts; + } + } catch (_) {} + return _readCache().accounts || {}; +}; + +/** + * Obtener token completo (con access_token + refresh_token) desde el servidor. + */ +const getAccountToken = async (accountKey) => { + try { + return await _get(`/yt/accounts/${encodeURIComponent(accountKey)}/token`); + } catch (_) { + return getAccount(accountKey); + } +}; + +/** Obtener todas las cuentas desde localStorage (sync) */ +const getAccounts = () => { + return _readCache().accounts || {}; +}; + +/** Obtener una cuenta por key desde localStorage (sync) */ +const getAccount = (accountKey) => { + return (_readCache().accounts || {})[accountKey] || null; +}; + +/** Obtener todos los datos */ +const getAll = () => _readCache(); + +/** Listar cuentas como array */ +const listAccounts = () => { + return Object.entries(_readCache().accounts || {}).map(([key, val]) => ({ key, ...val })); +}; + +/** + * Eliminar cuenta del servidor + caché. + */ +const removeAccount = async (accountKey) => { + // 1. Eliminar del caché + const cache = _readCache(); + if (cache.accounts && cache.accounts[accountKey]) { + delete cache.accounts[accountKey]; + _writeCache(cache); + } + // 2. Eliminar del servidor + try { + return await _del(`/yt/accounts/${encodeURIComponent(accountKey)}`); + } catch (err) { + console.warn('[ytOAuth] removeAccount server error:', err.message); + return { ok: false }; + } +}; + +// ── Token management ────────────────────────────────────────────────────────── + +/** + * Refrescar access_token usando el refresh_token del servidor. + * El servidor hace la llamada a Google y actualiza config.json. + */ const refreshAccount = async (accountKey) => { - const creds = getCredentials(); - const account = getAccount(accountKey); - if (!account || !account.refresh_token) throw new Error('No refresh token for account: ' + accountKey); - if (!creds.client_id || !creds.client_secret) throw new Error('Global OAuth2 credentials not set in Settings → Integrations'); + try { + const data = await _post(`/yt/accounts/${encodeURIComponent(accountKey)}/refresh`, {}); + if (data.error) throw new Error(data.error); + // Actualizar caché + const cache = _readCache(); + if (cache.accounts && cache.accounts[accountKey]) { + cache.accounts[accountKey].access_token = data.access_token; + cache.accounts[accountKey].token_expiry = data.token_expiry; + _writeCache(cache); + } + return data.access_token; + } catch (err) { + // Fallback: intentar con credenciales locales + const creds = getCredentials(); + const account = getAccount(accountKey); + if (!account || !account.refresh_token) throw new Error('No refresh token for account: ' + accountKey); + if (!creds.client_id || !creds.client_secret) throw new Error('Global OAuth2 credentials not set'); - const resp = await fetch('https://oauth2.googleapis.com/token', { - method: 'POST', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: new URLSearchParams({ - client_id: creds.client_id, - client_secret: creds.client_secret, - refresh_token: account.refresh_token, - grant_type: 'refresh_token', - }).toString(), - }); - const data = await resp.json(); - if (data.error) throw new Error(data.error_description || data.error); + const resp = await fetch('https://oauth2.googleapis.com/token', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + client_id: creds.client_id, + client_secret: creds.client_secret, + refresh_token: account.refresh_token, + grant_type: 'refresh_token', + }).toString(), + }); + const tokenData = await resp.json(); + if (tokenData.error) throw new Error(tokenData.error_description || tokenData.error); - saveAccount(accountKey, { - access_token: data.access_token, - token_expiry: Date.now() + (data.expires_in || 3600) * 1000, - }); - return data.access_token; + // Persistir nuevo access_token + await saveAccount(accountKey, { + access_token: tokenData.access_token, + token_expiry: Date.now() + (tokenData.expires_in || 3600) * 1000, + }); + return tokenData.access_token; + } }; /** Obtener token válido (refresca automáticamente si está expirado) */ @@ -135,7 +275,7 @@ const getValidToken = async (accountKey) => { return account.access_token; }; -/** Fetch info del canal de YouTube para enriquecer la cuenta */ +/** Fetch info del canal de YouTube */ const fetchChannelInfo = async (accessToken) => { try { const resp = await fetch( @@ -146,24 +286,33 @@ const fetchChannelInfo = async (accessToken) => { if (data.items && data.items.length > 0) { return { channel_title: data.items[0].snippet.title, - channel_id: data.items[0].id, + channel_id: data.items[0].id, }; } - } catch {} + } catch (_) {} return {}; }; export default { - getAll, + // Credentials setCredentials, + saveCredentials, getCredentials, + loadCredentialsFromServer, + // Accounts + getAll, saveAccount, + saveAccountSync, + loadAccountsFromServer, + getAccountToken, getAccounts, getAccount, removeAccount, listAccounts, + // Token management refreshAccount, getValidToken, + // Google API fetchChannelInfo, }; diff --git a/src/views/Edit/Sources/Network.js b/src/views/Edit/Sources/Network.js index ef66265..4de37f6 100644 --- a/src/views/Edit/Sources/Network.js +++ b/src/views/Edit/Sources/Network.js @@ -805,8 +805,12 @@ function Pull(props) { return; } setExtractorLoading(true); + // AbortController con 90s — yt-dlp puede tardar 20-30s en extraer la URL + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 90000); try { - const response = await fetch(STREAM_SERVICE_BASE + videoId); + const response = await fetch(STREAM_SERVICE_BASE + videoId, { signal: controller.signal }); + clearTimeout(timeoutId); if (!response.ok) { throw new Error('HTTP ' + response.status + ' - ' + response.statusText); } @@ -814,9 +818,8 @@ function Pull(props) { if (data && data.stream_url) { props.onChange('', 'address')({ target: { value: data.stream_url } }); setExtractorError(''); - // Propagar title y description si el servicio los devuelve if (typeof props.onYoutubeMetadata === 'function') { - const title = data.title || data.video_title || ''; + const title = data.title || data.video_title || ''; const description = data.description || data.video_description || ''; if (title || description) { props.onYoutubeMetadata(title, description); @@ -826,7 +829,12 @@ function Pull(props) { setExtractorError('No stream_url found in service response.'); } } catch (e) { - setExtractorError('Error: ' + e.message); + clearTimeout(timeoutId); + if (e.name === 'AbortError') { + setExtractorError('Timeout: the extractor took too long. Try again.'); + } else { + setExtractorError('Error: ' + e.message); + } } finally { setExtractorLoading(false); } @@ -861,6 +869,11 @@ function Pull(props) { {extractorError} )} + {extractorLoading && ( + + ⏳ Extracting stream URL… this may take up to 30s + + )} {detectedYouTubeId && ( diff --git a/src/views/Edit/Wizard/Sources/Network.js b/src/views/Edit/Wizard/Sources/Network.js index d4518b3..5b37119 100644 --- a/src/views/Edit/Wizard/Sources/Network.js +++ b/src/views/Edit/Wizard/Sources/Network.js @@ -97,8 +97,12 @@ function Source(props) { return; } setExtractorLoading(true); + // AbortController con 90s — yt-dlp puede tardar 20-30s en extraer la URL + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 90000); try { - const response = await fetch(STREAM_SERVICE_BASE + videoId); + const response = await fetch(STREAM_SERVICE_BASE + videoId, { signal: controller.signal }); + clearTimeout(timeoutId); if (!response.ok) throw new Error('HTTP ' + response.status); const data = await response.json(); if (data && data.stream_url) { @@ -106,7 +110,7 @@ function Source(props) { handleChange(newSettings); setExtractorError(''); if (typeof props.onYoutubeMetadata === 'function') { - const title = data.title || data.video_title || ''; + const title = data.title || data.video_title || ''; const description = data.description || data.video_description || ''; if (title || description) props.onYoutubeMetadata(title, description); } @@ -114,7 +118,12 @@ function Source(props) { setExtractorError('No stream_url found in service response.'); } } catch (e) { - setExtractorError('Error: ' + e.message); + clearTimeout(timeoutId); + if (e.name === 'AbortError') { + setExtractorError('Timeout: the extractor took too long. Try again.'); + } else { + setExtractorError('Error: ' + e.message); + } } finally { setExtractorLoading(false); } @@ -156,6 +165,11 @@ function Source(props) { {extractorError} )} + {extractorLoading && ( + + ⏳ Extracting stream URL… this may take up to 30s + + )} {detectedYouTubeId && ( diff --git a/src/views/Publication/Edit.js b/src/views/Publication/Edit.js index 86edeef..fd4dca1 100644 --- a/src/views/Publication/Edit.js +++ b/src/views/Publication/Edit.js @@ -448,6 +448,8 @@ export default function Edit(props) { metadata={$metadata} streams={$settings.streams} onChange={handleServiceChange} + channelId={_channelid} + publicationId={id} /> diff --git a/src/views/Publication/Services/Facebook.js b/src/views/Publication/Services/Facebook.js index ffd2f89..2d6e220 100644 --- a/src/views/Publication/Services/Facebook.js +++ b/src/views/Publication/Services/Facebook.js @@ -200,7 +200,11 @@ function Service(props) { if (!$appId) { setError('⚠️ No hay App ID configurado. Ve a Settings → Integrations.'); return; } setAuthorizing(true); try { - const { account, tokenType, expiresAt } = await fbOAuth.connectAccount($appId, $hasSecret); + const context = { + restreamer_channel_id: props.channelId || '', + restreamer_publication_id: props.publicationId || '', + }; + const { account, tokenType, expiresAt } = await fbOAuth.connectAccount($appId, $hasSecret, context); await loadAccounts(); const typeLabel = tokenType === 'long' ? `✅ Token de larga duración (~${Math.round((expiresAt - Date.now()) / 86400000)} días)` @@ -222,7 +226,11 @@ function Service(props) { } catch (err) { if (!$appId) { setError('❌ ' + err.message); setAuthorizing(false); return; } try { - const { account, tokenType } = await fbOAuth.connectAccount($appId, $hasSecret); + const context = { + restreamer_channel_id: props.channelId || '', + restreamer_publication_id: props.publicationId || '', + }; + const { account, tokenType } = await fbOAuth.connectAccount($appId, $hasSecret, context); await loadAccounts(); setSuccess(`✅ Token renovado para "${account.name}". (${tokenType === 'long' ? '60 días' : '2h'})`); } catch (err2) { setError('❌ ' + err2.message); } diff --git a/src/views/Publication/Services/Youtube.js b/src/views/Publication/Services/Youtube.js index 68691eb..7a0e67d 100644 --- a/src/views/Publication/Services/Youtube.js +++ b/src/views/Publication/Services/Youtube.js @@ -85,9 +85,21 @@ function Service(props) { const [$apiError, setApiError] = React.useState(''); const [$apiSuccess, setApiSuccess] = React.useState(''); const [$connecting, setConnecting] = React.useState(false); + const [$globalCreds, setGlobalCreds] = React.useState(() => ytOAuth.getCredentials()); + + // Sincronizar credenciales y cuentas desde el servidor al montar + React.useEffect(() => { + (async () => { + try { + const creds = await ytOAuth.loadCredentialsFromServer(); + setGlobalCreds(creds); + await ytOAuth.loadAccountsFromServer(); + } catch (_) {} + })(); + }, []); // eslint-disable-line react-hooks/exhaustive-deps // Credenciales globales guardadas en Settings → Integrations - const globalCreds = ytOAuth.getCredentials(); + const globalCreds = $globalCreds; const hasGlobalCreds = !!(globalCreds.client_id && globalCreds.client_secret); // Pre-fill title/description desde metadata @@ -225,42 +237,75 @@ function Service(props) { if (event.data.code) { try { - // Intercambiar code por tokens - const resp = await fetch('https://oauth2.googleapis.com/token', { + // Intercambiar code por tokens vía servidor (incluye IDs de Restreamer) + const exchangeResp = await fetch('/fb-server/yt/exchange', { method: 'POST', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: new URLSearchParams({ - code: event.data.code, - client_id: globalCreds.client_id, - client_secret: globalCreds.client_secret, - redirect_uri: redirectUri, - grant_type: 'authorization_code', - }).toString(), + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + code: event.data.code, + redirect_uri: redirectUri, + restreamer_channel_id: props.channelId || '', + restreamer_publication_id: props.publicationId || '', + }), }); - const tokenData = await resp.json(); - if (tokenData.error) throw new Error(tokenData.error_description || tokenData.error); + const exchangeData = await exchangeResp.json(); - // Obtener info del canal - const channelInfo = await ytOAuth.fetchChannelInfo(tokenData.access_token); - const accountKey = channelInfo.channel_id || ('yt_' + Date.now()); - const channelName = channelInfo.channel_title || accountKey; + if (exchangeData.error) { + // Fallback client-side si el servidor no tiene credentials + const localCreds = globalCreds; + if (!localCreds.client_id || !localCreds.client_secret) { + throw new Error(exchangeData.error); + } + const resp = await fetch('https://oauth2.googleapis.com/token', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + code: event.data.code, + client_id: localCreds.client_id, + client_secret: localCreds.client_secret, + redirect_uri: redirectUri, + grant_type: 'authorization_code', + }).toString(), + }); + const tokenData = await resp.json(); + if (tokenData.error) throw new Error(tokenData.error_description || tokenData.error); - // Guardar cuenta en ytOAuth store - ytOAuth.saveAccount(accountKey, { - label: channelName, - access_token: tokenData.access_token, - refresh_token: tokenData.refresh_token || '', - token_expiry: Date.now() + (tokenData.expires_in || 3600) * 1000, - channel_title: channelInfo.channel_title || '', - channel_id: channelInfo.channel_id || accountKey, - }); + const channelInfo = await ytOAuth.fetchChannelInfo(tokenData.access_token); + const accountKey = channelInfo.channel_id || ('yt_' + Date.now()); + const channelName = channelInfo.channel_title || accountKey; - // Actualizar settings de esta Publication - settings.account_key = accountKey; - settings.account_label = channelName; - pushSettings(settings); + await ytOAuth.saveAccount(accountKey, { + label: channelName, + access_token: tokenData.access_token, + refresh_token: tokenData.refresh_token || '', + token_expiry: Date.now() + (tokenData.expires_in || 3600) * 1000, + channel_title: channelInfo.channel_title || '', + channel_id: channelInfo.channel_id || accountKey, + restreamer_channel_id: props.channelId || '', + restreamer_publication_id: props.publicationId || '', + }); - setApiSuccess('✅ Channel "' + channelName + '" connected successfully! Now click "Create Live Broadcast & get stream key".'); + settings.account_key = accountKey; + settings.account_label = channelName; + pushSettings(settings); + setApiSuccess('✅ Channel "' + channelName + '" connected successfully!'); + } else { + // Éxito por servidor — cuenta ya guardada en config.json con IDs de Restreamer + const acc = exchangeData.account; + const accountKey = acc.account_key || acc.channel_id; + const channelName = acc.label || acc.channel_title || accountKey; + + ytOAuth.saveAccountSync(accountKey, { + ...acc, + restreamer_channel_id: props.channelId || '', + restreamer_publication_id: props.publicationId || '', + }); + + settings.account_key = accountKey; + settings.account_label = channelName; + pushSettings(settings); + setApiSuccess('✅ Channel "' + channelName + '" connected successfully!'); + } } catch (err) { setApiError('❌ ' + err.message); } finally { @@ -297,13 +342,15 @@ function Service(props) { const channelInfo = await ytOAuth.fetchChannelInfo(token.trim()); const accountKey = channelInfo.channel_id || ('yt_manual_' + Date.now()); - ytOAuth.saveAccount(accountKey, { - label: channelInfo.channel_title || 'Manual token', - access_token: token.trim(), - refresh_token: '', - token_expiry: Date.now() + 3600 * 1000, - channel_title: channelInfo.channel_title || '', - channel_id: channelInfo.channel_id || accountKey, + await ytOAuth.saveAccount(accountKey, { + label: channelInfo.channel_title || 'Manual token', + access_token: token.trim(), + refresh_token: '', + token_expiry: Date.now() + 3600 * 1000, + channel_title: channelInfo.channel_title || '', + channel_id: channelInfo.channel_id || accountKey, + restreamer_channel_id: props.channelId || '', + restreamer_publication_id: props.publicationId || '', }); settings.account_key = accountKey; diff --git a/src/views/Settings.js b/src/views/Settings.js index 118159e..0d79bb9 100644 --- a/src/views/Settings.js +++ b/src/views/Settings.js @@ -756,6 +756,19 @@ export default function Settings(props) { React.useEffect(() => { (async () => { await load(); + // Sincronizar credenciales desde el servidor al montar + try { + const ytCreds = await ytOAuth.loadCredentialsFromServer(); + if (ytCreds.client_id) { + setYtCreds(ytCreds); + } + } catch (_) {} + try { + const fbCfg = await fbOAuth.getConfig(); + if (fbCfg.app_id) { + setFbAppId(fbCfg.app_id); + } + } catch (_) {} })(); return () => { @@ -2304,8 +2317,8 @@ export default function Settings(props) { variant="outlined" color="primary" disabled={!$ytCredsModified} - onClick={() => { - ytOAuth.setCredentials($ytCreds.client_id, $ytCreds.client_secret); + onClick={async () => { + await ytOAuth.saveCredentials($ytCreds.client_id, $ytCreds.client_secret); setYtCredsModified(false); }} > @@ -2393,8 +2406,8 @@ export default function Settings(props) { variant="outlined" color="primary" disabled={!$fbAppIdModified} - onClick={() => { - fbOAuth.setAppId($fbAppId); + onClick={async () => { + await fbOAuth.saveConfig({ app_id: $fbAppId }); setFbAppIdModified(false); }} > diff --git a/yarn.lock b/yarn.lock index 9201cc1..3e2ac75 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39,15 +39,7 @@ resolved "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.1.3.tgz" integrity sha512-NMTBNuuG4g3rame1aCnNS5qFYIzsTUV5qTFPRfTyYFS1feS6jsCBR+eTq9YkxCp1yuoM2UIcjunPaoPl77U9xQ== -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.8.3": - version "7.23.5" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz" - integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== - dependencies: - "@babel/highlight" "^7.23.4" - chalk "^2.4.2" - -"@babel/code-frame@^7.24.7": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.24.7", "@babel/code-frame@^7.8.3": version "7.24.7" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz" integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== @@ -55,122 +47,12 @@ "@babel/highlight" "^7.24.7" picocolors "^1.0.0" -"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.3", "@babel/compat-data@^7.23.5": - version "7.23.5" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz" - integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== - -"@babel/compat-data@^7.25.2": +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.3", "@babel/compat-data@^7.23.5", "@babel/compat-data@^7.25.2": version "7.25.4" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz" integrity sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ== -"@babel/core@^7.1.0": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz" - integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.9" - "@babel/parser" "^7.23.9" - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/core@^7.11.1": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz" - integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.9" - "@babel/parser" "^7.23.9" - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/core@^7.12.3": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz" - integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.9" - "@babel/parser" "^7.23.9" - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/core@^7.16.0": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz" - integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.9" - "@babel/parser" "^7.23.9" - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/core@^7.21.0": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz" - integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.9" - "@babel/parser" "^7.23.9" - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/core@^7.25.2": +"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.21.0", "@babel/core@^7.25.2", "@babel/core@^7.7.2", "@babel/core@^7.8.0": version "7.25.2" resolved "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz" integrity sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA== @@ -191,48 +73,6 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/core@^7.7.2": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz" - integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.9" - "@babel/parser" "^7.23.9" - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/core@^7.8.0": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz" - integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.9" - "@babel/parser" "^7.23.9" - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - "@babel/eslint-parser@^7.16.3": version "7.23.9" resolved "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.23.9.tgz" @@ -242,17 +82,7 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.1" -"@babel/generator@^7.21.1", "@babel/generator@^7.23.6", "@babel/generator@^7.7.2": - version "7.23.6" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz" - integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== - dependencies: - "@babel/types" "^7.23.6" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.25.0": +"@babel/generator@^7.21.1", "@babel/generator@^7.25.0", "@babel/generator@^7.25.6", "@babel/generator@^7.7.2": version "7.25.6" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz" integrity sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw== @@ -262,24 +92,7 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^2.5.1" -"@babel/generator@^7.25.6": - version "7.25.6" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz" - integrity sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw== - dependencies: - "@babel/types" "^7.25.6" - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^2.5.1" - -"@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5": - version "7.22.5" - resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz" - integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-annotate-as-pure@^7.24.7": +"@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5", "@babel/helper-annotate-as-pure@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz" integrity sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg== @@ -293,18 +106,7 @@ dependencies: "@babel/types" "^7.22.15" -"@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6": - version "7.23.6" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz" - integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== - dependencies: - "@babel/compat-data" "^7.23.5" - "@babel/helper-validator-option" "^7.23.5" - browserslist "^4.22.2" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-compilation-targets@^7.25.2": +"@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6", "@babel/helper-compilation-targets@^7.25.2": version "7.25.2" resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz" integrity sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw== @@ -377,14 +179,7 @@ dependencies: "@babel/types" "^7.23.0" -"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.22.15": - version "7.22.15" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz" - integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== - dependencies: - "@babel/types" "^7.22.15" - -"@babel/helper-module-imports@^7.24.7": +"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz" integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== @@ -392,18 +187,7 @@ "@babel/traverse" "^7.24.7" "@babel/types" "^7.24.7" -"@babel/helper-module-transforms@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz" - integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== - dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.20" - -"@babel/helper-module-transforms@^7.25.2": +"@babel/helper-module-transforms@^7.23.3", "@babel/helper-module-transforms@^7.25.2": version "7.25.2" resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz" integrity sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ== @@ -420,12 +204,7 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.22.5" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz" - integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== - -"@babel/helper-plugin-utils@^7.24.7", "@babel/helper-plugin-utils@^7.24.8": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.24.7", "@babel/helper-plugin-utils@^7.24.8", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.24.8" resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz" integrity sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg== @@ -448,14 +227,7 @@ "@babel/helper-member-expression-to-functions" "^7.22.15" "@babel/helper-optimise-call-expression" "^7.22.5" -"@babel/helper-simple-access@^7.22.5": - version "7.22.5" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz" - integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-simple-access@^7.24.7": +"@babel/helper-simple-access@^7.22.5", "@babel/helper-simple-access@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz" integrity sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg== @@ -477,32 +249,17 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz" - integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== - "@babel/helper-string-parser@^7.24.8": version "7.24.8" resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz" integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - -"@babel/helper-validator-identifier@^7.24.7": +"@babel/helper-validator-identifier@^7.22.20", "@babel/helper-validator-identifier@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz" integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== -"@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.23.5": - version "7.23.5" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz" - integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== - -"@babel/helper-validator-option@^7.24.8": +"@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.23.5", "@babel/helper-validator-option@^7.24.8": version "7.24.8" resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz" integrity sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q== @@ -516,15 +273,6 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.22.19" -"@babel/helpers@^7.23.9": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz" - integrity sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ== - dependencies: - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" - "@babel/helpers@^7.25.0": version "7.25.6" resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz" @@ -533,15 +281,6 @@ "@babel/template" "^7.25.0" "@babel/types" "^7.25.6" -"@babel/highlight@^7.23.4": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz" - integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" - chalk "^2.4.2" - js-tokens "^4.0.0" - "@babel/highlight@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz" @@ -552,12 +291,7 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.2", "@babel/parser@^7.23.9": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz" - integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== - -"@babel/parser@^7.25.0", "@babel/parser@^7.25.6": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.2", "@babel/parser@^7.25.0", "@babel/parser@^7.25.6": version "7.25.6" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz" integrity sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q== @@ -667,14 +401,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-class-properties@^7.8.3": +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== @@ -709,14 +436,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-flow@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.23.3.tgz" - integrity sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-flow@^7.24.7": +"@babel/plugin-syntax-flow@^7.23.3", "@babel/plugin-syntax-flow@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.7.tgz" integrity sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw== @@ -737,14 +457,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-syntax-import-meta@^7.10.4": - version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-import-meta@^7.8.3": +"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== @@ -758,28 +471,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz" - integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-jsx@^7.24.7": +"@babel/plugin-syntax-jsx@^7.23.3", "@babel/plugin-syntax-jsx@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz" integrity sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ== dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== @@ -793,14 +492,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-numeric-separator@^7.8.3": +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== @@ -835,28 +527,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-top-level-await@^7.14.5": +"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.14.5" resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-typescript@^7.23.3": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz" - integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-typescript@^7.7.2": +"@babel/plugin-syntax-typescript@^7.23.3", "@babel/plugin-syntax-typescript@^7.7.2": version "7.23.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz" integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== @@ -1206,18 +884,7 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.22.5" -"@babel/plugin-transform-react-jsx@^7.22.15", "@babel/plugin-transform-react-jsx@^7.22.5": - version "7.23.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz" - integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-jsx" "^7.23.3" - "@babel/types" "^7.23.4" - -"@babel/plugin-transform-react-jsx@^7.25.2": +"@babel/plugin-transform-react-jsx@^7.22.15", "@babel/plugin-transform-react-jsx@^7.22.5", "@babel/plugin-transform-react-jsx@^7.25.2": version "7.25.2" resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.2.tgz" integrity sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA== @@ -1340,179 +1007,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/preset-env@^7.11.0": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz" - integrity sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A== - dependencies: - "@babel/compat-data" "^7.23.5" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.23.5" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.23.3" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.23.3" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.23.7" - "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.23.3" - "@babel/plugin-syntax-import-attributes" "^7.23.3" - "@babel/plugin-syntax-import-meta" "^7.10.4" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" - "@babel/plugin-transform-arrow-functions" "^7.23.3" - "@babel/plugin-transform-async-generator-functions" "^7.23.9" - "@babel/plugin-transform-async-to-generator" "^7.23.3" - "@babel/plugin-transform-block-scoped-functions" "^7.23.3" - "@babel/plugin-transform-block-scoping" "^7.23.4" - "@babel/plugin-transform-class-properties" "^7.23.3" - "@babel/plugin-transform-class-static-block" "^7.23.4" - "@babel/plugin-transform-classes" "^7.23.8" - "@babel/plugin-transform-computed-properties" "^7.23.3" - "@babel/plugin-transform-destructuring" "^7.23.3" - "@babel/plugin-transform-dotall-regex" "^7.23.3" - "@babel/plugin-transform-duplicate-keys" "^7.23.3" - "@babel/plugin-transform-dynamic-import" "^7.23.4" - "@babel/plugin-transform-exponentiation-operator" "^7.23.3" - "@babel/plugin-transform-export-namespace-from" "^7.23.4" - "@babel/plugin-transform-for-of" "^7.23.6" - "@babel/plugin-transform-function-name" "^7.23.3" - "@babel/plugin-transform-json-strings" "^7.23.4" - "@babel/plugin-transform-literals" "^7.23.3" - "@babel/plugin-transform-logical-assignment-operators" "^7.23.4" - "@babel/plugin-transform-member-expression-literals" "^7.23.3" - "@babel/plugin-transform-modules-amd" "^7.23.3" - "@babel/plugin-transform-modules-commonjs" "^7.23.3" - "@babel/plugin-transform-modules-systemjs" "^7.23.9" - "@babel/plugin-transform-modules-umd" "^7.23.3" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" - "@babel/plugin-transform-new-target" "^7.23.3" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.23.4" - "@babel/plugin-transform-numeric-separator" "^7.23.4" - "@babel/plugin-transform-object-rest-spread" "^7.23.4" - "@babel/plugin-transform-object-super" "^7.23.3" - "@babel/plugin-transform-optional-catch-binding" "^7.23.4" - "@babel/plugin-transform-optional-chaining" "^7.23.4" - "@babel/plugin-transform-parameters" "^7.23.3" - "@babel/plugin-transform-private-methods" "^7.23.3" - "@babel/plugin-transform-private-property-in-object" "^7.23.4" - "@babel/plugin-transform-property-literals" "^7.23.3" - "@babel/plugin-transform-regenerator" "^7.23.3" - "@babel/plugin-transform-reserved-words" "^7.23.3" - "@babel/plugin-transform-shorthand-properties" "^7.23.3" - "@babel/plugin-transform-spread" "^7.23.3" - "@babel/plugin-transform-sticky-regex" "^7.23.3" - "@babel/plugin-transform-template-literals" "^7.23.3" - "@babel/plugin-transform-typeof-symbol" "^7.23.3" - "@babel/plugin-transform-unicode-escapes" "^7.23.3" - "@babel/plugin-transform-unicode-property-regex" "^7.23.3" - "@babel/plugin-transform-unicode-regex" "^7.23.3" - "@babel/plugin-transform-unicode-sets-regex" "^7.23.3" - "@babel/preset-modules" "0.1.6-no-external-plugins" - babel-plugin-polyfill-corejs2 "^0.4.8" - babel-plugin-polyfill-corejs3 "^0.9.0" - babel-plugin-polyfill-regenerator "^0.5.5" - core-js-compat "^3.31.0" - semver "^6.3.1" - -"@babel/preset-env@^7.12.1": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz" - integrity sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A== - dependencies: - "@babel/compat-data" "^7.23.5" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.23.5" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.23.3" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.23.3" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.23.7" - "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.23.3" - "@babel/plugin-syntax-import-attributes" "^7.23.3" - "@babel/plugin-syntax-import-meta" "^7.10.4" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" - "@babel/plugin-transform-arrow-functions" "^7.23.3" - "@babel/plugin-transform-async-generator-functions" "^7.23.9" - "@babel/plugin-transform-async-to-generator" "^7.23.3" - "@babel/plugin-transform-block-scoped-functions" "^7.23.3" - "@babel/plugin-transform-block-scoping" "^7.23.4" - "@babel/plugin-transform-class-properties" "^7.23.3" - "@babel/plugin-transform-class-static-block" "^7.23.4" - "@babel/plugin-transform-classes" "^7.23.8" - "@babel/plugin-transform-computed-properties" "^7.23.3" - "@babel/plugin-transform-destructuring" "^7.23.3" - "@babel/plugin-transform-dotall-regex" "^7.23.3" - "@babel/plugin-transform-duplicate-keys" "^7.23.3" - "@babel/plugin-transform-dynamic-import" "^7.23.4" - "@babel/plugin-transform-exponentiation-operator" "^7.23.3" - "@babel/plugin-transform-export-namespace-from" "^7.23.4" - "@babel/plugin-transform-for-of" "^7.23.6" - "@babel/plugin-transform-function-name" "^7.23.3" - "@babel/plugin-transform-json-strings" "^7.23.4" - "@babel/plugin-transform-literals" "^7.23.3" - "@babel/plugin-transform-logical-assignment-operators" "^7.23.4" - "@babel/plugin-transform-member-expression-literals" "^7.23.3" - "@babel/plugin-transform-modules-amd" "^7.23.3" - "@babel/plugin-transform-modules-commonjs" "^7.23.3" - "@babel/plugin-transform-modules-systemjs" "^7.23.9" - "@babel/plugin-transform-modules-umd" "^7.23.3" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" - "@babel/plugin-transform-new-target" "^7.23.3" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.23.4" - "@babel/plugin-transform-numeric-separator" "^7.23.4" - "@babel/plugin-transform-object-rest-spread" "^7.23.4" - "@babel/plugin-transform-object-super" "^7.23.3" - "@babel/plugin-transform-optional-catch-binding" "^7.23.4" - "@babel/plugin-transform-optional-chaining" "^7.23.4" - "@babel/plugin-transform-parameters" "^7.23.3" - "@babel/plugin-transform-private-methods" "^7.23.3" - "@babel/plugin-transform-private-property-in-object" "^7.23.4" - "@babel/plugin-transform-property-literals" "^7.23.3" - "@babel/plugin-transform-regenerator" "^7.23.3" - "@babel/plugin-transform-reserved-words" "^7.23.3" - "@babel/plugin-transform-shorthand-properties" "^7.23.3" - "@babel/plugin-transform-spread" "^7.23.3" - "@babel/plugin-transform-sticky-regex" "^7.23.3" - "@babel/plugin-transform-template-literals" "^7.23.3" - "@babel/plugin-transform-typeof-symbol" "^7.23.3" - "@babel/plugin-transform-unicode-escapes" "^7.23.3" - "@babel/plugin-transform-unicode-property-regex" "^7.23.3" - "@babel/plugin-transform-unicode-regex" "^7.23.3" - "@babel/plugin-transform-unicode-sets-regex" "^7.23.3" - "@babel/preset-modules" "0.1.6-no-external-plugins" - babel-plugin-polyfill-corejs2 "^0.4.8" - babel-plugin-polyfill-corejs3 "^0.9.0" - babel-plugin-polyfill-regenerator "^0.5.5" - core-js-compat "^3.31.0" - semver "^6.3.1" - -"@babel/preset-env@^7.16.4": +"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4": version "7.23.9" resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz" integrity sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A== @@ -1607,19 +1102,7 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@^7.12.5": - version "7.23.3" - resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.23.3.tgz" - integrity sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.15" - "@babel/plugin-transform-react-display-name" "^7.23.3" - "@babel/plugin-transform-react-jsx" "^7.22.15" - "@babel/plugin-transform-react-jsx-development" "^7.22.5" - "@babel/plugin-transform-react-pure-annotations" "^7.23.3" - -"@babel/preset-react@^7.16.0": +"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.16.0": version "7.23.3" resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.23.3.tgz" integrity sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w== @@ -1647,37 +1130,14 @@ resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.13", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz" - integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw== - dependencies: - regenerator-runtime "^0.14.0" - -"@babel/runtime@^7.25.0": +"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.13", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.25.0", "@babel/runtime@^7.25.6", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.25.6" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz" integrity sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ== dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.25.6": - version "7.25.6" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz" - integrity sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ== - dependencies: - regenerator-runtime "^0.14.0" - -"@babel/template@^7.22.15", "@babel/template@^7.23.9", "@babel/template@^7.3.3": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz" - integrity sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/parser" "^7.23.9" - "@babel/types" "^7.23.9" - -"@babel/template@^7.25.0": +"@babel/template@^7.22.15", "@babel/template@^7.25.0", "@babel/template@^7.3.3": version "7.25.0" resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz" integrity sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q== @@ -1686,23 +1146,7 @@ "@babel/parser" "^7.25.0" "@babel/types" "^7.25.0" -"@babel/traverse@^7.23.9", "@babel/traverse@^7.7.2": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz" - integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.9" - "@babel/types" "^7.23.9" - debug "^4.3.1" - globals "^11.1.0" - -"@babel/traverse@^7.24.7", "@babel/traverse@^7.25.2": +"@babel/traverse@^7.24.7", "@babel/traverse@^7.25.2", "@babel/traverse@^7.7.2": version "7.25.6" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz" integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ== @@ -1715,16 +1159,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.20.7", "@babel/types@^7.21.2", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.23.9" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz" - integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== - dependencies: - "@babel/helper-string-parser" "^7.23.4" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" - -"@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6": +"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.20.7", "@babel/types@^7.21.2", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.25.6" resolved "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz" integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw== @@ -1956,10 +1391,10 @@ resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz" integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== -"@esbuild/win32-x64@0.17.19": +"@esbuild/linux-x64@0.17.19": version "0.17.19" - resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz" - integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz" + integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" @@ -1968,16 +1403,11 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.11.0": +"@eslint-community/regexpp@^4.11.0", "@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": version "4.11.0" resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz" integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== -"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": - version "4.10.0" - resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz" - integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== - "@eslint/config-array@^0.18.0": version "0.18.0" resolved "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz" @@ -2061,12 +1491,7 @@ dependencies: "@floating-ui/dom" "^1.0.0" -"@floating-ui/utils@^0.2.1": - version "0.2.1" - resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz" - integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q== - -"@floating-ui/utils@^0.2.7": +"@floating-ui/utils@^0.2.1", "@floating-ui/utils@^0.2.7": version "0.2.7" resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz" integrity sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA== @@ -2395,16 +1820,7 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.3" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz" - integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/gen-mapping@^0.3.5": +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz" integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== @@ -2418,11 +1834,6 @@ resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz" integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - "@jridgewell/set-array@^1.2.1": version "1.2.1" resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" @@ -2441,15 +1852,7 @@ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.22" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz" - integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.9": version "0.3.25" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -3190,12 +2593,7 @@ dependencies: "@types/unist" "*" -"@types/mime@*": - version "3.0.4" - resolved "https://registry.npmjs.org/@types/mime/-/mime-3.0.4.tgz" - integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw== - -"@types/mime@^1": +"@types/mime@*", "@types/mime@^1": version "1.3.5" resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz" integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== @@ -3229,12 +2627,7 @@ resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== -"@types/prop-types@*": - version "15.7.11" - resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz" - integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== - -"@types/prop-types@^15.7.12": +"@types/prop-types@*", "@types/prop-types@^15.7.12": version "15.7.12" resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz" integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== @@ -3261,16 +2654,7 @@ dependencies: "@types/react" "*" -"@types/react@*": - version "18.2.48" - resolved "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz" - integrity sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@^18.3.5": +"@types/react@*", "@types/react@^18.3.5": version "18.3.5" resolved "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz" integrity sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA== @@ -3290,11 +2674,6 @@ resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz" integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== -"@types/scheduler@*": - version "0.16.8" - resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz" - integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== - "@types/semver@^7.3.12": version "7.5.6" resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz" @@ -3446,21 +2825,7 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@^5.58.0": - version "5.62.0" - resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" - integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@types/json-schema" "^7.0.9" - "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.62.0" - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/typescript-estree" "5.62.0" - eslint-scope "^5.1.1" - semver "^7.3.7" - -"@typescript-eslint/utils@5.62.0": +"@typescript-eslint/utils@^5.58.0", "@typescript-eslint/utils@5.62.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== @@ -3487,20 +2852,6 @@ resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@videojs/http-streaming@3.10.0": - version "3.10.0" - resolved "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-3.10.0.tgz" - integrity sha512-Lf1rmhTalV4Gw0bJqHmH4lfk/FlepUDs9smuMtorblAYnqDlE2tbUOb7sBXVYoXGdbWbdTW8jH2cnS+6HWYJ4Q== - dependencies: - "@babel/runtime" "^7.12.5" - "@videojs/vhs-utils" "4.0.0" - aes-decrypter "4.0.1" - global "^4.4.0" - m3u8-parser "^7.1.0" - mpd-parser "^1.3.0" - mux.js "7.0.2" - video.js "^7 || ^8" - "@videojs/http-streaming@3.13.3": version "3.13.3" resolved "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-3.13.3.tgz" @@ -3533,15 +2884,6 @@ global "^4.4.0" url-toolkit "^2.2.1" -"@videojs/xhr@2.6.0": - version "2.6.0" - resolved "https://registry.npmjs.org/@videojs/xhr/-/xhr-2.6.0.tgz" - integrity sha512-7J361GiN1tXpm+gd0xz2QWr3xNWBE+rytvo8J3KuggFaLg+U37gZQ2BuPLcnkfGffy2e+ozY70RHC8jt7zjA6Q== - dependencies: - "@babel/runtime" "^7.5.5" - global "~4.4.0" - is-function "^1.0.1" - "@videojs/xhr@2.7.0": version "2.7.0" resolved "https://registry.npmjs.org/@videojs/xhr/-/xhr-2.7.0.tgz" @@ -3733,16 +3075,11 @@ acorn@^7.1.1: resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.12.0: +acorn@^8.12.0, acorn@^8.2.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: version "8.12.1" resolved "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== -acorn@^8.2.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: - version "8.11.3" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" - integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== - address@^1.0.1, address@^1.1.2: version "1.2.2" resolved "https://registry.npmjs.org/address/-/address-1.2.2.tgz" @@ -3802,7 +3139,7 @@ ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.9.0: +ajv@^8.0.0: version "8.12.0" resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== @@ -3822,6 +3159,16 @@ ajv@^8.6.0: require-from-string "^2.0.2" uri-js "^4.2.2" +ajv@^8.9.0: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ajv@8.12.0: version "8.12.0" resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" @@ -3878,7 +3225,14 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0: +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== @@ -4149,21 +3503,7 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-jest@^27.4.2: - version "27.5.1" - resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz" - integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== - dependencies: - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.5.1" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - -babel-jest@^27.5.1: +babel-jest@^27.4.2, babel-jest@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz" integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== @@ -4495,17 +3835,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.21.4, browserslist@^4.22.2: - version "4.22.2" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz" - integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== - dependencies: - caniuse-lite "^1.0.30001565" - electron-to-chromium "^1.4.601" - node-releases "^2.0.14" - update-browserslist-db "^1.0.13" - -browserslist@^4.23.1: +browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.21.4, browserslist@^4.22.2, browserslist@^4.23.1: version "4.23.3" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz" integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== @@ -4602,7 +3932,7 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001565, caniuse-lite@^1.0.30001578, caniuse-lite@^1.0.30001646: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001578, caniuse-lite@^1.0.30001646: version "1.0.30001660" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz" integrity sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg== @@ -4635,16 +3965,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.4.1: - version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^2.4.2: +chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -4661,7 +3982,31 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.2: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -4719,22 +4064,7 @@ check-types@^11.2.3: resolved "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz" integrity sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg== -chokidar@^3.4.2, chokidar@^3.5.3: - version "3.5.3" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -chokidar@3.5.1: +chokidar@^3.4.2, chokidar@3.5.1: version "3.5.1" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz" integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== @@ -4749,6 +4079,21 @@ chokidar@3.5.1: optionalDependencies: fsevents "~2.3.1" +chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz" @@ -4990,7 +4335,27 @@ content-type@~1.0.4: resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.4.0: + version "1.9.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^1.5.0: + version "1.9.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^1.5.1: + version "1.9.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^1.6.0: + version "1.9.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== @@ -5159,7 +4524,15 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" -css-tree@^1.1.2, css-tree@^1.1.3: +css-tree@^1.1.2: + version "1.1.3" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +css-tree@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== @@ -5644,11 +5017,6 @@ ejs@^3.1.6: dependencies: jake "^10.8.5" -electron-to-chromium@^1.4.601: - version "1.4.647" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.647.tgz" - integrity sha512-Z/fTNGwc45WrYQhPaEcz5tAJuZZ8G7S/DBnhS6Kgp4BxnS40Z/HqlJ0hHg3Z79IGVzuVartIlTcjw/cQbPLgOw== - electron-to-chromium@^1.5.4: version "1.5.22" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.22.tgz" @@ -5839,12 +5207,7 @@ esbuild@^0.17.10: "@esbuild/win32-ia32" "0.17.19" "@esbuild/win32-x64" "0.17.19" -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escalade@^3.1.2: +escalade@^3.1.1, escalade@^3.1.2: version "3.2.0" resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== @@ -6027,7 +5390,7 @@ eslint-plugin-testing-library@^5.0.1: dependencies: "@typescript-eslint/utils" "^5.58.0" -eslint-scope@^5.1.1, eslint-scope@5.1.1: +eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -6051,12 +5414,25 @@ eslint-scope@^8.0.2: esrecurse "^4.3.0" estraverse "^5.2.0" +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + eslint-visitor-keys@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.3.0: + version "3.4.3" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== @@ -6189,14 +5565,7 @@ esprima@1.2.2: resolved "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz" integrity sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A== -esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== - dependencies: - estraverse "^5.1.0" - -esquery@^1.5.0: +esquery@^1.4.2, esquery@^1.5.0: version "1.6.0" resolved "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz" integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== @@ -6590,7 +5959,17 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^9.0.0, fs-extra@^9.0.1: +fs-extra@^9.0.0: + version "9.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^9.0.1: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -7148,11 +6527,6 @@ indent-string@^4.0.0: resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -individual@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/individual/-/individual-2.0.0.tgz" - integrity sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" @@ -8354,11 +7728,6 @@ jwt-decode@^4.0.0: resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz" integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA== -keycode@2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz" - integrity sha512-ps3I9jAdNtRpJrbBvQjpzyFbss/skHqzS+eu4RxKLaEAtFqkjZaB6TZMSivPbLxf4K7VI4SjR0P5mRCX5+Q25A== - keyv@^4.5.3, keyv@^4.5.4: version "4.5.4" resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" @@ -8547,13 +7916,6 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - "lru-cache@^9.1.1 || ^10.0.0": version "10.2.0" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz" @@ -9089,15 +8451,7 @@ mute-stream@0.0.8: resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -mux.js@^7.0.1, mux.js@7.0.2: - version "7.0.2" - resolved "https://registry.npmjs.org/mux.js/-/mux.js-7.0.2.tgz" - integrity sha512-CM6+QuyDbc0qW1OfEjkd2+jVKzTXF+z5VOKH0eZxtZtnrG/ilkW/U7l7IXGtBNLASF9sKZMcK1u669cq50Qq0A== - dependencies: - "@babel/runtime" "^7.11.2" - global "^4.4.0" - -mux.js@7.0.3: +mux.js@^7.0.1, mux.js@7.0.3: version "7.0.3" resolved "https://registry.npmjs.org/mux.js/-/mux.js-7.0.3.tgz" integrity sha512-gzlzJVEGFYPtl2vvEiJneSWAWD4nfYRHD5XgxmB2gWvXraMPOYk+sxfvexmNfjQUFpmk6hwLR5C6iSFmuwCHdQ== @@ -9157,11 +8511,6 @@ node-int64@^0.4.0: resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.14: - version "2.0.14" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz" - integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== - node-releases@^2.0.18: version "2.0.18" resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz" @@ -9383,7 +8732,14 @@ os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== -p-limit@^2.0.0, p-limit@^2.2.0: +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -10284,7 +9640,12 @@ punycode@^1.3.2: resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== -punycode@^2.1.0, punycode@^2.1.1: +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +punycode@^2.1.1: version "2.3.1" resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== @@ -10330,7 +9691,12 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -range-parser@^1.2.1, range-parser@~1.2.1: +range-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +range-parser@~1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== @@ -10442,12 +9808,7 @@ react-is@^17.0.1: resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0: - version "18.2.0" - resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" - integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== - -react-is@^18.3.1: +react-is@^18.0.0, react-is@^18.3.1: version "18.3.1" resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== @@ -10876,13 +10237,6 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rust-result@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/rust-result/-/rust-result-1.0.0.tgz" - integrity sha512-6cJzSBU+J/RJCF063onnQf0cDUOHs9uZI1oroSGnHOph+CQTIJ5Pp2hK5kEQq1+7yE/EEWfulSNXAQ2jikPthA== - dependencies: - individual "^2.0.0" - rxjs@^6.6.0: version "6.6.7" resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" @@ -10915,13 +10269,6 @@ safe-buffer@5.1.2: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-json-parse@4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-4.0.0.tgz" - integrity sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ== - dependencies: - rust-result "^1.0.0" - safe-regex-test@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz" @@ -10977,25 +10324,7 @@ schema-utils@^2.6.5: ajv "^6.12.4" ajv-keywords "^3.5.2" -schema-utils@^3.0.0: - version "3.3.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" - integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== - dependencies: - "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - -schema-utils@^3.1.1: - version "3.3.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" - integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== - dependencies: - "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - -schema-utils@^3.2.0: +schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== @@ -11041,47 +10370,17 @@ semver@^6.0.0: resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^6.3.0, semver@^6.3.1: +semver@^6.3.0: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2: - version "7.5.4" - resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.5: - version "7.5.4" - resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - -semver@^7.3.7: - version "7.5.4" - resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - -semver@^7.5.3: - version "7.5.4" - resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - -semver@^7.5.4: - version "7.5.4" - resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - -semver@^7.6.3: +semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3: version "7.6.3" resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -11294,7 +10593,7 @@ source-map-support@^0.4.15: dependencies: source-map "^0.5.6" -source-map-support@^0.5.6, source-map-support@~0.5.20: +source-map-support@^0.5.6: version "0.5.21" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -11302,17 +10601,25 @@ source-map-support@^0.5.6, source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.5.6: +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.5.7: - version "0.5.7" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" - integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1, source-map@0.6.1: +source-map@^0.6.1, source-map@0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -11329,6 +10636,16 @@ source-map@^0.8.0-beta.0: dependencies: whatwg-url "^7.0.0" +source-map@~0.6.0: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + sourcemap-codec@^1.4.8: version "1.4.8" resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" @@ -11646,7 +10963,14 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.0.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -11960,7 +11284,7 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" -type-check@^0.4.0: +type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== @@ -11974,13 +11298,6 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - type-detect@4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" @@ -12214,14 +11531,6 @@ upath@^1.2.0: resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -update-browserslist-db@^1.0.13: - version "1.0.13" - resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz" - integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - update-browserslist-db@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz" @@ -12335,27 +11644,7 @@ vfile@^6.0.0: unist-util-stringify-position "^4.0.0" vfile-message "^4.0.0" -"video.js@^6 || ^7 || ^8": - version "8.10.0" - resolved "https://registry.npmjs.org/video.js/-/video.js-8.10.0.tgz" - integrity sha512-7UeG/flj/pp8tNGW8WKPP1VJb3x2FgLoqUWzpZqkoq5YIyf6MNzmIrKtxprl438T5RVkcj+OzV8IX4jYSAn4Sw== - dependencies: - "@babel/runtime" "^7.12.5" - "@videojs/http-streaming" "3.10.0" - "@videojs/vhs-utils" "^4.0.0" - "@videojs/xhr" "2.6.0" - aes-decrypter "^4.0.1" - global "4.4.0" - keycode "2.2.0" - m3u8-parser "^7.1.0" - mpd-parser "^1.2.2" - mux.js "^7.0.1" - safe-json-parse "4.0.0" - videojs-contrib-quality-levels "4.0.0" - videojs-font "4.1.0" - videojs-vtt.js "0.15.5" - -"video.js@^7 || ^8", video.js@^8.17.3: +"video.js@^6 || ^7 || ^8", "video.js@^7 || ^8", video.js@^8.17.3: version "8.17.4" resolved "https://registry.npmjs.org/video.js/-/video.js-8.17.4.tgz" integrity sha512-AECieAxKMKB/QgYK36ci50phfpWys6bFT6+pGMpSafeFYSoZaQ2Vpl83T9Qqcesv4TO7oNtiycnVeaBnrva2oA== @@ -12373,13 +11662,6 @@ vfile@^6.0.0: videojs-font "4.2.0" videojs-vtt.js "0.15.5" -videojs-contrib-quality-levels@4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/videojs-contrib-quality-levels/-/videojs-contrib-quality-levels-4.0.0.tgz" - integrity sha512-u5rmd8BjLwANp7XwuQ0Q/me34bMe6zg9PQdHfTS7aXgiVRbNTb4djcmfG7aeSrkpZjg+XCLezFNenlJaCjBHKw== - dependencies: - global "^4.4.0" - videojs-contrib-quality-levels@4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/videojs-contrib-quality-levels/-/videojs-contrib-quality-levels-4.1.0.tgz" @@ -12387,11 +11669,6 @@ videojs-contrib-quality-levels@4.1.0: dependencies: global "^4.4.0" -videojs-font@4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/videojs-font/-/videojs-font-4.1.0.tgz" - integrity sha512-X1LuPfLZPisPLrANIAKCknZbZu5obVM/ylfd1CN+SsCmPZQ3UMDPcvLTpPBJxcBuTpHQq2MO1QCFt7p8spnZ/w== - videojs-font@4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/videojs-font/-/videojs-font-4.2.0.tgz" @@ -12947,11 +12224,6 @@ yallist@^3.0.2: resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: version "1.10.2" resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz"