From e9e82c48ac102d8b483d8c18616e115266adfab4 Mon Sep 17 00:00:00 2001 From: Flacier Date: Mon, 25 May 2026 18:48:31 +0800 Subject: [PATCH 1/9] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20Firefox=20?= =?UTF-8?q?=E6=B5=8F=E8=A7=88=E5=99=A8=E5=85=BC=E5=AE=B9=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 browser-compat.ts,提供 compatStorage.session 包装层 - 运行时探测 chrome.storage.session 可用性,不可用时降级到 storage.local - .gitignore 添加 dist-firefox/ 和 *.xpi --- .gitignore | 2 ++ src/utils/browser-compat.ts | 52 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/utils/browser-compat.ts diff --git a/.gitignore b/.gitignore index e46c22a..f900e0c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ docs/public/icon.svg # Build and packaged extension output dist/ +dist-firefox/ build/ release/ releases/ @@ -16,6 +17,7 @@ artifacts/ public/injected/ *.zip *.crx +*.xpi *.pem # Test output and local reports diff --git a/src/utils/browser-compat.ts b/src/utils/browser-compat.ts new file mode 100644 index 0000000..ecf6c44 --- /dev/null +++ b/src/utils/browser-compat.ts @@ -0,0 +1,52 @@ +const SESSION_PREFIX = '__sp_session__:' + +let sessionSupported: boolean | null = null + +const checkSessionSupport = async (): Promise => { + if (sessionSupported !== null) return sessionSupported + try { + await chrome.storage.session.get('__probe__') + sessionSupported = true + } catch { + sessionSupported = false + } + return sessionSupported +} + +export const compatStorage = { + session: { + get: async (key: string): Promise> => { + if (await checkSessionSupport()) { + return chrome.storage.session.get(key) + } + const result = await chrome.storage.local.get(SESSION_PREFIX + key) + const raw = result[SESSION_PREFIX + key] + return raw ? { [key]: raw } : {} + }, + set: async (items: Record): Promise => { + if (await checkSessionSupport()) { + return chrome.storage.session.set(items) + } + const prefixed: Record = {} + for (const [key, value] of Object.entries(items)) { + prefixed[SESSION_PREFIX + key] = value + } + return chrome.storage.local.set(prefixed) + }, + remove: async (keys: string[]): Promise => { + if (await checkSessionSupport()) { + return chrome.storage.session.remove(keys) + } + return chrome.storage.local.remove(keys.map(k => SESSION_PREFIX + k)) + } + } +} + +export const clearLegacySessionKeys = async (): Promise => { + if (await checkSessionSupport()) return + const all = await chrome.storage.local.get(null) + const sessionKeys = Object.keys(all).filter(k => k.startsWith(SESSION_PREFIX)) + if (sessionKeys.length) { + await chrome.storage.local.remove(sessionKeys) + } +} From e8fc1f0832ffb64466839d4f463bc148463da313 Mon Sep 17 00:00:00 2001 From: Flacier Date: Mon, 25 May 2026 18:48:40 +0800 Subject: [PATCH 2/9] =?UTF-8?q?refactor:=20tab-store=20=E5=92=8C=20popup-c?= =?UTF-8?q?ache=20=E4=BD=BF=E7=94=A8=20compatStorage=20=E5=8C=85=E8=A3=85?= =?UTF-8?q?=20storage.session?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tab-store.ts 4 处 chrome.storage.session 调用替换为 compatStorage.session - popup-cache.ts 1 处 chrome.storage.session.set 替换为 compatStorage.session.set - index.ts onStartup 中添加 clearLegacySessionKeys 清理降级遗留数据 --- src/background/index.ts | 2 ++ src/background/popup-cache.ts | 3 ++- src/background/tab-store.ts | 10 ++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/background/index.ts b/src/background/index.ts index 8845314..80fc856 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -15,6 +15,7 @@ import { registerMessageRouter } from './message-router' import { clearBundleLicenseTimer } from './bundle-license' import { clearTabWriteLock, withTabWriteLock } from './tab-write-lock' import { isDetectablePageUrl, isObservableRequestUrl } from '@/utils/page-support' +import { clearLegacySessionKeys } from '@/utils/browser-compat' registerMessageRouter() refreshAllBadges().catch(() => {}) @@ -24,6 +25,7 @@ chrome.runtime.onInstalled.addListener(() => { }) chrome.runtime.onStartup.addListener(() => { + clearLegacySessionKeys().catch(() => {}) injectContentObserverIntoOpenTabs() }) diff --git a/src/background/popup-cache.ts b/src/background/popup-cache.ts index 5eae9f3..ae68e94 100644 --- a/src/background/popup-cache.ts +++ b/src/background/popup-cache.ts @@ -1,3 +1,4 @@ +import { compatStorage } from '@/utils/browser-compat' import { attachTechnologyLinks } from './tech-links' import { addStoredCustomHeaderRules } from './headers' import { clearBadge, clearTabSession, getPopupCache, getTabData, getTabSnapshot, popupStorageKey, storageKey } from './tab-store' @@ -500,7 +501,7 @@ export const getPopupResultResponse = async (tabId: number) => { if (legacyPopup) { nextStorage[storageKey(tabId)] = tabData } - chrome.storage.session.set(nextStorage).catch(() => {}) + compatStorage.session.set(nextStorage).catch(() => {}) } const updatedAt = getStoredUpdatedAt(data) diff --git a/src/background/tab-store.ts b/src/background/tab-store.ts index 4b614b6..65a6531 100644 --- a/src/background/tab-store.ts +++ b/src/background/tab-store.ts @@ -1,3 +1,5 @@ +import { compatStorage } from '@/utils/browser-compat' + const TAB_DATA_PREFIX = 'tab:' const POPUP_DATA_PREFIX = 'popup:' @@ -8,7 +10,7 @@ export const popupStorageKey = (tabId: number): string => `${POPUP_DATA_PREFIX}$ export const getTabData = async (tabId: number): Promise => { const key = storageKey(tabId) try { - const stored = await chrome.storage.session.get(key) + const stored = await compatStorage.session.get(key) return stored[key] || {} } catch { return {} @@ -18,7 +20,7 @@ export const getTabData = async (tabId: number): Promise => { export const getPopupCache = async (tabId: number): Promise => { const key = popupStorageKey(tabId) try { - const stored = await chrome.storage.session.get(key) + const stored = await compatStorage.session.get(key) return stored[key] || null } catch { return null @@ -26,14 +28,14 @@ export const getPopupCache = async (tabId: number): Promise => { } export const writeTabData = async (tabId: number, tabData: Record, popupRecord: any): Promise => { - await chrome.storage.session.set({ + await compatStorage.session.set({ [storageKey(tabId)]: tabData, [popupStorageKey(tabId)]: popupRecord }) } export const clearTabSession = async (tabId: number): Promise => { - await chrome.storage.session.remove([storageKey(tabId), popupStorageKey(tabId)]).catch(() => {}) + await compatStorage.session.remove([storageKey(tabId), popupStorageKey(tabId)]).catch(() => {}) } export const getTabSnapshot = async (tabId: number): Promise<{ id: number; url: string; title: string }> => { From d68378c1c2e2c80f8b6e25222261701068c92d39 Mon Sep 17 00:00:00 2001 From: Flacier Date: Mon, 25 May 2026 18:48:45 +0800 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20Firefox=20?= =?UTF-8?q?=E6=89=93=E5=8C=85=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 build-scripts/package-firefox.mjs - 复制 dist/ 并转换 manifest.json(service_worker → background.scripts,添加 browser_specific_settings.gecko) - 解决 CRXJS ES module 代码分割问题:shared chunks 包裹 IIFE 隔离作用域 - package.json 添加 build:firefox 脚本 --- build-scripts/package-firefox.mjs | 146 ++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 147 insertions(+) create mode 100644 build-scripts/package-firefox.mjs diff --git a/build-scripts/package-firefox.mjs b/build-scripts/package-firefox.mjs new file mode 100644 index 0000000..fa80816 --- /dev/null +++ b/build-scripts/package-firefox.mjs @@ -0,0 +1,146 @@ +import { cpSync, rmSync, readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs' +import { resolve, dirname, basename } from 'node:path' +import { execFileSync } from 'node:child_process' + +const root = resolve(import.meta.dirname, '..') +const distDir = resolve(root, 'dist') +const firefoxDir = resolve(root, 'dist-firefox') + +if (!existsSync(distDir)) { + console.error('[package-firefox] dist/ not found, run `pnpm build` first') + process.exit(1) +} + +rmSync(firefoxDir, { recursive: true, force: true }) +cpSync(distDir, firefoxDir, { recursive: true }) + +// --- Inline ES module chunks into a single background script --- +// CRXJS outputs background as ES modules with code-split shared chunks. +// Firefox background scripts don't support ES modules, so we: +// 1. Wrap each shared chunk in an IIFE to isolate scope (prevents variable name collisions) +// 2. Store each chunk's exports in a per-chunk namespace +// 3. Replace the entry chunk's imports with variable declarations from those namespaces + +const parseAllImportBindings = (code) => { + const re = /import\{([^}]*)\}from"([^"]*)"/g + const imports = [] + let match + while ((match = re.exec(code)) !== null) { + const bindings = match[1].split(',').map(s => { + const parts = s.trim().split(/\s+as\s+/) + return { exported: parts[0].trim(), local: (parts[1] || parts[0]).trim() } + }) + imports.push({ path: match[2], bindings }) + } + return imports +} + +const parseExportBindings = (code) => { + const match = code.match(/export\{([^}]*)\};?\s*$/) + if (!match) return [] + return match[1].split(',').map(s => { + const parts = s.trim().split(/\s+as\s+/) + return { local: parts[0].trim(), exported: (parts[1] || parts[0]).trim() } + }) +} + +const stripModuleSyntax = (code) => + code.replace(/import\{[^}]*\}from"[^"]*";?/g, '').replace(/export\{[^}]*\};?/g, '') + +const resolveImports = (filePath) => { + const code = readFileSync(filePath, 'utf8') + const dir = dirname(filePath) + const importRe = /import\{[^}]*\}from"(\.\/[^"]+)"/g + const deps = [] + let match + while ((match = importRe.exec(code)) !== null) { + deps.push(resolve(dir, match[1])) + } + return { code, deps } +} + +const topologicalSort = (entryPath) => { + const visited = new Set() + const order = [] + const visit = (filePath) => { + if (visited.has(filePath)) return + visited.add(filePath) + const { deps } = resolveImports(filePath) + for (const dep of deps) visit(dep) + order.push(filePath) + } + visit(entryPath) + return order +} + +// Read the loader to find the entry chunk +const loaderPath = resolve(firefoxDir, 'service-worker-loader.js') +const loaderCode = readFileSync(loaderPath, 'utf8') +const entryMatch = loaderCode.match(/import\s+'\.\/(assets\/[^']+)'/) +if (!entryMatch) { + console.error('[package-firefox] could not resolve service-worker-loader entry') + process.exit(1) +} + +const entryPath = resolve(firefoxDir, entryMatch[1]) +const chunkOrder = topologicalSort(entryPath) +const sharedChunks = chunkOrder.slice(0, -1) +const entryChunkPath = chunkOrder[chunkOrder.length - 1] + +const parts = [`var __chunks = {};`] + +for (const filePath of sharedChunks) { + const code = readFileSync(filePath, 'utf8') + const chunkId = basename(filePath, '.js') + const exports = parseExportBindings(code) + const stripped = stripModuleSyntax(code) + const exportAssignments = exports + .map(e => `__chunks["${chunkId}"].${e.exported} = ${e.local};`) + .join(' ') + parts.push(`(function() { ${stripped} __chunks["${chunkId}"] = {}; ${exportAssignments} })();`) +} + +const entryCode = readFileSync(entryChunkPath, 'utf8') +const entryImports = parseAllImportBindings(entryCode) +let entryBody = stripModuleSyntax(entryCode) +if (entryImports.length) { + const varDecls = entryImports.flatMap(imp => { + const chunkId = basename(resolve(dirname(entryChunkPath), imp.path), '.js') + return imp.bindings.map(b => `var ${b.local} = __chunks["${chunkId}"].${b.exported};`) + }).join(' ') + entryBody = varDecls + '\n' + entryBody +} +parts.push(entryBody) + +const backgroundPath = resolve(firefoxDir, 'background.js') +writeFileSync(backgroundPath, parts.join('\n')) +console.log(`[package-firefox] inlined ${chunkOrder.length} chunks into background.js`) + +// --- Transform manifest.json --- + +const manifestPath = resolve(firefoxDir, 'manifest.json') +const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) + +if (manifest.background?.service_worker) { + manifest.background = { scripts: ['background.js'] } +} + +manifest.browser_specific_settings = { + gecko: { + id: 'stackprism@stackprism.dev', + strict_min_version: '128.0' + } +} + +writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)) +console.log('[package-firefox] manifest.json transformed') + +// --- Package .xpi --- + +const releaseDir = resolve(root, 'release') +if (!existsSync(releaseDir)) mkdirSync(releaseDir) + +const version = manifest.version +const xpiName = `stackprism-v${version}.xpi` +execFileSync('zip', ['-r', resolve(releaseDir, xpiName), '.'], { cwd: firefoxDir, stdio: 'inherit' }) +console.log(`[package-firefox] created release/${xpiName}`) diff --git a/package.json b/package.json index aaec668..68b8d98 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "eslint src", "build:injected": "node build-scripts/build-injected.mjs", "build": "pnpm run build:injected && vite build", + "build:firefox": "pnpm run build && node build-scripts/package-firefox.mjs", "test:unit": "node --test tests/*.test.mjs", "check:links": "node build-scripts/check-tech-links.mjs", "extract:icons": "node build-scripts/extract-wappalyzer-icons.mjs", From bc4c218f4a662e99c1eb9ee79b481c35b27deeea Mon Sep 17 00:00:00 2001 From: Flacier Date: Mon, 25 May 2026 18:48:51 +0800 Subject: [PATCH 4/9] =?UTF-8?q?ci:=20release=20=E5=B7=A5=E4=BD=9C=E6=B5=81?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20Firefox=20.xpi=20=E6=89=93=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - meta 步骤输出 xpi_name - 打包 zip 后新增 Firefox .xpi 打包步骤 - 上传产物包含 .xpi 和 .sha256 --- .github/workflows/release-extension.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/release-extension.yml b/.github/workflows/release-extension.yml index e19818a..73a1628 100644 --- a/.github/workflows/release-extension.yml +++ b/.github/workflows/release-extension.yml @@ -66,6 +66,7 @@ jobs: echo "tag=$tag" >> "$GITHUB_OUTPUT" echo "zip_name=stackprism-v${version}.zip" >> "$GITHUB_OUTPUT" echo "crx_name=stackprism-v${version}.crx" >> "$GITHUB_OUTPUT" + echo "xpi_name=stackprism-v${version}.xpi" >> "$GITHUB_OUTPUT" - name: 打包 zip shell: bash @@ -78,6 +79,13 @@ jobs: ) sha256sum "release/${{ steps.meta.outputs.zip_name }}" > "release/${{ steps.meta.outputs.zip_name }}.sha256" + - name: 打包 Firefox .xpi + shell: bash + run: | + set -euo pipefail + node build-scripts/package-firefox.mjs + sha256sum "release/${{ steps.meta.outputs.xpi_name }}" > "release/${{ steps.meta.outputs.xpi_name }}.sha256" + - name: 签名 crx id: crx shell: bash @@ -105,6 +113,8 @@ jobs: assets=( "release/${{ steps.meta.outputs.zip_name }}" "release/${{ steps.meta.outputs.zip_name }}.sha256" + "release/${{ steps.meta.outputs.xpi_name }}" + "release/${{ steps.meta.outputs.xpi_name }}.sha256" ) if [ -f "release/${{ steps.meta.outputs.crx_name }}" ]; then assets+=( From d4fb5fbf7419b3037c2e329555281686831df259 Mon Sep 17 00:00:00 2001 From: Flacier Date: Mon, 25 May 2026 19:00:44 +0800 Subject: [PATCH 5/9] =?UTF-8?q?fix:=20executeScript=20MAIN=20world=20?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E5=85=BC=E5=AE=B9=20Firefox=20Promise=20?= =?UTF-8?q?=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Firefox 的 chrome.scripting.executeScript 可能返回原始 Promise 而非自动 await 后的 resolved 值,导致页面检测结果为空。 --- src/background/detection.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/background/detection.ts b/src/background/detection.ts index c624509..9741f06 100644 --- a/src/background/detection.ts +++ b/src/background/detection.ts @@ -124,7 +124,9 @@ export const runActivePageDetection = async (tabId: number, options: { force?: b world: 'MAIN', files: ['injected/page-detector.iife.js'] }) - const page = injection?.[0]?.result + const rawResult = injection?.[0]?.result + // Firefox may return the raw Promise instead of awaiting it + const page = rawResult && typeof rawResult.then === 'function' ? await rawResult : rawResult if (!page) return const augmentedPage = await augmentPageWithWordPressThemeStyles(page) From 4e08d94ce3b3fc6c937d466719253f808a8baf26 Mon Sep 17 00:00:00 2001 From: Flacier Date: Tue, 26 May 2026 23:23:27 +0800 Subject: [PATCH 6/9] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20falsy=20?= =?UTF-8?q?=E5=80=BC=E4=B8=A2=E5=A4=B1=E4=B8=8E=20Node=2020=20=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - browser-compat: storage.local 降级 get 改用 hasOwnProperty 检查,避免 0/false/'' 等 falsy 值被丢弃 - package-firefox: import.meta.dirname 替换为 fileURLToPath,兼容 Node 20 CI 环境 - package-firefox: import/export 解析增加守卫,遇到不支持的模块语法时抛出明确错误 --- build-scripts/package-firefox.mjs | 13 +++++++++++-- src/utils/browser-compat.ts | 8 +++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/build-scripts/package-firefox.mjs b/build-scripts/package-firefox.mjs index fa80816..82f4a69 100644 --- a/build-scripts/package-firefox.mjs +++ b/build-scripts/package-firefox.mjs @@ -1,8 +1,9 @@ import { cpSync, rmSync, readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs' import { resolve, dirname, basename } from 'node:path' import { execFileSync } from 'node:child_process' +import { fileURLToPath } from 'node:url' -const root = resolve(import.meta.dirname, '..') +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..') const distDir = resolve(root, 'dist') const firefoxDir = resolve(root, 'dist-firefox') @@ -32,12 +33,20 @@ const parseAllImportBindings = (code) => { }) imports.push({ path: match[2], bindings }) } + if (!imports.length && /import\s/.test(code)) { + throw new Error('[package-firefox] unsupported import syntax in entry chunk — cannot inline') + } return imports } const parseExportBindings = (code) => { const match = code.match(/export\{([^}]*)\};?\s*$/) - if (!match) return [] + if (!match) { + if (/export\s/.test(code)) { + throw new Error('[package-firefox] unsupported export syntax in shared chunk — cannot inline') + } + return [] + } return match[1].split(',').map(s => { const parts = s.trim().split(/\s+as\s+/) return { local: parts[0].trim(), exported: (parts[1] || parts[0]).trim() } diff --git a/src/utils/browser-compat.ts b/src/utils/browser-compat.ts index ecf6c44..567335a 100644 --- a/src/utils/browser-compat.ts +++ b/src/utils/browser-compat.ts @@ -19,9 +19,11 @@ export const compatStorage = { if (await checkSessionSupport()) { return chrome.storage.session.get(key) } - const result = await chrome.storage.local.get(SESSION_PREFIX + key) - const raw = result[SESSION_PREFIX + key] - return raw ? { [key]: raw } : {} + const storageKey = SESSION_PREFIX + key + const result = await chrome.storage.local.get(storageKey) + return Object.prototype.hasOwnProperty.call(result, storageKey) + ? { [key]: result[storageKey] } + : {} }, set: async (items: Record): Promise => { if (await checkSessionSupport()) { From 1e8b5f48f9d16d8c6893be3620a369eae86440c6 Mon Sep 17 00:00:00 2001 From: Flacier Date: Wed, 27 May 2026 11:58:18 +0800 Subject: [PATCH 7/9] =?UTF-8?q?refactor:=20Firefox=20=E6=89=93=E5=8C=85?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E6=94=B9=E7=94=A8=20esbuild=20=E5=92=8C=20ar?= =?UTF-8?q?chiver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 用 esbuild bundle + format:'iife' 替代手写 ESM 解析器,不再依赖 minifier 输出格式 - 用 archiver 替代系统 zip 命令,兼容 Windows 构建环境 - gecko.id 修正为 stackprism@setube.github.io --- build-scripts/package-firefox.mjs | 135 ++----- package.json | 2 + pnpm-lock.yaml | 602 +++++++++++++++++++++++++++++- 3 files changed, 618 insertions(+), 121 deletions(-) diff --git a/build-scripts/package-firefox.mjs b/build-scripts/package-firefox.mjs index 82f4a69..99a58ef 100644 --- a/build-scripts/package-firefox.mjs +++ b/build-scripts/package-firefox.mjs @@ -1,7 +1,11 @@ -import { cpSync, rmSync, readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs' -import { resolve, dirname, basename } from 'node:path' -import { execFileSync } from 'node:child_process' +import { cpSync, rmSync, readFileSync, writeFileSync, mkdirSync, existsSync, createWriteStream } from 'node:fs' +import { resolve, dirname } from 'node:path' import { fileURLToPath } from 'node:url' +import { createRequire } from 'node:module' +import archiver from 'archiver' + +const require = createRequire(import.meta.url) +const esbuild = require('esbuild') const root = resolve(dirname(fileURLToPath(import.meta.url)), '..') const distDir = resolve(root, 'dist') @@ -15,74 +19,11 @@ if (!existsSync(distDir)) { rmSync(firefoxDir, { recursive: true, force: true }) cpSync(distDir, firefoxDir, { recursive: true }) -// --- Inline ES module chunks into a single background script --- +// --- Bundle background script as IIFE --- // CRXJS outputs background as ES modules with code-split shared chunks. -// Firefox background scripts don't support ES modules, so we: -// 1. Wrap each shared chunk in an IIFE to isolate scope (prevents variable name collisions) -// 2. Store each chunk's exports in a per-chunk namespace -// 3. Replace the entry chunk's imports with variable declarations from those namespaces - -const parseAllImportBindings = (code) => { - const re = /import\{([^}]*)\}from"([^"]*)"/g - const imports = [] - let match - while ((match = re.exec(code)) !== null) { - const bindings = match[1].split(',').map(s => { - const parts = s.trim().split(/\s+as\s+/) - return { exported: parts[0].trim(), local: (parts[1] || parts[0]).trim() } - }) - imports.push({ path: match[2], bindings }) - } - if (!imports.length && /import\s/.test(code)) { - throw new Error('[package-firefox] unsupported import syntax in entry chunk — cannot inline') - } - return imports -} - -const parseExportBindings = (code) => { - const match = code.match(/export\{([^}]*)\};?\s*$/) - if (!match) { - if (/export\s/.test(code)) { - throw new Error('[package-firefox] unsupported export syntax in shared chunk — cannot inline') - } - return [] - } - return match[1].split(',').map(s => { - const parts = s.trim().split(/\s+as\s+/) - return { local: parts[0].trim(), exported: (parts[1] || parts[0]).trim() } - }) -} - -const stripModuleSyntax = (code) => - code.replace(/import\{[^}]*\}from"[^"]*";?/g, '').replace(/export\{[^}]*\};?/g, '') - -const resolveImports = (filePath) => { - const code = readFileSync(filePath, 'utf8') - const dir = dirname(filePath) - const importRe = /import\{[^}]*\}from"(\.\/[^"]+)"/g - const deps = [] - let match - while ((match = importRe.exec(code)) !== null) { - deps.push(resolve(dir, match[1])) - } - return { code, deps } -} +// Firefox background scripts don't support ES modules, so we rebundle +// the entry point into a single IIFE via esbuild. -const topologicalSort = (entryPath) => { - const visited = new Set() - const order = [] - const visit = (filePath) => { - if (visited.has(filePath)) return - visited.add(filePath) - const { deps } = resolveImports(filePath) - for (const dep of deps) visit(dep) - order.push(filePath) - } - visit(entryPath) - return order -} - -// Read the loader to find the entry chunk const loaderPath = resolve(firefoxDir, 'service-worker-loader.js') const loaderCode = readFileSync(loaderPath, 'utf8') const entryMatch = loaderCode.match(/import\s+'\.\/(assets\/[^']+)'/) @@ -92,38 +33,19 @@ if (!entryMatch) { } const entryPath = resolve(firefoxDir, entryMatch[1]) -const chunkOrder = topologicalSort(entryPath) -const sharedChunks = chunkOrder.slice(0, -1) -const entryChunkPath = chunkOrder[chunkOrder.length - 1] - -const parts = [`var __chunks = {};`] - -for (const filePath of sharedChunks) { - const code = readFileSync(filePath, 'utf8') - const chunkId = basename(filePath, '.js') - const exports = parseExportBindings(code) - const stripped = stripModuleSyntax(code) - const exportAssignments = exports - .map(e => `__chunks["${chunkId}"].${e.exported} = ${e.local};`) - .join(' ') - parts.push(`(function() { ${stripped} __chunks["${chunkId}"] = {}; ${exportAssignments} })();`) -} +const backgroundPath = resolve(firefoxDir, 'background.js') -const entryCode = readFileSync(entryChunkPath, 'utf8') -const entryImports = parseAllImportBindings(entryCode) -let entryBody = stripModuleSyntax(entryCode) -if (entryImports.length) { - const varDecls = entryImports.flatMap(imp => { - const chunkId = basename(resolve(dirname(entryChunkPath), imp.path), '.js') - return imp.bindings.map(b => `var ${b.local} = __chunks["${chunkId}"].${b.exported};`) - }).join(' ') - entryBody = varDecls + '\n' + entryBody -} -parts.push(entryBody) +await esbuild.build({ + entryPoints: [entryPath], + bundle: true, + format: 'iife', + outfile: backgroundPath, + target: 'es2022', + platform: 'browser', + logLevel: 'warning' +}) -const backgroundPath = resolve(firefoxDir, 'background.js') -writeFileSync(backgroundPath, parts.join('\n')) -console.log(`[package-firefox] inlined ${chunkOrder.length} chunks into background.js`) +console.log('[package-firefox] bundled background.js as IIFE') // --- Transform manifest.json --- @@ -136,7 +58,7 @@ if (manifest.background?.service_worker) { manifest.browser_specific_settings = { gecko: { - id: 'stackprism@stackprism.dev', + id: 'stackprism@setube.github.io', strict_min_version: '128.0' } } @@ -151,5 +73,16 @@ if (!existsSync(releaseDir)) mkdirSync(releaseDir) const version = manifest.version const xpiName = `stackprism-v${version}.xpi` -execFileSync('zip', ['-r', resolve(releaseDir, xpiName), '.'], { cwd: firefoxDir, stdio: 'inherit' }) +const xpiPath = resolve(releaseDir, xpiName) + +await new Promise((resolve, reject) => { + const output = createWriteStream(xpiPath) + const archive = archiver('zip', { zlib: { level: 9 } }) + output.on('close', resolve) + archive.on('error', reject) + archive.pipe(output) + archive.glob('**', { cwd: firefoxDir, dot: true }) + archive.finalize() +}) + console.log(`[package-firefox] created release/${xpiName}`) diff --git a/package.json b/package.json index 68b8d98..dd668d7 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,8 @@ "@types/node": "^20.12.7", "@vitejs/plugin-vue": "^5.0.4", "@vue/eslint-config-typescript": "^14.7.0", + "archiver": "^7.0.1", + "esbuild": "^0.21.5", "eslint": "^10.3.0", "eslint-plugin-vue": "^10.9.1", "lucide-vue-next": "^1.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 839dcc7..5019f0d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,12 @@ importers: '@vue/eslint-config-typescript': specifier: ^14.7.0 version: 14.7.0(eslint-plugin-vue@10.9.1(@typescript-eslint/parser@8.59.2(eslint@10.3.0)(typescript@5.4.5))(eslint@10.3.0)(vue-eslint-parser@10.4.0(eslint@10.3.0)))(eslint@10.3.0)(typescript@5.4.5) + archiver: + specifier: ^7.0.1 + version: 7.0.1 + esbuild: + specifier: ^0.21.5 + version: 0.21.5 eslint: specifier: ^10.3.0 version: 10.3.0 @@ -198,12 +204,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - '@esbuild/android-x64@0.21.5': resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} @@ -252,12 +252,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-mips64el@0.21.5': resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} @@ -380,6 +374,10 @@ packages: '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -483,6 +481,10 @@ packages: resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} engines: {node: '>= 10.0.0'} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@rollup/pluginutils@4.2.1': resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} engines: {node: '>= 8.0.0'} @@ -885,6 +887,10 @@ packages: '@webcomponents/custom-elements@1.6.0': resolution: {integrity: sha512-CqTpxOlUCPWRNUPZDxT5v2NnHXA4oox612iUGnmTUGQFhZ1Gkj8kirtl/2wcF6MqX7+PqqicZzOCBKKfIn0dww==} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -909,6 +915,41 @@ packages: alien-signals@1.0.13: resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + archiver-utils@5.0.2: + resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} + engines: {node: '>= 14'} + + archiver@7.0.1: + resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} + engines: {node: '>= 14'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + b4a@1.8.1: + resolution: {integrity: sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==} + peerDependencies: + react-native-b4a: '*' + peerDependenciesMeta: + react-native-b4a: + optional: true + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -916,6 +957,50 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + bare-events@2.8.3: + resolution: {integrity: sha512-HdUm8EMQBLaJvGUdidNNbqpA1kYkwNcb+MYxkxCLAPJGQzlv9J0C24h8V65Z4c5GLd/JEALDvpFCQgpLJqc0zw==} + peerDependencies: + bare-abort-controller: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + + bare-fs@4.7.1: + resolution: {integrity: sha512-WDRsyVN52eAx/lBamKD6uyw8H4228h/x0sGGGegOamM2cd7Pag88GfMQalobXI+HaEUxpCkbKQUDOQqt9wawRw==} + engines: {bare: '>=1.16.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + + bare-os@3.9.1: + resolution: {integrity: sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ==} + engines: {bare: '>=1.14.0'} + + bare-path@3.0.0: + resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} + + bare-stream@2.13.1: + resolution: {integrity: sha512-Vp0cnjYyrEC4whYTymQ+YZi6pBpfiICZO3cfRG8sy67ZNWe951urv1x4eW1BKNngw3U+3fPYb5JQvHbCtxH7Ow==} + peerDependencies: + bare-abort-controller: '*' + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + bare-buffer: + optional: true + bare-events: + optional: true + + bare-url@2.4.3: + resolution: {integrity: sha512-Kccpc7ACfXaxfeInfqKcZtW4pT5YBn1mesc4sCsun6sRwtbJ4h+sNOaksUpYEJUKfN65YWC6Bw2OJEFiKxq8nQ==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + birpc@2.9.0: resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} @@ -933,6 +1018,13 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + buffer-crc32@1.0.0: + resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} + engines: {node: '>=8.0.0'} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -946,12 +1038,23 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + colorjs.io@0.5.2: resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==} comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + compress-commons@6.0.2: + resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} + engines: {node: '>= 14'} + convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -959,6 +1062,18 @@ packages: resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} engines: {node: '>=18'} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@6.0.0: + resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} + engines: {node: '>= 14'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1017,9 +1132,18 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + emoji-regex-xs@1.0.0: resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -1099,9 +1223,23 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -1146,6 +1284,10 @@ packages: focus-trap@7.8.0: resolution: {integrity: sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -1163,6 +1305,11 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} @@ -1189,6 +1336,9 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1204,10 +1354,17 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1216,13 +1373,23 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + is-what@5.5.0: resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} engines: {node: '>=18'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -1243,6 +1410,10 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -1251,8 +1422,15 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lucide-vue-next@1.0.0: resolution: {integrity: sha512-V6SPvx1IHTj/UY+FrIYWV5faISsPSb8BnWSFDxAtezWKvWc9ZZ40PDrdu1/Qb5vg4lHWr1hs1BAMGVGm6V1Xdg==} + deprecated: Package deprecated. Please use @lucide/vue instead. peerDependencies: vue: '>=3.0.1' @@ -1292,10 +1470,18 @@ packages: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + minimatch@9.0.9: resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + minisearch@7.2.0: resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==} @@ -1322,6 +1508,10 @@ packages: node-html-parser@7.1.0: resolution: {integrity: sha512-iJo8b2uYGT40Y8BTyy5ufL6IVbN8rbm/1QK2xffXU/1a/v3AAa0d1YAoqBNYqaS4R/HajkWIpIfdE6KcyFh1AQ==} + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -1340,6 +1530,9 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -1351,6 +1544,10 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -1388,6 +1585,13 @@ packages: engines: {node: '>=14'} hasBin: true + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} @@ -1402,6 +1606,16 @@ packages: resolution: {integrity: sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==} engines: {node: '>=0.10.0'} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -1438,6 +1652,12 @@ packages: rxjs@7.5.7: resolution: {integrity: sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + sass-embedded-all-unknown@1.99.0: resolution: {integrity: sha512-qPIRG8Uhjo6/OKyAKixTnwMliTz+t9K6Duk0mx5z+K7n0Ts38NSJz2sjDnc7cA/8V9Lb3q09H38dZ1CLwD+ssw==} cpu: ['!arm', '!arm64', '!riscv64', '!x64'] @@ -1579,6 +1799,10 @@ packages: shiki@2.5.0: resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1590,9 +1814,34 @@ packages: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} + streamx@2.25.0: + resolution: {integrity: sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + superjson@2.2.6: resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} engines: {node: '>=16'} @@ -1612,6 +1861,15 @@ packages: tabbable@6.4.0: resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + tar-stream@3.2.0: + resolution: {integrity: sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==} + + teex@1.0.1: + resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} + + text-decoder@1.2.7: + resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} + tinyglobby@0.2.16: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} @@ -1778,6 +2036,14 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} @@ -1786,6 +2052,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zip-stream@6.0.1: + resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} + engines: {node: '>= 14'} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -1970,9 +2240,6 @@ snapshots: '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm@0.21.5': - optional: true - '@esbuild/android-x64@0.21.5': optional: true @@ -1997,9 +2264,6 @@ snapshots: '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-loong64@0.21.5': - optional: true - '@esbuild/linux-mips64el@0.21.5': optional: true @@ -2085,6 +2349,15 @@ snapshots: '@iconify/types@2.0.0': {} + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jridgewell/sourcemap-codec@1.5.5': {} '@nodelib/fs.scandir@2.1.5': @@ -2160,6 +2433,9 @@ snapshots: '@parcel/watcher-win32-x64': 2.5.6 optional: true + '@pkgjs/parseargs@0.11.0': + optional: true + '@rollup/pluginutils@4.2.1': dependencies: estree-walker: 2.0.2 @@ -2566,6 +2842,10 @@ snapshots: '@webcomponents/custom-elements@1.6.0': {} + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -2602,10 +2882,82 @@ snapshots: alien-signals@1.0.13: {} + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + archiver-utils@5.0.2: + dependencies: + glob: 10.5.0 + graceful-fs: 4.2.11 + is-stream: 2.0.1 + lazystream: 1.0.1 + lodash: 4.18.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + archiver@7.0.1: + dependencies: + archiver-utils: 5.0.2 + async: 3.2.6 + buffer-crc32: 1.0.0 + readable-stream: 4.7.0 + readdir-glob: 1.1.3 + tar-stream: 3.2.0 + zip-stream: 6.0.1 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + async@3.2.6: {} + + b4a@1.8.1: {} + balanced-match@1.0.2: {} balanced-match@4.0.4: {} + bare-events@2.8.3: {} + + bare-fs@4.7.1: + dependencies: + bare-events: 2.8.3 + bare-path: 3.0.0 + bare-stream: 2.13.1(bare-events@2.8.3) + bare-url: 2.4.3 + fast-fifo: 1.3.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + bare-os@3.9.1: {} + + bare-path@3.0.0: + dependencies: + bare-os: 3.9.1 + + bare-stream@2.13.1(bare-events@2.8.3): + dependencies: + streamx: 2.25.0 + teex: 1.0.1 + optionalDependencies: + bare-events: 2.8.3 + transitivePeerDependencies: + - react-native-b4a + + bare-url@2.4.3: + dependencies: + bare-path: 3.0.0 + + base64-js@1.5.1: {} + birpc@2.9.0: {} boolbase@1.0.0: {} @@ -2622,6 +2974,13 @@ snapshots: dependencies: fill-range: 7.1.1 + buffer-crc32@1.0.0: {} + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + ccount@2.0.1: {} character-entities-html4@2.1.0: {} @@ -2633,16 +2992,39 @@ snapshots: readdirp: 4.1.2 optional: true + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + colorjs.io@0.5.2: {} comma-separated-tokens@2.0.3: {} + compress-commons@6.0.2: + dependencies: + crc-32: 1.2.2 + crc32-stream: 6.0.0 + is-stream: 2.0.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + convert-source-map@1.9.0: {} copy-anything@4.0.5: dependencies: is-what: 5.5.0 + core-util-is@1.0.3: {} + + crc-32@1.2.2: {} + + crc32-stream@6.0.0: + dependencies: + crc-32: 1.2.2 + readable-stream: 4.7.0 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -2698,8 +3080,14 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 + eastasianwidth@0.2.0: {} + emoji-regex-xs@1.0.0: {} + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + entities@4.5.0: {} entities@7.0.1: {} @@ -2709,7 +3097,6 @@ snapshots: esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 '@esbuild/android-arm64': 0.21.5 '@esbuild/android-x64': 0.21.5 '@esbuild/darwin-arm64': 0.21.5 @@ -2719,7 +3106,6 @@ snapshots: '@esbuild/linux-arm': 0.21.5 '@esbuild/linux-arm64': 0.21.5 '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 '@esbuild/linux-mips64el': 0.21.5 '@esbuild/linux-ppc64': 0.21.5 '@esbuild/linux-riscv64': 0.21.5 @@ -2813,8 +3199,20 @@ snapshots: esutils@2.0.3: {} + event-target-shim@5.0.1: {} + + events-universal@1.0.1: + dependencies: + bare-events: 2.8.3 + transitivePeerDependencies: + - bare-abort-controller + + events@3.3.0: {} + fast-deep-equal@3.1.3: {} + fast-fifo@1.3.2: {} + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2859,6 +3257,11 @@ snapshots: dependencies: tabbable: 6.4.0 + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 @@ -2876,6 +3279,15 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + globrex@0.1.2: {} graceful-fs@4.2.11: {} @@ -2906,6 +3318,8 @@ snapshots: html-void-elements@3.0.0: {} + ieee754@1.2.1: {} + ignore@5.3.2: {} ignore@7.0.5: {} @@ -2914,18 +3328,32 @@ snapshots: imurmurhash@0.1.4: {} + inherits@2.0.4: {} + is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: {} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 is-number@7.0.0: {} + is-stream@2.0.1: {} + is-what@5.5.0: {} + isarray@1.0.0: {} + isexe@2.0.0: {} + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -2944,6 +3372,10 @@ snapshots: dependencies: json-buffer: 3.0.1 + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -2953,6 +3385,10 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash@4.18.1: {} + + lru-cache@10.4.3: {} + lucide-vue-next@1.0.0(vue@3.5.34(typescript@5.4.5)): dependencies: vue: 3.5.34(typescript@5.4.5) @@ -3003,10 +3439,16 @@ snapshots: dependencies: brace-expansion: 5.0.6 + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.0 + minimatch@9.0.9: dependencies: brace-expansion: 2.1.0 + minipass@7.1.3: {} + minisearch@7.2.0: {} mitt@3.0.1: {} @@ -3027,6 +3469,8 @@ snapshots: css-select: 5.2.2 he: 1.2.0 + normalize-path@3.0.0: {} + nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -3054,12 +3498,19 @@ snapshots: dependencies: p-limit: 3.1.0 + package-json-from-dist@1.0.1: {} + path-browserify@1.0.1: {} path-exists@4.0.0: {} path-key@3.1.1: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + pathe@2.0.3: {} perfect-debounce@1.0.0: {} @@ -3087,6 +3538,10 @@ snapshots: prettier@3.8.3: {} + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + property-information@7.1.0: {} punycode@2.3.1: {} @@ -3095,6 +3550,28 @@ snapshots: react-refresh@0.13.0: {} + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.9 + readdirp@4.1.2: optional: true @@ -3155,6 +3632,10 @@ snapshots: dependencies: tslib: 2.8.1 + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + sass-embedded-all-unknown@1.99.0: dependencies: sass: 1.99.0 @@ -3272,17 +3753,56 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 + signal-exit@4.1.0: {} + source-map-js@1.2.1: {} space-separated-tokens@2.0.2: {} speakingurl@14.0.1: {} + streamx@2.25.0: + dependencies: + events-universal: 1.0.1 + fast-fifo: 1.3.2 + text-decoder: 1.2.7 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + superjson@2.2.6: dependencies: copy-anything: 4.0.5 @@ -3299,6 +3819,30 @@ snapshots: tabbable@6.4.0: {} + tar-stream@3.2.0: + dependencies: + b4a: 1.8.1 + bare-fs: 4.7.1 + fast-fifo: 1.3.2 + streamx: 2.25.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + teex@1.0.1: + dependencies: + streamx: 2.25.0 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + text-decoder@1.2.7: + dependencies: + b4a: 1.8.1 + transitivePeerDependencies: + - react-native-b4a + tinyglobby@0.2.16: dependencies: fdir: 6.5.0(picomatch@4.0.4) @@ -3489,8 +4033,26 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + xml-name-validator@4.0.0: {} yocto-queue@0.1.0: {} + zip-stream@6.0.1: + dependencies: + archiver-utils: 5.0.2 + compress-commons: 6.0.2 + readable-stream: 4.7.0 + zwitch@2.0.4: {} From c4f4a318202c15f4e16fc124c2c962707113174d Mon Sep 17 00:00:00 2001 From: Flacier Date: Wed, 27 May 2026 11:58:43 +0800 Subject: [PATCH 8/9] =?UTF-8?q?fix:=20=E7=AE=80=E5=8C=96=20executeScript?= =?UTF-8?q?=20=E7=BB=93=E6=9E=9C=E7=9A=84=20Promise=20=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit await 对非 Promise 值是 no-op,无需额外的 typeof then 守卫 --- src/background/detection.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/background/detection.ts b/src/background/detection.ts index 9741f06..dc14d51 100644 --- a/src/background/detection.ts +++ b/src/background/detection.ts @@ -124,9 +124,7 @@ export const runActivePageDetection = async (tabId: number, options: { force?: b world: 'MAIN', files: ['injected/page-detector.iife.js'] }) - const rawResult = injection?.[0]?.result - // Firefox may return the raw Promise instead of awaiting it - const page = rawResult && typeof rawResult.then === 'function' ? await rawResult : rawResult + const page = await injection?.[0]?.result if (!page) return const augmentedPage = await augmentPageWithWordPressThemeStyles(page) From 8530dbfbc7db48726bce246900b78ddf8929df42 Mon Sep 17 00:00:00 2001 From: Flacier Date: Wed, 27 May 2026 20:42:58 +0800 Subject: [PATCH 9/9] =?UTF-8?q?fix:=20clearLegacySessionKeys=20=E6=8C=82?= =?UTF-8?q?=E8=BD=BD=20onInstalled=20=E5=B9=B6=E6=B8=85=E7=90=86=E6=89=93?= =?UTF-8?q?=E5=8C=85=E8=84=9A=E6=9C=AC=E7=BB=86=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - onInstalled 回调中也调用 clearLegacySessionKeys,覆盖扩展更新场景 - package-firefox.mjs: 修复 resolve 变量遮蔽 path.resolve - package-firefox.mjs: archive.glob 排除 .DS_Store 等隐藏文件 --- build-scripts/package-firefox.mjs | 4 ++-- src/background/index.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build-scripts/package-firefox.mjs b/build-scripts/package-firefox.mjs index 99a58ef..e77c62a 100644 --- a/build-scripts/package-firefox.mjs +++ b/build-scripts/package-firefox.mjs @@ -75,10 +75,10 @@ const version = manifest.version const xpiName = `stackprism-v${version}.xpi` const xpiPath = resolve(releaseDir, xpiName) -await new Promise((resolve, reject) => { +await new Promise((ok, reject) => { const output = createWriteStream(xpiPath) const archive = archiver('zip', { zlib: { level: 9 } }) - output.on('close', resolve) + output.on('close', ok) archive.on('error', reject) archive.pipe(output) archive.glob('**', { cwd: firefoxDir, dot: true }) diff --git a/src/background/index.ts b/src/background/index.ts index 80fc856..87e7acf 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -21,6 +21,7 @@ registerMessageRouter() refreshAllBadges().catch(() => {}) chrome.runtime.onInstalled.addListener(() => { + clearLegacySessionKeys().catch(() => {}) injectContentObserverIntoOpenTabs() })