From b003f3507cb6e65bccf8b0c2f339438273e0052d Mon Sep 17 00:00:00 2001 From: eric Date: Wed, 25 Mar 2026 16:14:20 -0500 Subject: [PATCH] 's' --- electron/package.json | 23 +- .../scripts/download-chrome-for-testing.mjs | 143 ----------- electron/scripts/download-easytier.mjs | 103 -------- .../src/main/chromeForTestingBootstrap.ts | 237 ------------------ electron/src/main/easytier.ts | 229 ----------------- electron/src/main/index.ts | 77 ++---- electron/src/preload/index.ts | 3 +- electron/src/renderer/src/RemoteBinding.tsx | 191 +++++--------- .../src/renderer/src/WorkspaceDashboard.tsx | 24 +- electron/src/renderer/src/vite-env.d.ts | 10 - electron/tsconfig.node.tsbuildinfo | 1 + 11 files changed, 91 insertions(+), 950 deletions(-) delete mode 100644 electron/scripts/download-chrome-for-testing.mjs delete mode 100644 electron/scripts/download-easytier.mjs delete mode 100644 electron/src/main/chromeForTestingBootstrap.ts delete mode 100644 electron/src/main/easytier.ts create mode 100644 electron/tsconfig.node.tsbuildinfo diff --git a/electron/package.json b/electron/package.json index 77d5632..390872c 100644 --- a/electron/package.json +++ b/electron/package.json @@ -6,10 +6,8 @@ "main": "./out/main/index.js", "type": "module", "scripts": { - "prepare-easytier": "node scripts/download-easytier.mjs", - "prepare-chrome": "node scripts/download-chrome-for-testing.mjs", - "dev": "node scripts/download-easytier.mjs && node scripts/download-chrome-for-testing.mjs && electron-vite dev", - "build": "node scripts/download-easytier.mjs && node scripts/download-chrome-for-testing.mjs && electron-vite build", + "dev": "electron-vite dev", + "build": "electron-vite build", "preview": "electron-vite preview", "dist": "pnpm run build && electron-builder --win --publish never" }, @@ -20,8 +18,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "sql.js": "^1.14.1", - "three": "^0.183.2", - "adm-zip": "^0.5.16" + "three": "^0.183.2" }, "devDependencies": { "@types/d3": "^7.4.3", @@ -56,20 +53,6 @@ "package.json" ], "extraResources": [ - { - "from": "resources/easytier", - "to": "easytier", - "filter": [ - "**/*" - ] - }, - { - "from": "resources/chrome-cft", - "to": "chrome-cft", - "filter": [ - "**/*" - ] - }, { "from": "..", "to": "backend", diff --git a/electron/scripts/download-chrome-for-testing.mjs b/electron/scripts/download-chrome-for-testing.mjs deleted file mode 100644 index f984b7d..0000000 --- a/electron/scripts/download-chrome-for-testing.mjs +++ /dev/null @@ -1,143 +0,0 @@ -/** - * 下载 Chrome for Testing(Stable / win64)完整目录到 resources/chrome-cft/chrome-win64/, - * 与 chrome.exe 同级的 DLL 等依赖一并保留,随安装包分发,开箱即用。 - * 设置 SKIP_CHROME=1 可跳过(CI 无网环境)。 - */ -import fs from 'fs' -import https from 'https' -import path from 'path' -import { fileURLToPath } from 'url' -import AdmZip from 'adm-zip' - -const __dirname = path.dirname(fileURLToPath(import.meta.url)) -const root = path.resolve(__dirname, '..') -const resDir = path.join(root, 'resources', 'chrome-cft') -const CFT_JSON_URL = - 'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json' -const PLATFORM_KEY = 'win64' -const markerName = '.chrome_cft_bundle_ok' - -if (process.env.SKIP_CHROME === '1') { - console.log('[chrome-cft] SKIP_CHROME=1,跳过下载') - process.exit(0) -} - -function downloadBuffer(url) { - return new Promise((resolve, reject) => { - https - .get(url, (res) => { - if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { - downloadBuffer(res.headers.location).then(resolve).catch(reject) - return - } - if (res.statusCode !== 200) { - reject(new Error(`HTTP ${res.statusCode} ${url}`)) - return - } - const chunks = [] - res.on('data', (c) => chunks.push(c)) - res.on('end', () => resolve(Buffer.concat(chunks))) - res.on('error', reject) - }) - .on('error', reject) - }) -} - -function downloadToFile(url, dest) { - return new Promise((resolve, reject) => { - const file = fs.createWriteStream(dest) - https - .get(url, (res) => { - if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { - file.close() - try { - fs.unlinkSync(dest) - } catch { - /* ignore */ - } - downloadToFile(res.headers.location, dest).then(resolve).catch(reject) - return - } - if (res.statusCode !== 200) { - file.close() - reject(new Error(`HTTP ${res.statusCode}`)) - return - } - res.pipe(file) - file.on('finish', () => { - file.close() - resolve() - }) - }) - .on('error', (e) => { - try { - file.close() - } catch { - /* ignore */ - } - reject(e) - }) - }) -} - -const chromeExe = path.join(resDir, 'chrome-win64', 'chrome.exe') -const markerPath = path.join(resDir, markerName) - -fs.mkdirSync(resDir, { recursive: true }) - -if (fs.existsSync(chromeExe) && fs.existsSync(markerPath)) { - console.log('[chrome-cft] 已存在完整包,跳过:', chromeExe) - process.exit(0) -} - -console.log('[chrome-cft] 获取元数据:', CFT_JSON_URL) -const metaBuf = await downloadBuffer(CFT_JSON_URL) -const doc = JSON.parse(metaBuf.toString('utf-8')) -const stable = doc.channels?.Stable -const list = stable?.downloads?.chrome ?? [] -const hit = list.find((x) => x.platform === PLATFORM_KEY) -if (!hit?.url) { - console.error('[chrome-cft] 元数据中无', PLATFORM_KEY) - process.exit(1) -} - -const zipPath = path.join(resDir, '_chrome-download.zip') -console.log('[chrome-cft] 下载(体积较大):', hit.url) -await downloadToFile(hit.url, zipPath) - -console.log('[chrome-cft] 解压中…') -const zip = new AdmZip(zipPath) -const tmp = path.join(resDir, '_extract_tmp') -if (fs.existsSync(tmp)) fs.rmSync(tmp, { recursive: true }) -fs.mkdirSync(tmp, { recursive: true }) -zip.extractAllTo(tmp, true) - -const inner = path.join(tmp, 'chrome-win64') -if (!fs.existsSync(path.join(inner, 'chrome.exe'))) { - console.error('[chrome-cft] zip 内未找到 chrome-win64/chrome.exe') - process.exit(1) -} - -const destDir = path.join(resDir, 'chrome-win64') -if (fs.existsSync(destDir)) fs.rmSync(destDir, { recursive: true }) -fs.cpSync(inner, destDir, { recursive: true }) -fs.rmSync(tmp, { recursive: true }) - -try { - fs.unlinkSync(zipPath) -} catch { - /* ignore */ -} - -fs.writeFileSync( - path.join(resDir, 'NOTICE-ChromeForTesting.txt'), - [ - '本目录包含 Chrome for Testing(Stable / Windows x64)。', - '清单来源:' + CFT_JSON_URL, - '与 chrome.exe 同目录文件均为官方 zip 原样复制,未修改二进制。', - '', - ].join('\n'), - 'utf-8', -) -fs.writeFileSync(markerPath, `${stable.version}\n${new Date().toISOString()}\n`, 'utf-8') -console.log('[chrome-cft] 完成:', chromeExe) diff --git a/electron/scripts/download-easytier.mjs b/electron/scripts/download-easytier.mjs deleted file mode 100644 index 54bddff..0000000 --- a/electron/scripts/download-easytier.mjs +++ /dev/null @@ -1,103 +0,0 @@ -/** - * 下载官方 EasyTier Windows x64 发行包并解压到 resources/easytier/ - * 须复制 exe 同目录下全部 DLL 等依赖,否则 easytier-core 会以 0xC0000135(3221225781)退出。 - * 设置 SKIP_EASYTIER=1 可跳过(CI 无网环境) - */ -import fs from 'fs' -import path from 'path' -import https from 'https' -import { fileURLToPath } from 'url' -import AdmZip from 'adm-zip' - -const __dirname = path.dirname(fileURLToPath(import.meta.url)) -const root = path.resolve(__dirname, '..') -const resDir = path.join(root, 'resources', 'easytier') -const DEFAULT_URL = - process.env.EASYTIER_DOWNLOAD_URL || - 'https://github.com/EasyTier/EasyTier/releases/download/v2.4.5/easytier-windows-x86_64-v2.4.5.zip' -const exeName = 'easytier-core.exe' -const bundleMarker = '.easytier_bundle_ok' - -if (process.env.SKIP_EASYTIER === '1') { - console.log('[easytier] SKIP_EASYTIER=1,跳过下载') - process.exit(0) -} - -function download(url) { - return new Promise((resolve, reject) => { - https - .get(url, (res) => { - if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { - download(res.headers.location).then(resolve).catch(reject) - return - } - if (res.statusCode !== 200) { - reject(new Error(`HTTP ${res.statusCode} ${url}`)) - return - } - const chunks = [] - res.on('data', (c) => chunks.push(c)) - res.on('end', () => resolve(Buffer.concat(chunks))) - res.on('error', reject) - }) - .on('error', reject) - }) -} - -function findExe(dir, name) { - if (!fs.existsSync(dir)) return null - for (const f of fs.readdirSync(dir, { withFileTypes: true })) { - const p = path.join(dir, f.name) - if (f.isDirectory()) { - const r = findExe(p, name) - if (r) return r - } else if (f.name === name) { - return p - } - } - return null -} - -fs.mkdirSync(resDir, { recursive: true }) -const outExe = path.join(resDir, exeName) -const markerPath = path.join(resDir, bundleMarker) -if (fs.existsSync(outExe) && fs.existsSync(markerPath)) { - console.log('[easytier] 已存在完整包,跳过:', outExe) - process.exit(0) -} -if (fs.existsSync(outExe) && !fs.existsSync(markerPath)) { - console.log('[easytier] 检测到旧版仅复制了 exe,将重新下载并解压全部依赖文件…') -} - -console.log('[easytier] 下载:', DEFAULT_URL) -const buf = await download(DEFAULT_URL) -const zip = new AdmZip(buf) -const tmp = path.join(resDir, '_extract_tmp') -if (fs.existsSync(tmp)) fs.rmSync(tmp, { recursive: true }) -fs.mkdirSync(tmp, { recursive: true }) -zip.extractAllTo(tmp, true) -const found = findExe(tmp, exeName) -if (!found) { - console.error('[easytier] zip 内未找到', exeName) - process.exit(1) -} -const srcDir = path.dirname(found) -for (const name of fs.readdirSync(srcDir)) { - const from = path.join(srcDir, name) - const to = path.join(resDir, name) - if (!fs.statSync(from).isFile()) continue - fs.copyFileSync(from, to) -} -fs.rmSync(tmp, { recursive: true }) -fs.writeFileSync( - path.join(resDir, 'NOTICE-EasyTier.txt'), - [ - 'EasyTier 以 LGPL-3.0 授权发布。', - '源码: https://github.com/EasyTier/EasyTier', - '本程序随官方 GitHub Release 附带 easytier-core.exe 及同目录依赖,未修改二进制。', - '', - ].join('\n'), - 'utf-8', -) -fs.writeFileSync(markerPath, `${new Date().toISOString()}\n`, 'utf-8') -console.log('[easytier] 完成(已复制 zip 内同目录全部文件):', outExe) diff --git a/electron/src/main/chromeForTestingBootstrap.ts b/electron/src/main/chromeForTestingBootstrap.ts deleted file mode 100644 index bad6a20..0000000 --- a/electron/src/main/chromeForTestingBootstrap.ts +++ /dev/null @@ -1,237 +0,0 @@ -/** - * 1) 安装包内置:构建时 scripts/download-chrome-for-testing.mjs 解压至 resources/chrome-cft/(随 extraResources 分发) - * 2) 兜底:无内置时从官方源下载至 userData/cft-chrome/ - */ -import { app } from 'electron' -import fs, { createWriteStream } from 'fs' -import path from 'path' -import { Readable } from 'stream' -import { pipeline } from 'stream/promises' -import { fileURLToPath } from 'url' -import AdmZip from 'adm-zip' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -import { appendClientLog } from './clientDb' - -const CFT_JSON_URL = - 'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json' - -const LOCK_NAME = 'cft-chrome.lock' - -function cftRoot(): string { - return path.join(app.getPath('userData'), 'cft-chrome') -} - -function fileExists(p: string): boolean { - try { - return fs.existsSync(p) && fs.statSync(p).isFile() - } catch { - return false - } -} - -/** 与官方 zip 内顶层目录名一致,如 chrome-win64、chrome-mac-arm64 */ -export function getCftPlatformKey(): string { - if (process.platform === 'win32') { - return process.arch === 'ia32' ? 'win32' : 'win64' - } - if (process.platform === 'darwin') { - return process.arch === 'arm64' ? 'mac-arm64' : 'mac-x64' - } - return 'linux64' -} - -function extractFolderName(platformKey: string): string { - return `chrome-${platformKey}` -} - -/** 解压完成后可执行文件相对 cftRoot 的路径(按官方包结构) */ -function expectedChromeRelative(platformKey: string): string { - const folder = extractFolderName(platformKey) - if (platformKey === 'win32' || platformKey === 'win64') { - return path.join(folder, 'chrome.exe') - } - if (platformKey === 'linux64') { - return path.join(extractFolderName(platformKey), 'chrome') - } - if (platformKey === 'mac-arm64') { - return path.join( - 'chrome-mac-arm64', - 'Google Chrome for Testing.app', - 'Contents', - 'MacOS', - 'Google Chrome for Testing', - ) - } - if (platformKey === 'mac-x64') { - return path.join( - 'chrome-mac-x64', - 'Google Chrome for Testing.app', - 'Contents', - 'MacOS', - 'Google Chrome for Testing', - ) - } - return path.join(folder, 'chrome.exe') -} - -export function getCftChromePathIfPresent(): string | null { - const root = cftRoot() - const rel = expectedChromeRelative(getCftPlatformKey()) - const full = path.join(root, rel) - return fileExists(full) ? full : null -} - -/** 安装包/开发目录随包分发的 Chrome for Testing(Windows x64),含完整依赖目录 */ -export function getBundledChromePath(): string | null { - if (process.platform !== 'win32') return null - const rel = path.join('chrome-win64', 'chrome.exe') - if (app.isPackaged) { - const p = path.join(process.resourcesPath, 'chrome-cft', rel) - return fileExists(p) ? p : null - } - const dev = path.resolve(__dirname, '../../resources/chrome-cft', rel) - return fileExists(dev) ? dev : null -} - -type CftJson = { - channels?: { - Stable?: { - version?: string - downloads?: { - chrome?: Array<{ platform: string; url: string }> - } - } - } -} - -async function fetchJson(): Promise { - const res = await fetch(CFT_JSON_URL, { redirect: 'follow' }) - if (!res.ok) { - throw new Error(`获取 Chrome for Testing 元数据失败:HTTP ${res.status}`) - } - return (await res.json()) as CftJson -} - -function pickChromeZipUrl(doc: CftJson, platformKey: string): { version: string; url: string } { - const stable = doc.channels?.Stable - const version = stable?.version?.trim() ?? '' - const list = stable?.downloads?.chrome ?? [] - const hit = list.find((x) => x.platform === platformKey) - if (!hit?.url) { - throw new Error(`Chrome for Testing 元数据中缺少平台 ${platformKey} 的下载地址。`) - } - return { version, url: hit.url } -} - -async function downloadFile(url: string, dest: string): Promise { - const res = await fetch(url, { redirect: 'follow' }) - if (!res.ok) { - throw new Error(`下载失败:HTTP ${res.status}`) - } - if (!res.body) { - throw new Error('下载失败:无响应体') - } - await fs.promises.mkdir(path.dirname(dest), { recursive: true }) - const tmp = `${dest}.part` - await pipeline(Readable.fromWeb(res.body as any), createWriteStream(tmp)) - try { - fs.renameSync(tmp, dest) - } catch { - fs.copyFileSync(tmp, dest) - fs.unlinkSync(tmp) - } -} - -let inFlight: Promise | null = null - -/** - * 确保 userData 下已有 CFT Chrome;若尚无则下载并解压。失败返回 null。 - */ -export function ensureChromeForTestingInstalled(): Promise { - const existing = getCftChromePathIfPresent() - if (existing) return Promise.resolve(existing) - - if (!inFlight) { - inFlight = doInstall().finally(() => { - inFlight = null - }) - } - return inFlight -} - -async function doInstall(): Promise { - const root = cftRoot() - const platformKey = getCftPlatformKey() - const lockPath = path.join(root, LOCK_NAME) - const zipPath = path.join(root, `chrome-${platformKey}.zip`) - const versionPath = path.join(root, 'version.txt') - - try { - fs.mkdirSync(root, { recursive: true }) - } catch { - /* ignore */ - } - - try { - fs.writeFileSync(lockPath, `${Date.now()}\n`, 'utf-8') - } catch { - /* ignore */ - } - - appendClientLog( - 'info', - 'main', - '未找到内置 Chrome 与系统安装,正从 Chrome for Testing 拉取备用副本至用户目录(体积较大)…', - ) - - try { - const doc = await fetchJson() - const { version, url } = pickChromeZipUrl(doc, platformKey) - appendClientLog('info', 'main', `Chrome for Testing 版本:${version}`) - - await downloadFile(url, zipPath) - - appendClientLog('info', 'main', '下载完成,正在解压…') - const zip = new AdmZip(zipPath) - zip.extractAllTo(root, true) - - try { - fs.unlinkSync(zipPath) - } catch { - /* ignore */ - } - - const exe = path.join(root, expectedChromeRelative(platformKey)) - if (!fileExists(exe)) { - appendClientLog('error', 'main', `解压后未找到 Chrome:${exe}`) - return null - } - - try { - fs.writeFileSync(versionPath, `${version}\n`, 'utf-8') - } catch { - /* ignore */ - } - - appendClientLog('info', 'main', `Chrome for Testing 已就绪:${exe}`) - return exe - } catch (e) { - const msg = e instanceof Error ? e.message : String(e) - appendClientLog('error', 'main', `自动安装 Chrome for Testing 失败:${msg}`) - try { - if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath) - } catch { - /* ignore */ - } - return null - } finally { - try { - if (fs.existsSync(lockPath)) fs.unlinkSync(lockPath) - } catch { - /* ignore */ - } - } -} diff --git a/electron/src/main/easytier.ts b/electron/src/main/easytier.ts deleted file mode 100644 index 2e8b46f..0000000 --- a/electron/src/main/easytier.ts +++ /dev/null @@ -1,229 +0,0 @@ -/** - * 内置 EasyTier(Windows):随应用分发 easytier-core.exe,自动生成组网密钥,无需用户单独安装。 - * 二进制来源:https://github.com/EasyTier/EasyTier (LGPL-3.0) - */ -import { app } from 'electron' -import { execFile, spawn, type ChildProcess } from 'child_process' -import { promisify } from 'util' -import crypto from 'crypto' -import fs from 'fs' -import path from 'path' -import { fileURLToPath } from 'url' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -const execFileAsync = promisify(execFile) - -export type EasyTierNetworkConfig = { - networkName: string - networkSecret: string - peerUrls: string[] -} - -let easytierChild: ChildProcess | null = null -let easytierLastError = '' - -/** Windows 进程退出码(含 NTSTATUS)可读说明 */ -function formatWindowsExitHint(code: number): string { - const u = code >>> 0 - if (u === 0xc0000135) { - return ' — 缺少 DLL(常见:官方 zip 内依赖未与 exe 同目录;开发环境请删除 electron/resources/easytier 后重新执行 pnpm run prepare-easytier)。也可能是未安装 VC++ 运行库。' - } - if (u === 0xc000007b) { - return ' — 无效映像(32/64 位与系统不符或文件损坏)。' - } - return '' -} - -export function getBundledEasyTierExePath(): string | null { - if (process.platform !== 'win32') return null - const exe = 'easytier-core.exe' - if (app.isPackaged) { - const p = path.join(process.resourcesPath, 'easytier', exe) - return fs.existsSync(p) ? p : null - } - const dev = path.resolve(__dirname, '../../resources/easytier', exe) - return fs.existsSync(dev) ? dev : null -} - -/** 与 easytier-core 同目录分发的 CLI,用于 peer list 等工作台统计 */ -export function getBundledEasyTierCliPath(): string | null { - if (process.platform !== 'win32') return null - const exe = 'easytier-cli.exe' - if (app.isPackaged) { - const p = path.join(process.resourcesPath, 'easytier', exe) - return fs.existsSync(p) ? p : null - } - const dev = path.resolve(__dirname, '../../resources/easytier', exe) - return fs.existsSync(dev) ? dev : null -} - -export type EasyTierPeerStats = { - ok: boolean - error?: string - totalPeers?: number - onlinePeers?: number - raw?: string -} - -function parsePeerListOutput(text: string): { totalPeers: number; onlinePeers: number } { - const lines = text - .split(/\r?\n/) - .map((l) => l.trim()) - .filter((l) => l.length > 0) - const ipRe = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g - let withIp = 0 - let online = 0 - for (const line of lines) { - if (/^[-=│├└┌┐┘┬┴┼]+$/.test(line)) continue - if (/^peer\s|^id\s|^hostname\s/i.test(line) && line.length < 100) continue - const ips = line.match(ipRe) - if (!ips) continue - withIp += 1 - const low = line.toLowerCase() - if (!/offline|unreachable|disconnected|lost|timeout/.test(low)) online += 1 - } - if (withIp === 0) { - const guess = lines.filter((l) => l.length > 12 && !/^error|^usage/i.test(l)).length - const n = Math.max(0, guess > 0 ? guess - 1 : 0) - return { totalPeers: n, onlinePeers: n } - } - return { totalPeers: withIp, onlinePeers: online > 0 ? online : withIp } -} - -/** 调用 easytier-cli peer list;需 easytier-core 已运行且 RPC 端口可达 */ -export async function queryEasyTierPeerStats(): Promise { - const cli = getBundledEasyTierCliPath() - if (!cli) return { ok: false, error: '未找到内置 easytier-cli.exe' } - if (!getEasyTierRuntimeState().running) { - return { ok: false, error: 'easytier-core 未运行', totalPeers: 0, onlinePeers: 0 } - } - try { - const { stdout, stderr } = await execFileAsync(cli, ['peer', 'list'], { - cwd: path.dirname(cli), - timeout: 5000, - maxBuffer: 2 * 1024 * 1024, - windowsHide: true, - }) - const raw = `${stdout || ''}${stderr || ''}` - const { totalPeers, onlinePeers } = parsePeerListOutput(raw) - return { ok: true, totalPeers, onlinePeers, raw: raw.slice(0, 6000) } - } catch (e) { - const msg = e instanceof Error ? e.message : String(e) - return { ok: false, error: msg, totalPeers: 0, onlinePeers: 0 } - } -} - -export function readEasyTierNetworkName(backendRoot: string): string { - const p = path.join(backendRoot, 'config', 'easytier_network.json') - if (!fs.existsSync(p)) return '' - try { - const raw = JSON.parse(fs.readFileSync(p, 'utf-8')) as Record - return String(raw.networkName ?? '') - } catch { - return '' - } -} - -export function loadOrCreateNetworkConfig(backendRoot: string): EasyTierNetworkConfig { - const p = path.join(backendRoot, 'config', 'easytier_network.json') - fs.mkdirSync(path.dirname(p), { recursive: true }) - if (fs.existsSync(p)) { - try { - const raw = JSON.parse(fs.readFileSync(p, 'utf-8')) as Record - return { - networkName: String(raw.networkName ?? ''), - networkSecret: String(raw.networkSecret ?? ''), - peerUrls: Array.isArray(raw.peerUrls) ? raw.peerUrls.map(String) : [], - } - } catch { - /* fallthrough */ - } - } - const cfg: EasyTierNetworkConfig = { - networkName: `lr_${crypto.randomBytes(6).toString('hex')}`, - networkSecret: crypto.randomBytes(24).toString('hex'), - peerUrls: [], - } - fs.writeFileSync(p, JSON.stringify(cfg, null, 2), 'utf-8') - return cfg -} - -export function getEasyTierRuntimeState(): { - bundled: boolean - running: boolean - lastError: string -} { - const bundled = !!getBundledEasyTierExePath() - const running = !!easytierChild && !easytierChild.killed - return { bundled, running, lastError: easytierLastError } -} - -export function startBundledEasyTier(backendRoot: string): void { - easytierLastError = '' - const exe = getBundledEasyTierExePath() - if (!exe) { - easytierLastError = '未找到内置 easytier-core.exe(开发环境可执行 pnpm run prepare-easytier)' - return - } - if (easytierChild && !easytierChild.killed) { - return - } - const cfg = loadOrCreateNetworkConfig(backendRoot) - const args = ['-d', '--network-name', cfg.networkName, '--network-secret', cfg.networkSecret] - for (const u of cfg.peerUrls) { - const t = u.trim() - if (t) args.push('-p', t) - } - let child: ChildProcess - try { - child = spawn(exe, args, { - cwd: path.dirname(exe), - windowsHide: true, - stdio: ['ignore', 'pipe', 'pipe'], - env: { ...process.env }, - }) - } catch (e) { - easytierLastError = e instanceof Error ? e.message : String(e) - easytierChild = null - return - } - easytierChild = child - const appendErr = (chunk: Buffer): void => { - const s = chunk.toString() - if (s.length < 2000) easytierLastError = (easytierLastError + s).slice(-2000) - } - child.stderr?.on('data', appendErr) - child.stdout?.on('data', appendErr) - child.on('error', (err) => { - easytierLastError = err.message - }) - child.on('close', (code) => { - if (code !== 0 && code !== null) { - const hint = formatWindowsExitHint(code) - easytierLastError = `easytier-core 已退出(代码 ${code})${hint}${easytierLastError ? `\n${easytierLastError}` : ''}` - } - easytierChild = null - }) -} - -export function stopBundledEasyTier(): void { - if (!easytierChild || easytierChild.killed) { - easytierChild = null - return - } - try { - if (process.platform === 'win32') { - spawn('taskkill', ['/PID', String(easytierChild.pid), '/T', '/F'], { - windowsHide: true, - stdio: 'ignore', - }) - } else { - easytierChild.kill('SIGTERM') - } - } catch { - /* ignore */ - } - easytierChild = null -} diff --git a/electron/src/main/index.ts b/electron/src/main/index.ts index 44de320..8e31a87 100644 --- a/electron/src/main/index.ts +++ b/electron/src/main/index.ts @@ -8,14 +8,6 @@ import crypto from 'crypto' import { networkInterfaces } from 'os' import { fileURLToPath } from 'url' -import { - getEasyTierRuntimeState, - loadOrCreateNetworkConfig, - queryEasyTierPeerStats, - readEasyTierNetworkName, - startBundledEasyTier, - stopBundledEasyTier, -} from './easytier' import { ensureYoutubeStudioWindow } from './youtubeStudioSidecar' import { appendClientLog, @@ -45,7 +37,7 @@ const __dirname = path.dirname(__filename) const execFileAsync = promisify(execFile) const PORT = Number(process.env.WEB2_PORT || 8001) -/** uvicorn 监听地址;0.0.0.0 时建议配置 config/web2_api_token.txt 供远程/EasyTier 访问 */ +/** uvicorn 监听地址;0.0.0.0 时建议配置 config/web2_api_token.txt 供远程访问 */ let web2Host = '127.0.0.1' let mainWindow: BrowserWindow | null = null @@ -57,48 +49,32 @@ let _pythonExe: string | null = null let pendingNav: string | null = null /** - * 供扫码绑定 baseUrl:优先虚拟网卡(EasyTier 等),其次 10.x,再补充 Wi‑Fi/以太网等局域网 IPv4, - * 避免仅有虚拟网时列表为空、用户无处选 IP。 + * 供扫码绑定 baseUrl:优先 Wi‑Fi/以太网等局域网 IPv4,排除常见虚拟网卡名。 */ -function collectEasyTierCandidateIPs(): { address: string; name: string }[] { +function collectLanCandidateIPs(): { address: string; name: string }[] { const nets = networkInterfaces() const seen = new Set() - const out: { address: string; name: string }[] = [] - const nameMatch = /easytier|wintun|tun|tap|netbird|wireguard|tailscale|zerotier|sing-tun|nordlynx/i + const preferred: { address: string; name: string }[] = [] + const other: { address: string; name: string }[] = [] + + const preferredRe = /(wlan|wi-?fi|wifi|wireless)/i + const excludedRe = /(easytier|wintun|tap|tun|netbird|wireguard|tailscale|zerotier|sing-tun|nordlynx|docker|vmware|virtualbox|vbox|venet|virtual ethernet|\bve\b)/i for (const [name, addrs] of Object.entries(nets)) { if (!addrs) continue - for (const a of addrs) { - if (a.family !== 'IPv4' || a.internal) continue - if (nameMatch.test(name) && !seen.has(a.address)) { - seen.add(a.address) - out.push({ address: a.address, name }) - } - } - } - if (out.length === 0) { - for (const [name, addrs] of Object.entries(nets)) { - if (!addrs) continue - for (const a of addrs) { - if (a.family !== 'IPv4' || a.internal) continue - if (a.address.startsWith('10.') && !seen.has(a.address)) { - seen.add(a.address) - out.push({ address: a.address, name: `${name}(10.x,可能含 EasyTier)` }) - } - } - } - } - for (const [name, addrs] of Object.entries(nets)) { - if (!addrs) continue + if (excludedRe.test(name)) continue for (const a of addrs) { if (a.family !== 'IPv4' || a.internal) continue if (a.address === '127.0.0.1') continue if (seen.has(a.address)) continue seen.add(a.address) - out.push({ address: a.address, name: `${name}(局域网)` }) + const item = { address: a.address, name: name || 'network' } + if (preferredRe.test(name)) preferred.push(item) + else other.push(item) } } - return out + if (preferred.length) return preferred + return other } async function restartWeb2Backend(): Promise<{ ok: true } | { ok: false; error: string }> { @@ -562,9 +538,6 @@ if (!gotLock) { return { ok: true as const } }) - ipcMain.removeHandler('easytier:get-peer-stats') - ipcMain.handle('easytier:get-peer-stats', () => queryEasyTierPeerStats()) - ipcMain.removeHandler('youtube-studio:ensure') ipcMain.handle('youtube-studio:ensure', async (_evt, suffix: unknown) => { await ensureYoutubeStudioWindow(typeof suffix === 'string' ? suffix : undefined) @@ -584,28 +557,19 @@ if (!gotLock) { ipcMain.removeHandler('remote:get-status') ipcMain.handle('remote:get-status', () => { const bindHost = resolveWeb2Host() - const et = getEasyTierRuntimeState() - const br = _backendRoot ?? getBackendRoot() - const easytierNetworkName = readEasyTierNetworkName(br) return { bindHost, remoteEnabled: bindHost === '0.0.0.0', port: PORT, hasToken: !!resolveWeb2ApiTokenForChild(), - candidates: collectEasyTierCandidateIPs(), + candidates: collectLanCandidateIPs(), envHostOverride: !!process.env.WEB2_HOST?.trim(), - easytierBundled: et.bundled, - easytierRunning: et.running, - easytierLastError: et.lastError, - easytierNetworkName, } }) ipcMain.removeHandler('remote:get-bind-payload') ipcMain.handle('remote:get-bind-payload', (_evt, selectedIp: unknown) => { if (typeof selectedIp !== 'string' || !selectedIp.trim()) return null - const br = _backendRoot ?? getBackendRoot() - const cfg = loadOrCreateNetworkConfig(br) const token = resolveWeb2ApiTokenForChild() ?? '' const ip = selectedIp.trim() return { @@ -613,11 +577,6 @@ if (!gotLock) { baseUrl: `http://${ip}:${PORT}`, port: PORT, token, - easytier: { - networkName: cfg.networkName, - networkSecret: cfg.networkSecret, - peerUrls: cfg.peerUrls, - }, } }) @@ -640,7 +599,6 @@ if (!gotLock) { fs.mkdirSync(cfgDir, { recursive: true }) fs.writeFileSync(path.join(cfgDir, 'web2_bind_host.txt'), enabled ? '0.0.0.0' : '127.0.0.1', 'utf-8') const r = await restartWeb2Backend() - if (r.ok && enabled) startBundledEasyTier(br) return r }) @@ -652,7 +610,6 @@ if (!gotLock) { const tok = crypto.randomBytes(24).toString('hex') fs.writeFileSync(path.join(cfgDir, 'web2_api_token.txt'), tok, 'utf-8') const r = await restartWeb2Backend() - if (r.ok) startBundledEasyTier(br) return r }) @@ -686,8 +643,6 @@ if (!gotLock) { return } - startBundledEasyTier(backendRoot) - appendClientLog('info', 'main', '本地服务已连接,准备打开界面') await createWindow() @@ -698,7 +653,6 @@ if (!gotLock) { }) app.on('window-all-closed', () => { - stopBundledEasyTier() killPythonTree() app.quit() }) @@ -706,7 +660,6 @@ if (!gotLock) { app.on('before-quit', () => { appendClientLog('info', 'main', '应用退出') flushClientDb() - stopBundledEasyTier() killPythonTree() }) } diff --git a/electron/src/preload/index.ts b/electron/src/preload/index.ts index e8cec57..cf9b2ca 100644 --- a/electron/src/preload/index.ts +++ b/electron/src/preload/index.ts @@ -25,9 +25,8 @@ contextBridge.exposeInMainWorld('workspace', { getDashboard: () => ipcRenderer.invoke('workspace:get-dashboard'), }) -/** 工作台 EasyTier;YouTube 后台在系统浏览器打开 */ +/** YouTube 后台在系统浏览器打开 */ contextBridge.exposeInMainWorld('deskBridge', { - getEasyTierPeers: () => ipcRenderer.invoke('easytier:get-peer-stats'), ensureYoutubeStudio: (streamKeySuffix?: string) => ipcRenderer.invoke('youtube-studio:ensure', streamKeySuffix ?? ''), }) diff --git a/electron/src/renderer/src/RemoteBinding.tsx b/electron/src/renderer/src/RemoteBinding.tsx index 0a0fd53..3be6246 100644 --- a/electron/src/renderer/src/RemoteBinding.tsx +++ b/electron/src/renderer/src/RemoteBinding.tsx @@ -9,10 +9,6 @@ type RemoteStatus = { hasToken: boolean candidates: { address: string; name: string }[] envHostOverride: boolean - easytierBundled: boolean - easytierRunning: boolean - easytierLastError: string - easytierNetworkName: string } type RemoteBindingApi = { @@ -22,7 +18,7 @@ type RemoteBindingApi = { getBindPayload: (selectedIp: string) => Promise | null> } -/** React Native 扫码:JSON 含 baseUrl、token、easytier(组网名/密钥/可选 peer) */ +/** 远程控制:候选 IP、绑定 JSON、二维码(供手机扫码);手机端仍可改地址后保存 */ export default function RemoteBinding() { const rb = (typeof window !== 'undefined' ? (window as Window & { remoteBinding?: RemoteBindingApi }).remoteBinding @@ -32,12 +28,8 @@ export default function RemoteBinding() { const [busy, setBusy] = useState(false) const [msg, setMsg] = useState('') const [selectedIp, setSelectedIp] = useState('') - const [qrImg, setQrImg] = useState(null) - const [bindPayload, setBindPayload] = useState | null>(null) - - const pickBindIp = useCallback((): string => { - return selectedIp.trim() - }, [selectedIp]) + const [payloadJson, setPayloadJson] = useState('') + const [qrDataUrl, setQrDataUrl] = useState('') const load = useCallback(async () => { if (!rb?.getStatus) { @@ -66,7 +58,6 @@ export default function RemoteBinding() { const autoEnableRemoteOnce = useRef(false) - /** 默认开启局域网/虚拟网访问,且界面不允许关闭 */ useEffect(() => { if (!rb?.setRemoteEnabled || !status) return if (status.envHostOverride) return @@ -91,40 +82,51 @@ export default function RemoteBinding() { }, [rb, status?.remoteEnabled, status?.envHostOverride, load]) useEffect(() => { - if (!status?.candidates?.length) return - setSelectedIp((prev) => { - if (prev && status.candidates.some((c) => c.address === prev)) return prev - return status.candidates[0].address - }) - }, [status]) - - useEffect(() => { - if (!status?.remoteEnabled || !rb?.getBindPayload) { - setBindPayload(null) + const list = status?.candidates ?? [] + if (!list.length) { + setSelectedIp('') return } - const ip = pickBindIp() - if (!ip) { - setBindPayload(null) + setSelectedIp((prev) => { + if (prev && list.some((c) => c.address === prev)) return prev + return list[0].address + }) + }, [status?.candidates]) + + useEffect(() => { + if (!rb?.getBindPayload || !selectedIp.trim()) { + setPayloadJson('') + setQrDataUrl('') return } let cancelled = false - void rb.getBindPayload(ip).then((p) => { - if (!cancelled) setBindPayload(p) - }) + void (async () => { + setBusy(true) + try { + const p = await rb.getBindPayload(selectedIp.trim()) + if (cancelled || !p) { + setPayloadJson('') + setQrDataUrl('') + return + } + const json = JSON.stringify(p) + setPayloadJson(json) + const url = await QRCode.toDataURL(json, { width: 220, margin: 1, errorCorrectionLevel: 'M' }) + if (!cancelled) setQrDataUrl(url) + } catch (e) { + if (!cancelled) { + setPayloadJson('') + setQrDataUrl('') + setMsg(e instanceof Error ? e.message : String(e)) + } + } finally { + if (!cancelled) setBusy(false) + } + })() return () => { cancelled = true } - }, [status?.remoteEnabled, rb, status?.hasToken, pickBindIp]) - - useEffect(() => { - if (!bindPayload) { - setQrImg(null) - return - } - const json = JSON.stringify(bindPayload) - void QRCode.toDataURL(json, { margin: 2, width: 224 }).then(setQrImg).catch(() => setQrImg(null)) - }, [bindPayload]) + }, [rb, selectedIp, status?.port]) const onGenerateToken = async () => { if (!rb?.generateToken) return @@ -143,30 +145,6 @@ export default function RemoteBinding() { } } - const onGenerateQr = async () => { - if (!rb?.getBindPayload) return - if (!status?.remoteEnabled) { - setMsg('请等待局域网 / 虚拟网访问已开启并生效后再试。') - return - } - const ip = pickBindIp() - if (!ip) { - const hasList = !!status.candidates?.length - setMsg(hasList ? '请从上方选择本机可访问的 IP。' : '未检测到本机局域网 IP,请检查网卡与虚拟网。') - return - } - setBusy(true) - setMsg('') - try { - const p = await rb.getBindPayload(ip) - setBindPayload(p) - } catch (e) { - setMsg(e instanceof Error ? e.message : String(e)) - } finally { - setBusy(false) - } - } - if (!rb?.getStatus) { return (
@@ -176,6 +154,7 @@ export default function RemoteBinding() { } const s = status + const candidates = s?.candidates ?? [] return (
@@ -186,84 +165,52 @@ export default function RemoteBinding() { )}
- -
- -
- - - {status?.hasToken ? '已配置密钥' : '未配置密钥(远程时强烈建议)'} + + API Token:{status?.hasToken ? '已配置' : '未配置'} +
-
-
- - 供 React Native 扫码;需已开启「允许远程」并填写可访问的 IP。 -
- - {!s?.remoteEnabled && ( -

正在或等待开启局域网访问;生效后虚拟网内手机可访问本机 API 并扫码绑定。

- )} - - {s?.remoteEnabled && bindPayload && qrImg && ( + {payloadJson && qrDataUrl && (
-

- React Native 扫码解析 JSON:含 baseUrltoken、 - easytier(与内置客户端使用相同组网名与密钥)。勿外传。 -

- 绑定信息二维码 +

用手机 App 扫描下方二维码导入绑定;导入后可在 App 内修改 IP/域名与端口再保存。

+ 绑定二维码
-            {JSON.stringify(bindPayload, null, 2)}
+            {payloadJson}
           
- +

内容与二维码一致(v1 JSON,含 baseUrl / port / token)。

)} - {s?.remoteEnabled && !bindPayload && ( -

选择局域网 IP 后点击「生成绑定二维码」。

- )} - {msg && (

{msg} diff --git a/electron/src/renderer/src/WorkspaceDashboard.tsx b/electron/src/renderer/src/WorkspaceDashboard.tsx index ab867e5..4a1fecd 100644 --- a/electron/src/renderer/src/WorkspaceDashboard.tsx +++ b/electron/src/renderer/src/WorkspaceDashboard.tsx @@ -30,13 +30,6 @@ type ClientOverview = { } } -type EasyTierPeerPayload = { - ok?: boolean - error?: string - totalPeers?: number - onlinePeers?: number -} - type RelayLivePayload = { youtubeProcess?: string processStatus?: string @@ -115,7 +108,6 @@ export default function WorkspaceDashboard() { const ws = getWorkspace() const [data, setData] = useState(null) const [overview, setOverview] = useState(null) - const [peerStats, setPeerStats] = useState(null) const [err, setErr] = useState('') const [proMode, setProMode] = useState(false) const [proLive, setProLive] = useState(null) @@ -129,13 +121,7 @@ export default function WorkspaceDashboard() { try { const d = (await ws.getDashboard()) as WorkspaceDbSnapshot setData(d) - const desk = ( - window as Window & { - deskBridge?: { getEasyTierPeers?: () => Promise } - } - ).deskBridge let ov: ClientOverview | null = null - let et: EasyTierPeerPayload | null = null try { const r = await apiFetch(apiUrl('/client/overview')) if (r.ok) { @@ -145,14 +131,8 @@ export default function WorkspaceDashboard() { } catch { setOverview(null) } - try { - et = (await desk?.getEasyTierPeers?.()) ?? null - setPeerStats(et) - } catch { - setPeerStats(null) - } if (ov) { - saveWorkspaceSnapshot('client_overview', { ...ov, easyTierPeers: et }) + saveWorkspaceSnapshot('client_overview', ov) } } catch (e) { setErr(e instanceof Error ? e.message : String(e)) @@ -210,7 +190,7 @@ export default function WorkspaceDashboard() { channels={overview?.youtubeChannels ?? []} relayLive={proMode ? proLive : null} recentLiveEventCount={data?.liveEvents?.length ?? 0} - peerOnline={peerStats?.onlinePeers ?? 0} + peerOnline={0} />

diff --git a/electron/src/renderer/src/vite-env.d.ts b/electron/src/renderer/src/vite-env.d.ts index c0c31eb..cd0575d 100644 --- a/electron/src/renderer/src/vite-env.d.ts +++ b/electron/src/renderer/src/vite-env.d.ts @@ -25,10 +25,6 @@ interface Window { hasToken: boolean candidates: { address: string; name: string }[] envHostOverride: boolean - easytierBundled: boolean - easytierRunning: boolean - easytierLastError: string - easytierNetworkName: string }> setRemoteEnabled: (enabled: boolean) => Promise<{ ok: true } | { ok: false; error: string }> generateToken: () => Promise<{ ok: true } | { ok: false; error: string }> @@ -47,12 +43,6 @@ interface Window { }> } deskBridge?: { - getEasyTierPeers?: () => Promise<{ - ok?: boolean - error?: string - totalPeers?: number - onlinePeers?: number - }> ensureYoutubeStudio?: (streamKeySuffix?: string) => Promise } appTheme?: { diff --git a/electron/tsconfig.node.tsbuildinfo b/electron/tsconfig.node.tsbuildinfo new file mode 100644 index 0000000..03ebbb2 --- /dev/null +++ b/electron/tsconfig.node.tsbuildinfo @@ -0,0 +1 @@ +{"fileNames":["./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es5.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.dom.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.webworker.importscripts.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.scripthost.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/.pnpm/typescript@5.6.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/compatibility/index.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/ts5.6/globals.typedarray.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/ts5.6/buffer.buffer.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/globals.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/web-globals/events.d.ts","./node_modules/.pnpm/buffer@5.7.1/node_modules/buffer/index.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/header.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/readable.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/file.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/fetch.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/formdata.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/connector.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/client.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/errors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/dispatcher.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-dispatcher.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-origin.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool-stats.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/handlers.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/balanced-pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-interceptor.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-client.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-errors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/proxy-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-handler.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/api.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/interceptors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/util.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cookies.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/patch.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/websocket.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/eventsource.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/filereader.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/content-type.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cache.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/index.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/web-globals/storage.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/assert.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/assert/strict.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/async_hooks.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/buffer.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/child_process.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/cluster.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/console.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/constants.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/crypto.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/dgram.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/dns.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/dns/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/domain.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/events.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/fs.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/fs/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/http.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/http2.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/https.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/inspector.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/inspector.generated.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/module.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/net.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/os.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/path.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/perf_hooks.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/process.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/punycode.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/querystring.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/readline.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/readline/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/repl.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/sea.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/sqlite.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/stream.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/stream/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/stream/consumers.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/stream/web.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/string_decoder.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/test.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/timers.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/timers/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/tls.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/trace_events.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/tty.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/url.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/util.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/v8.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/vm.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/wasi.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/worker_threads.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/zlib.d.ts","./node_modules/.pnpm/@types+node@22.19.15/node_modules/@types/node/ts5.6/index.d.ts","./node_modules/.pnpm/@types+estree@1.0.8/node_modules/@types/estree/index.d.ts","./node_modules/.pnpm/rollup@4.60.0/node_modules/rollup/dist/rollup.d.ts","./node_modules/.pnpm/rollup@4.60.0/node_modules/rollup/dist/parseast.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.15/node_modules/vite/types/hmrpayload.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.15/node_modules/vite/types/customevent.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.15/node_modules/vite/types/hot.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.15/node_modules/vite/dist/node/types.d-agj9qkwt.d.ts","./node_modules/.pnpm/esbuild@0.21.5/node_modules/esbuild/lib/main.d.ts","./node_modules/.pnpm/source-map-js@1.2.1/node_modules/source-map-js/source-map.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/previous-map.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/input.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/declaration.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/root.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/warning.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/lazy-result.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/no-work-result.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/processor.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/result.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/document.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/rule.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/node.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/comment.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/container.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/at-rule.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/list.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/postcss.d.ts","./node_modules/.pnpm/postcss@8.5.8/node_modules/postcss/lib/postcss.d.mts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.15/node_modules/vite/dist/node/runtime.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.15/node_modules/vite/types/importglob.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.15/node_modules/vite/types/metadata.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.15/node_modules/vite/dist/node/index.d.ts","./node_modules/.pnpm/electron-vite@2.3.0_vite@5.4.21_@types+node@22.19.15_/node_modules/electron-vite/dist/index.d.ts","./node_modules/.pnpm/@babel+types@7.29.0/node_modules/@babel/types/lib/index.d.ts","./node_modules/.pnpm/@types+babel__generator@7.27.0/node_modules/@types/babel__generator/index.d.ts","./node_modules/.pnpm/@babel+parser@7.29.2/node_modules/@babel/parser/typings/babel-parser.d.ts","./node_modules/.pnpm/@types+babel__template@7.4.4/node_modules/@types/babel__template/index.d.ts","./node_modules/.pnpm/@types+babel__traverse@7.28.0/node_modules/@types/babel__traverse/index.d.ts","./node_modules/.pnpm/@types+babel__core@7.20.5/node_modules/@types/babel__core/index.d.ts","./node_modules/.pnpm/@vitejs+plugin-react@4.7.0_vite@5.4.21_@types+node@22.19.15_/node_modules/@vitejs/plugin-react/dist/index.d.ts","./electron.vite.config.ts"],"fileIdsList":[[54,103,120,121,125,186,193],[54,103,120,121,187],[54,103,120,121],[54,103,120,121,187,188,189,190,191],[54,103,120,121,187,189],[54,100,101,103,120,121],[54,102,103,120,121],[54,103,108,120,121,138],[54,103,104,109,114,120,121,123,135,146],[54,103,104,105,114,120,121,123],[49,50,51,54,103,120,121],[54,103,106,120,121,147],[54,103,107,108,115,120,121,124],[54,103,108,120,121,135,143],[54,103,109,111,114,120,121,123],[54,102,103,110,120,121],[54,103,111,112,120,121],[54,103,113,114,120,121],[54,102,103,114,120,121],[54,103,114,115,116,120,121,135,146],[54,103,114,115,116,120,121,130,135,138],[54,96,103,111,114,117,120,121,123,135,146],[54,103,114,115,117,118,120,121,123,135,143,146],[54,103,117,119,120,121,135,143,146],[54,103,114,120,121],[54,103,120,121,122,146],[54,103,111,114,120,121,123,135],[54,103,120,121,124],[54,103,120,121,125],[54,102,103,120,121,126],[54,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152],[54,103,120,121,128],[54,103,120,121,129],[54,103,114,120,121,130,131],[54,103,120,121,130,132,147,149],[54,103,115,120,121],[54,103,114,120,121,135,136,138],[54,103,120,121,137,138],[54,103,120,121,135,136],[54,103,120,121,138],[54,103,120,121,139],[54,100,103,120,121,135,140,146],[54,103,114,120,121,141,142],[54,103,120,121,141,142],[54,103,108,120,121,123,135,143],[54,103,120,121,144],[103,120,121],[52,53,54,55,56,57,58,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152],[54,103,120,121,123,145],[54,103,117,120,121,129,146],[54,103,108,120,121,147],[54,103,120,121,135,148],[54,103,120,121,122,149],[54,103,120,121,150],[54,96,103,120,121],[54,96,103,114,116,120,121,126,135,138,146,148,149,151],[54,103,120,121,135,152],[54,103,120,121,185,192],[54,103,120,121,153,185],[54,103,120,121,177],[54,103,120,121,175,177],[54,103,120,121,166,174,175,176,178,180],[54,103,120,121,164],[54,103,120,121,167,172,177,180],[54,103,120,121,163,180],[54,103,120,121,167,168,171,172,173,180],[54,103,120,121,167,168,169,171,172,180],[54,103,120,121,164,165,166,167,168,172,173,174,176,177,178,180],[54,103,120,121,180],[54,103,120,121,162,164,165,166,167,168,169,171,172,173,174,175,176,177,178,179],[54,103,120,121,162,180],[54,103,120,121,167,169,170,172,173,180],[54,103,120,121,171,180],[54,103,120,121,172,173,177,180],[54,103,120,121,165,175],[54,103,120,121,155,184],[54,103,120,121,154,155],[54,68,72,103,120,121,146],[54,68,103,120,121,135,146],[54,63,103,120,121],[54,65,68,103,120,121,143,146],[54,103,120,121,123,143],[54,103,120,121,153],[54,63,103,120,121,153],[54,65,68,103,120,121,123,146],[54,60,61,64,67,103,114,120,121,135,146],[54,68,75,103,120,121],[54,60,66,103,120,121],[54,68,89,90,103,120,121],[54,64,68,103,120,121,138,146,153],[54,89,103,120,121,153],[54,62,63,103,120,121,153],[54,68,103,120,121],[54,62,63,64,65,66,67,68,69,70,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,90,91,92,93,94,95,103,120,121],[54,68,83,103,120,121],[54,68,75,76,103,120,121],[54,66,68,76,77,103,120,121],[54,67,103,120,121],[54,60,63,68,103,120,121],[54,68,72,76,77,103,120,121],[54,72,103,120,121],[54,66,68,71,103,120,121,146],[54,60,65,68,75,103,120,121],[54,103,120,121,135],[54,63,68,89,103,120,121,151,153],[54,103,114,115,117,118,119,120,121,123,135,143,146,152,153,155,156,157,158,159,160,161,181,182,183,184],[54,103,120,121,157,158,159,160],[54,103,120,121,157,158,159],[54,103,120,121,157],[54,103,120,121,158],[54,103,120,121,155]],"fileInfos":[{"version":"a7297ff837fcdf174a9524925966429eb8e5feecc2cc55cc06574e6b092c1eaa","impliedFormat":1},{"version":"44e584d4f6444f58791784f1d530875970993129442a847597db702a073ca68c","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"9a68c0c07ae2fa71b44384a839b7b8d81662a236d4b9ac30916718f7510b1b2d","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"9e8ca8ed051c2697578c023d9c29d6df689a083561feba5c14aedee895853999","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"6920e1448680767498a0b77c6a00a8e77d14d62c3da8967b171f1ddffa3c18e4","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"45d8ccb3dfd57355eb29749919142d4321a0aa4df6acdfc54e30433d7176600a","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"93495ff27b8746f55d19fcbcdbaccc99fd95f19d057aed1bd2c0cafe1335fbf0","affectsGlobalScope":true,"impliedFormat":1},{"version":"6fc23bb8c3965964be8c597310a2878b53a0306edb71d4b5a4dfe760186bcc01","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea011c76963fb15ef1cdd7ce6a6808b46322c527de2077b6cfdf23ae6f5f9ec7","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"4738f2420687fd85629c9efb470793bb753709c2379e5f85bc1815d875ceadcd","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"9fc46429fbe091ac5ad2608c657201eb68b6f1b8341bd6d670047d32ed0a88fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1a94697425a99354df73d9c8291e2ecd4dddd370aed4023c2d6dee6cccb32666","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"bf14a426dbbf1022d11bd08d6b8e709a2e9d246f0c6c1032f3b2edb9a902adbe","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3f9fc0ec0b96a9e642f11eda09c0be83a61c7b336977f8b9fdb1e9788e925fe","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"479553e3779be7d4f68e9f40cdb82d038e5ef7592010100410723ceced22a0f7","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"33358442698bb565130f52ba79bfd3d4d484ac85fe33f3cb1759c54d18201393","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"1456e80bd8a3870034d89f91bd7df12ac29acfb083e31c0bb1fb38ca7bf5fbc2","affectsGlobalScope":true,"impliedFormat":1},{"version":"a98aedd64ad81793f146d36d1611ed9ba61b8b49ff040f0d13a103ed626595d9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"58647d85d0f722a1ce9de50955df60a7489f0593bf1a7015521efe901c06d770","impliedFormat":1},{"version":"6b4e081d55ac24fc8a4631d5dd77fe249fa25900abd7d046abb87d90e3b45645","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"83e63d6ccf8ec004a3bb6d58b9bb0104f60e002754b1e968024b320730cc5311","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"bceb58df66ab8fb00170df20cd813978c5ab84be1d285710c4eb005d8e9d8efb","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"f27524f4bef4b6519c604bdb23bf4465bddcccbf3f003abb901acbd0d7404d99","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"6b039f55681caaf111d5eb84d292b9bee9e0131d0db1ad0871eef0964f533c73","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"2b55d426ff2b9087485e52ac4bc7cfafe1dc420fc76dad926cd46526567c501a","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"358765d5ea8afd285d4fd1532e78b88273f18cb3f87403a9b16fef61ac9fdcfe","impliedFormat":1},{"version":"96e9e7f1164e252c30f2dee6f97148593e94975d6cc159e1da0c64eea4dc534b","impliedFormat":1},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"ee70b8037ecdf0de6c04f35277f253663a536d7e38f1539d270e4e916d225a3f","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"282f98006ed7fa9bb2cd9bdbe2524595cfc4bcd58a0bb3232e4519f2138df811","impliedFormat":1},{"version":"6222e987b58abfe92597e1273ad7233626285bc2d78409d4a7b113d81a83496b","impliedFormat":1},{"version":"cbe726263ae9a7bf32352380f7e8ab66ee25b3457137e316929269c19e18a2be","impliedFormat":1},{"version":"8b96046bf5fb0a815cba6b0880d9f97b7f3a93cf187e8dcfe8e2792e97f38f87","impliedFormat":99},{"version":"bacf2c84cf448b2cd02c717ad46c3d7fd530e0c91282888c923ad64810a4d511","affectsGlobalScope":true,"impliedFormat":1},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"7965dc3c7648e2a7a586d11781cabb43d4859920716bc2fdc523da912b06570d","impliedFormat":1},{"version":"90c2bd9a3e72fe08b8fa5982e78cb8dc855a1157b26e11e37a793283c52bf64b","impliedFormat":1},{"version":"a8122fe390a2a987079e06c573b1471296114677923c1c094c24a53ddd7344a2","impliedFormat":1},{"version":"70c2cb19c0c42061a39351156653aa0cf5ba1ecdc8a07424dd38e3a1f1e3c7f4","impliedFormat":1},{"version":"a8fb10fd8c7bc7d9b8f546d4d186d1027f8a9002a639bec689b5000dab68e35c","impliedFormat":1},{"version":"c9b467ea59b86bd27714a879b9ad43c16f186012a26d0f7110b1322025ceaa83","impliedFormat":1},{"version":"57ea19c2e6ba094d8087c721bac30ff1c681081dbd8b167ac068590ef633e7a5","impliedFormat":1},{"version":"cba81ec9ae7bc31a4dc56f33c054131e037649d6b9a2cfa245124c67e23e4721","impliedFormat":1},{"version":"ad193f61ba708e01218496f093c23626aa3808c296844a99189be7108a9c8343","impliedFormat":1},{"version":"a0544b3c8b70b2f319a99ea380b55ab5394ede9188cdee452a5d0ce264f258b2","impliedFormat":1},{"version":"8c654c17c334c7c168c1c36e5336896dc2c892de940886c1639bebd9fc7b9be4","impliedFormat":1},{"version":"6a4da742485d5c2eb6bcb322ae96993999ffecbd5660b0219a5f5678d8225bb0","impliedFormat":1},{"version":"c65ca21d7002bdb431f9ab3c7a6e765a489aa5196e7e0ef00aed55b1294df599","impliedFormat":1},{"version":"c8fc655c2c4bafc155ceee01c84ab3d6c03192ced5d3f2de82e20f3d1bd7f9fa","impliedFormat":1},{"version":"be5a7ff3b47f7e553565e9483bdcadb0ca2040ac9e5ec7b81c7e115a81059882","impliedFormat":1},{"version":"1a93f36ecdb60a95e3a3621b561763e2952da81962fae217ab5441ac1d77ffc5","impliedFormat":1},{"version":"2a771d907aebf9391ac1f50e4ad37952943515eeea0dcc7e78aa08f508294668","impliedFormat":1},{"version":"0146fd6262c3fd3da51cb0254bb6b9a4e42931eb2f56329edd4c199cb9aaf804","impliedFormat":1},{"version":"183f480885db5caa5a8acb833c2be04f98056bdcc5fb29e969ff86e07efe57ab","impliedFormat":99},{"version":"82e687ebd99518bc63ea04b0c3810fb6e50aa6942decd0ca6f7a56d9b9a212a6","impliedFormat":99},{"version":"7f698624bbbb060ece7c0e51b7236520ebada74b747d7523c7df376453ed6fea","impliedFormat":1},{"version":"8f07f2b6514744ac96e51d7cb8518c0f4de319471237ea10cf688b8d0e9d0225","impliedFormat":1},{"version":"257b83faa134d971c738a6b9e4c47e59bb7b23274719d92197580dd662bfafc3","impliedFormat":99},{"version":"bd0e57158bd69732d3199a1d287435d52057e1687c760ae2c21af234b2ec7672","impliedFormat":99},{"version":"556ccd493ec36c7d7cb130d51be66e147b91cc1415be383d71da0f1e49f742a9","impliedFormat":1},{"version":"b6d03c9cfe2cf0ba4c673c209fcd7c46c815b2619fd2aad59fc4229aaef2ed43","impliedFormat":1},{"version":"95aba78013d782537cc5e23868e736bec5d377b918990e28ed56110e3ae8b958","impliedFormat":1},{"version":"670a76db379b27c8ff42f1ba927828a22862e2ab0b0908e38b671f0e912cc5ed","impliedFormat":1},{"version":"13b77ab19ef7aadd86a1e54f2f08ea23a6d74e102909e3c00d31f231ed040f62","impliedFormat":1},{"version":"069bebfee29864e3955378107e243508b163e77ab10de6a5ee03ae06939f0bb9","impliedFormat":1},{"version":"26e0ffceb2198feb1ef460d5d14111c69ad07d44c5a67fd4bfeb74c969aa9afb","impliedFormat":99},"98f624bffa7ce3ac05a07bc32c5b7398ad310f381972c96b38127d1820ed0311"],"root":[194],"options":{"allowSyntheticDefaultImports":true,"composite":true,"module":99,"skipLibCheck":true,"strict":true},"referencedMap":[[194,1],[189,2],[187,3],[192,4],[188,2],[190,5],[191,2],[154,3],[100,6],[101,6],[102,7],[103,8],[104,9],[105,10],[49,3],[52,11],[50,3],[51,3],[106,12],[107,13],[108,14],[109,15],[110,16],[111,17],[112,17],[113,18],[114,19],[115,20],[116,21],[55,3],[117,22],[118,23],[119,24],[120,25],[121,3],[122,26],[123,27],[124,28],[125,29],[126,30],[127,31],[128,32],[129,33],[130,34],[131,34],[132,35],[133,3],[134,36],[135,37],[137,38],[136,39],[138,40],[139,41],[140,42],[141,43],[142,44],[143,45],[144,46],[54,47],[53,3],[153,48],[145,49],[146,50],[147,51],[148,52],[149,53],[150,54],[56,3],[57,3],[58,3],[97,55],[98,3],[99,3],[151,56],[152,57],[193,58],[59,3],[186,59],[161,3],[178,60],[176,61],[177,62],[165,63],[166,61],[173,64],[164,65],[169,66],[179,3],[170,67],[175,68],[181,69],[180,70],[163,71],[171,72],[172,73],[167,74],[174,60],[168,75],[156,76],[155,77],[162,3],[1,3],[47,3],[48,3],[9,3],[13,3],[12,3],[3,3],[14,3],[15,3],[16,3],[17,3],[18,3],[19,3],[20,3],[21,3],[4,3],[22,3],[5,3],[23,3],[27,3],[24,3],[25,3],[26,3],[28,3],[29,3],[30,3],[6,3],[31,3],[32,3],[33,3],[34,3],[7,3],[38,3],[35,3],[36,3],[37,3],[39,3],[8,3],[40,3],[45,3],[46,3],[41,3],[42,3],[43,3],[44,3],[2,3],[11,3],[10,3],[75,78],[85,79],[74,78],[95,80],[66,81],[65,82],[94,83],[88,84],[93,85],[68,86],[82,87],[67,88],[91,89],[63,90],[62,83],[92,91],[64,92],[69,93],[70,3],[73,93],[60,3],[96,94],[86,95],[77,96],[78,97],[80,98],[76,99],[79,100],[89,83],[71,101],[72,102],[81,103],[61,104],[84,95],[83,93],[87,3],[90,105],[185,106],[182,107],[160,108],[158,109],[157,3],[159,110],[183,3],[184,111]],"affectedFilesPendingEmit":[[194,17]],"emitSignatures":[194],"version":"5.6.3"} \ No newline at end of file