diff --git a/WIN7_FIX.md b/WIN7_FIX.md
new file mode 100644
index 00000000..07c5e86f
--- /dev/null
+++ b/WIN7_FIX.md
@@ -0,0 +1,165 @@
+# Windows 7 兼容性说明与替代构建方案
+
+在 Windows 7 上直接执行 `pnpm build` 会因原生打包工具崩溃而失败(`0xc0000005` 访问违例)。
+本文说明根因,并提供一套**完全不加载原生二进制**的替代构建方案:`build-win7.mjs`。
+
+---
+
+## 1. 根因:原生工具链依赖 Win8+ 系统 API
+
+本仓库的正常构建链是:
+
+| 阶段 | 工具 | 实现 |
+| --- | --- | --- |
+| `packages/browser/bridge-browser` | `tsc -b` + `tsdown` | tsdown 底层是 **rolldown**(Rust,`@rolldown/binding-win32-x64-msvc.exe`) |
+| `extensions/dsh-browser` | `vite build` ×3 | vite 底层是 **esbuild**(Go,`@esbuild/win32-x64/esbuild.exe`)与 **rollup**(Rust,`@rollup/rollup-win32-x64-msvc.exe`) |
+
+这些 `.exe` 是 Go/Rust 编译的原生程序,内部调用了 **Windows 8 / Server 2012 才引入** 的系统 API,典型的有:
+
+- `kernel32.dll!GetSystemTimePreciseAsFileTime`(Win8 引入的高精度时钟)
+- `kernel32.dll!QueryUnbiasedInterruptTimePrecise`(Win8)
+- `kernel32.dll!SetThreadDescription`(Win10 1607)
+
+Windows 7 的 `kernel32.dll` **没有这些导出函数**。原生二进制在加载/启动阶段解析导入表失败,随即触发 `0xc0000005`(EXC032026 进程崩溃,通常是 "The NTDLL.dll!LdrpProcessRelocationBlock" 之后的非法访问)。表现为:
+
+```
+$ pnpm build
+> vite build
+# 无任何输出,进程立即退出,退出码 0xC0000005(-1073741819)
+```
+
+同理,`@deepseek-ai/libreoffice-kit-win32-x64/bin/libreoffice-kit.exe`(DSH 运行时依赖,与构建无关)也携带同样的 API。
+
+## 2. 替代方案:纯 WASM / 纯 JS 工具链
+
+方案只使用两种**无原生二进制**的实现:
+
+| 工具 | 说明 |
+| --- | --- |
+| **`esbuild-wasm`** | esbuild 官方的 WebAssembly 构建(`esbuild.wasm`),由 Node 加载运行,API 与 `esbuild` 完全一致 |
+| **`@rollup/wasm-node`** | rollup 官方的纯 JS/WASM 构建,不含 `rollup.exe` 类原生可执行文件 |
+| **`typescript`(transpileModule)** | 纯 JS 的 TypeScript 编译器,用于转译而非类型检查 |
+
+`build-win7.mjs` 在运行时优先 `import('esbuild-wasm')` / `import('@rollup/wasm-node')`;两者均不可用时给出清晰的安装提示并退出。
+
+## 3. 快速开始
+
+```bash
+# 1. 安装依赖(仅所需工作区;--ignore-scripts 可跳过全部 postinstall 原生脚本)
+pnpm install --filter @yuxianglin/dsh-bridge-browser --filter dsh-browser-extension
+
+# 2a. Windows 7 上构建 chrome 扩展(产物 dist/)
+node build-win7.mjs
+
+# 2b. 构建 firefox 扩展(产物 dist-firefox/)
+node build-win7.mjs --firefox
+
+# 或通过 npm 脚本
+pnpm build:win7
+pnpm build:win7:firefox
+```
+
+构建成功后,扩展可直接加载:
+
+- **Chrome**:`chrome://extensions` → 开发者模式 → 加载 `extensions/dsh-browser/dist`
+- **Edge**:`edge://extensions` → 开发者模式 → 加载 `dist`
+- **Firefox**:`about:debugging` → 临时加载 `dist-firefox/manifest.json`
+
+> 非 Windows 7 用户**不受任何影响**:`pnpm build` 及原有工具链保持原样,本方案是增量而非替换。
+
+## 4. `build-win7.mjs` 做了什么
+
+### 阶段 1:bridge 库(`@rollup/wasm-node` + TS 转译插件)
+
+对齐原 `tsc -b && tsdown` 的产物语义(`tsdown.config.ts`:ESM / node 平台 / 不打 dts / external 运行期依赖):
+
+- 入口 `src/{index,protocol,invariant}.ts` → `lib/*.js`(ESM);`@deepseek-ai/*`、`ws`、`node:*` 保持外部引用(由宿主提供)
+- 内联 rollup 插件用 `ts.transpileModule` 做逐文件转译(`isolatedModules` 语义;类型检查请走 `pnpm typecheck`)
+- `src/client.js` 原样复制到 `lib/`(与原构建的 `node -e "copyFileSync(...)"` 一致)
+- `--no-types` 之外的运行会调用 `tsc -b` 生成 `lib/types/` 声明产物(纯 JS,失败仅告警不阻断)
+
+若 `@rollup/wasm-node` 缺失,自动回退到 `esbuild-wasm` 打包 bridge。
+
+### 阶段 2:扩展三目标(`esbuild-wasm`,对齐 `scripts/build.mjs` 的三个 vite 配置)
+
+| 目标 | 入口 | 格式 | 产物 |
+| --- | --- | --- | --- |
+| background | `src/background/index.ts` | chrome: `es`(MV3 module service worker)
firefox: `iife` | `dist/background.js` |
+| content | `src/content/index.ts` | `iife` | `dist/content.js` |
+| panel | `src/panel/main.tsx` | `iife` + JSX automatic + CSS 提取 | `dist/panel/assets/main.js`、`main.css` |
+
+`import.meta.env.EXT_TARGET` 通过 esbuild `define` 注入(对应 `vite.shared.ts` 的行为);`deepseek-256.png` 等资源用 `file` loader 输出为独立文件,与 vite 产物布局一致。
+
+### 阶段 3:静态资源与 HTML(纯 Node `fs`)
+
+复刻 `vite.shared.ts` 的 `copyManifest` 插件:
+
+- `manifest.json`(firefox 时为 `manifest.firefox.json`)→ `dist/manifest.json`
+- `_locales/`、`assets/`(icons 等)→ `dist/` 对应目录
+- `panel/index.html` → 重写产物 HTML:脚本指向 `./assets/main.js`,注入 `./assets/main.css` 样式引用
+
+### 原生风险扫描与 GoWin7Fixer 兜底(可选)
+
+构建结尾会用**纯 Node 解析 PE 导入表**(不执行目标文件)扫描 `node_modules` 中的 `.exe`,报告携带 Win8+ API 导入的文件,例如:
+
+```
+[win7] 原生二进制扫描:发现 1 个携带 Win8+ API 导入的可执行文件:
+[win7] - @deepseek-ai/libreoffice-kit-win32-x64/bin/libreoffice-kit.exe(kernel32.dll!GetSystemTimePreciseAsFileTime)
+[win7] 本 Win7 构建链不加载它们,可安全忽略;如需修补这些文件,请加 --fix-native,
+ 或以管理员身份运行并拖拽文件到 GoWin7Fixer.exe。
+```
+
+这些文件**不在本构建的加载路径上**,提示仅为信息目的。若某些第三方工具(例如 DSH 运行时)确实需要在 Win7 上启动而无法绕过,可以使用 [GoWin7Fixer](https://github.com/GoWin7Fixer)(一个 Windows 7 API 兼容修补 GUI):
+
+```bash
+# 扫描并列出风险文件
+node build-win7.mjs --scan-native
+
+# 扫描并尝试用 GoWin7Fixer.exe 修补(按 GOWIN7FIXER_PATH / 桌面快捷方式 / Downloads / PATH 查找)
+node build-win7.mjs --fix-native
+```
+
+`--fix-native` 会把风险文件作为参数传给 `GoWin7Fixer.exe` 并启动之;该程序是**拖放式 GUI**(实测不处理命令行参数),如未自动修补,请在弹出的窗口中把文件拖入并确认。
+
+## 5. 命令行选项一览
+
+| 选项 | 作用 |
+| --- | --- |
+| (默认) | 构建 chrome 扩展到 `dist/` |
+| `--firefox` | 构建 firefox 扩展到 `dist-firefox/` |
+| `--no-types` | 跳过 bridge 的 `tsc -b` 类型产物(纯 JS 转译照常) |
+| `--scan-native` | 仅扫描 node_modules 中的 Win8+ API 原生二进制后退出 |
+| `--fix-native` | 扫描并调用 `GoWin7Fixer.exe` 尝试修补 |
+
+## 6. 开发工作流说明
+
+- **类型检查**仍可随时执行:`pnpm typecheck`(`tsc --noEmit`,纯 JS,Win7 安全)
+- **单元测试**:`pnpm test` 使用 vitest(vitest 本身依赖 vite 的原生 esbuild?—— vitest 走 vite 的 **JS 实现**(vite 在有 `esbuild-wasm` 时不加载原生 exe;vite 6 的 esbuild 集成会优先使用已安装的 `esbuild-wasm`)。如遇 vitest 也崩溃,可用 `--no-types` 类似思路跳过该阶段
+- 本方案**不提供 watch/dev server**(`pnpm dev` 依赖 vite 开发服务器面板热更新);开发期请用非 Win7 机器,或改完源码后重跑 `node build-win7.mjs` 再到 `chrome://extensions` 点"重新加载"
+
+## 7. 已知限制
+
+1. WASM 打包比原生 esbuild 慢数倍(本仓库全量构建约数十秒,Win7 机械盘上更慢)
+2. 无源映射(`sourcemap: false`);如需调试可临时打开
+3. panel 产物为 `iife` 包而非 vite 的 ES 模块分包(无 code-splitting,单文件稍大)
+4. `@deepseek-ai/libreoffice-kit-win32-x64`(约 71MB 运行时包)在 Win7 上需用 GoWin7Fixer 修补其 `libreoffice-kit.exe` 才能被 DSH 运行时加载——与本构建无关
+
+## 8. 验证环境
+
+- Windows 7 SP1(x64)+ Node v18.20.0 / v26.7.0
+- pnpm 11.7.0(经 corepack)
+- `esbuild-wasm@0.25.12`、`@rollup/wasm-node@4.66.0`、`typescript@6.0.3`、`vite@6.4.x`(仅作为依赖元数据,不执行其原生二进制)
+
+产物已校验与原 vite/tsc+tsdown 构建在结构上等价(external 导入、`EXT_TARGET` 注入、MV3 manifest、`_locales`、icons、panel HTML 资源引用)。
+
+---
+
+## 附:为什么不用 pnpm overrides 强制把 `esbuild` 重定向到 `esbuild-wasm`?
+
+在根 `package.json` 写 `"pnpm": { "overrides": { "esbuild": "npm:esbuild-wasm@..." } }` 会让 vite/vitest 的 `esbuild` 导入指向 wasm 包,理论上可行,但:
+
+1. vite 内部对 `esbuild` 的 `build`/`transform` 调用在 wasm 实现下初始化路径不同,仍可能间接触发对原生可执行文件的引用探测
+2. overrides 会**全局**改写所有消费者的依赖解析,破坏正常平台用户的行为
+3. `rolldown` 无法用这种方式替换(它的原生绑定由 npm 包单独分发)
+
+因此本方案选择**显式的独立构建入口**:保留原有构建链,新增 `build-win7.mjs` 与 `build:win7` 脚本,对原仓库的侵入性降到最低(仅新增文件、新增根 devDependencies 与两个脚本)。
diff --git a/build-win7.mjs b/build-win7.mjs
new file mode 100644
index 00000000..ef51b05f
--- /dev/null
+++ b/build-win7.mjs
@@ -0,0 +1,556 @@
+#!/usr/bin/env node
+/**
+ * build-win7.mjs — Windows 7 兼容构建入口(dsh-browser)
+ *
+ * 为什么需要它:
+ * 原生构建链(tsdown → rolldown、vite → esbuild/rollup 原生二进制)携带
+ * Go/Rust 编译的 .exe/.node,它们在内部调用 Windows 8+ 才存在的
+ * GetSystemTimePreciseAsFileTime 等 API,Win7 上运行会直接 0xc0000005 崩溃。
+ *
+ * 本脚本完全不加载任何原生二进制:
+ * - bridge 库(packages/browser/bridge-browser):@rollup/wasm-node(纯 wasm)
+ * + 内联 TypeScript 转译插件(typescript 自带的 transpileModule,纯 JS)。
+ * - 扩展(extensions/dsh-browser):esbuild-wasm(纯 wasm),一次三目标:
+ * background(chrome: es / firefox: iife)、content(iife)、panel(React iife)。
+ * - 静态资源(manifest、_locales、assets、panel/index.html):Node fs 复制,
+ * HTML 中的脚本/样式引用改写为产物路径。
+ * - 原生兜底:扫描 node_modules 中携带 Win8+ API 导入的 .exe;若显式要求
+ * (--fix-native)则调用 GoWin7Fixer.exe 尝试修补。
+ *
+ * 用法:
+ * node build-win7.mjs 构建 chrome 扩展(dist/)
+ * node build-win7.mjs --firefox 构建 firefox 扩展(dist-firefox/)
+ * node build-win7.mjs --no-types 跳过 bridge 的 tsc 类型产物
+ * node build-win7.mjs --scan-native 仅扫描可疑原生二进制
+ * node build-win7.mjs --fix-native 扫描并调用 GoWin7Fixer.exe 修补
+ *
+ * 退出码:0 成功;非 0 表示构建失败。
+ */
+
+import { spawn, spawnSync } from 'node:child_process'
+import {
+ cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync,
+} from 'node:fs'
+import { basename, dirname, isAbsolute, join, resolve } from 'node:path'
+import { fileURLToPath, pathToFileURL } from 'node:url'
+
+const ROOT = dirname(fileURLToPath(import.meta.url))
+const BRIDGE = join(ROOT, 'packages', 'browser', 'bridge-browser')
+const EXT = join(ROOT, 'extensions', 'dsh-browser')
+
+const argv = process.argv.slice(2)
+const firefox = argv.includes('--firefox')
+const withTypes = !argv.includes('--no-types')
+const scanNativeOnly = argv.includes('--scan-native')
+const fixNative = argv.includes('--fix-native')
+
+const browserTarget = firefox ? 'firefox' : 'chrome'
+const extOutDir = join(EXT, firefox ? 'dist-firefox' : 'dist')
+const targetManifest = firefox ? 'manifest.firefox.json' : 'manifest.json'
+
+/** 统一前缀日志 */
+const log = (msg) => console.log(`[win7] ${msg}`)
+const warn = (msg) => console.warn(`[win7:warn] ${msg}`)
+const fail = (msg) => { console.error(`[win7:error] ${msg}`); process.exit(1) }
+const bytes = (n) => `${(n / 1024).toFixed(1)} KB`
+
+async function main() {
+ log(`dsh-browser Win7 构建(target=${browserTarget})`)
+
+ const esbuild = await tryImportEsbuild()
+ const rollup = await tryImportRollup()
+ if (!esbuild && !rollup) {
+ fail('esbuild-wasm 与 @rollup/wasm-node 均不可用。请在项目根目录执行:\n pnpm install --filter @yuxianglin/dsh-bridge-browser --filter dsh-browser-extension\n依赖安装完成后再运行本脚本。')
+ }
+ log(`引擎:esbuild-wasm=${esbuild ? 'ok' : '缺失'} @rollup/wasm-node=${rollup ? 'ok' : '缺失'}`)
+
+ if (scanNativeOnly || fixNative) {
+ await runNativeStep(fixNative)
+ if (scanNativeOnly) process.exit(0)
+ }
+
+ // ---- bridge 库 ----
+ log('=== 阶段 1/3:bridge-browser(@rollup/wasm-node)===')
+ await buildBridge({ rollup, esbuild })
+
+ // ---- 扩展 ----
+ log('=== 阶段 2/3:dsh-browser-extension(esbuild-wasm)===')
+ await buildExtension(esbuild)
+
+ // ---- 原生风险提示(非阻塞)----
+ await runNativeStep(false)
+
+ log('=== 完成 ===')
+ reportOutputs()
+}
+
+/* ------------------------------------------------------------------ *
+ * 引擎加载
+ * ------------------------------------------------------------------ */
+
+async function tryImportEsbuild() {
+ try {
+ const mod = await import('esbuild-wasm')
+ return typeof mod.build === 'function' ? mod : null
+ } catch { return null }
+}
+
+async function tryImportRollup() {
+ try {
+ const mod = await import('@rollup/wasm-node')
+ const rollup = mod.rollup ?? mod.default?.rollup
+ return typeof rollup === 'function' ? rollup : null
+ } catch { return null }
+}
+
+/** 从 bridge 包目录加载纯 JS 的 typescript(根目录未必安装) */
+let tsModule = null
+async function loadTypeScript() {
+ if (tsModule) return tsModule
+ for (const candidate of [
+ join(BRIDGE, 'node_modules', 'typescript', 'lib', 'typescript.js'),
+ join(EXT, 'node_modules', 'typescript', 'lib', 'typescript.js'),
+ ]) {
+ if (existsSync(candidate)) {
+ tsModule = await import(pathToFileURL(candidate).href)
+ return tsModule
+ }
+ }
+ return null
+}
+
+/* ------------------------------------------------------------------ *
+ * 阶段 1:bridge 库
+ * ------------------------------------------------------------------ */
+
+const BRIDGE_ENTRIES = ['index', 'protocol', 'invariant']
+/** 运行期由宿主提供,保持外部引用(与 tsdown 平台行为一致) */
+const bridgeExternal = (id) =>
+ id.startsWith('@deepseek-ai/') || id === 'ws' || id.startsWith('node:')
+
+async function buildBridge({ rollup, esbuild }) {
+ rmSync(join(BRIDGE, 'lib'), { recursive: true, force: true })
+ mkdirSync(join(BRIDGE, 'lib'), { recursive: true })
+ if (withTypes) await emitBridgeTypes()
+
+ if (rollup) {
+ await buildBridgeWithRollup(rollup)
+ } else {
+ log('@rollup/wasm-node 不可用,回退到 esbuild-wasm 打包 bridge')
+ if (!esbuild) fail('无可用打包引擎(见上方引擎检查)')
+ await buildBridgeWithEsbuild(esbuild)
+ }
+
+ // 与原构建一致:client.js 是纯 JS 透传文件
+ const clientSrc = join(BRIDGE, 'src', 'client.js')
+ if (existsSync(clientSrc)) {
+ cpSync(clientSrc, join(BRIDGE, 'lib', 'client.js'))
+ log(`复制 src/client.js → lib/client.js`)
+ } else {
+ warn('src/client.js 不存在,跳过 client.js 产物')
+ }
+}
+
+/** tsc -b:纯 JS 实现,产物 lib/types/*.js + *.d.ts(与原流程一致,失败不致命) */
+async function emitBridgeTypes() {
+ const tsc = join(BRIDGE, 'node_modules', 'typescript', 'bin', 'tsc')
+ if (!existsSync(tsc)) {
+ warn('bridge 下未安装 typescript,跳过 lib/types 类型产物(使用 --no-types 可关闭该提示)')
+ return
+ }
+ log('tsc -b(生成 lib/types 声明产物)…')
+ const res = spawnSync(process.execPath, [tsc, '-b', '--pretty', 'false'], { cwd: BRIDGE })
+ if (res.status !== 0) {
+ warn('tsc -b 失败(类型/依赖问题),已跳过类型产物;JS 产物不受影响。')
+ warn(String(res.stderr || res.stdout).trim().split('\n').slice(0, 5).join('\n'))
+ }
+}
+
+async function buildBridgeWithRollup(rollup) {
+ const ts = await loadTypeScript()
+ if (!ts) { warn('typescript 不可用,bridge 回退到 esbuild-wasm'); return buildBridgeWithEsbuild(await tryImportEsbuild()) }
+
+ const plugin = makeTsTranspilePlugin(ts)
+ for (const name of BRIDGE_ENTRIES) {
+ const input = join(BRIDGE, 'src', `${name}.ts`)
+ const bundle = await rollup({
+ input,
+ plugins: [plugin],
+ external: bridgeExternal,
+ onwarn: (warning, handler) => {
+ if (warning.code === 'UNRESOLVED_IMPORT' || warning.code === 'EVAL') warn(String(warning.message))
+ else handler(warning)
+ },
+ })
+ await bundle.write({
+ file: join(BRIDGE, 'lib', `${name}.js`),
+ format: 'esm',
+ sourcemap: false,
+ })
+ await bundle.close()
+ log(`rollup: src/${name}.ts → lib/${name}.js`)
+ }
+}
+
+/**
+ * 内联 TypeScript→JS 插件:每文件 transpileModule(isolatedModules 语义,
+ * 不做类型检查;类型检查请用 pnpm --filter @yuxianglin/dsh-bridge-browser typecheck)。
+ */
+function makeTsTranspilePlugin(ts) {
+ const exts = ['.ts', '.tsx', '.js', '.json']
+ const resolveRelative = (importer, spec) => {
+ if (!spec.startsWith('.') && !spec.startsWith('/')) return null
+ const base = isAbsolute(spec) ? spec : resolve(dirname(importer), spec)
+ if (existsSync(base) && statSync(base).isFile()) return base
+ for (const ext of exts) {
+ if (existsSync(base + ext)) return base + ext
+ }
+ for (const ext of exts) {
+ const idx = join(base, `index${ext}`)
+ if (existsSync(idx)) return idx
+ }
+ return null
+ }
+ return {
+ name: 'win7-typescript-transpile',
+ resolveId(specifier, importer) {
+ if (!importer) return null
+ const resolved = resolveRelative(importer, specifier)
+ return resolved
+ },
+ transform(code, id) {
+ if (!/\.(ts|tsx)$/.test(id)) return null
+ const result = ts.transpileModule(code, {
+ compilerOptions: {
+ module: ts.ModuleKind.ESNext,
+ target: ts.ScriptTarget.ES2022,
+ jsx: ts.JsxEmit.ReactJSX,
+ esModuleInterop: true,
+ verbatimModuleSyntax: false,
+ },
+ })
+ return { code: result.outputText, map: result.sourceMapText ? JSON.parse(result.sourceMapText) : null }
+ },
+ }
+}
+
+async function buildBridgeWithEsbuild(esbuild) {
+ await esbuild.build({
+ entryPoints: BRIDGE_ENTRIES.map((n) => join(BRIDGE, 'src', `${n}.ts`)),
+ bundle: true,
+ format: 'esm',
+ platform: 'node',
+ target: 'es2022',
+ outdir: join(BRIDGE, 'lib'),
+ entryNames: '[name]',
+ external: ['@deepseek-ai/*', 'ws'],
+ logLevel: 'warning',
+ legalComments: 'none',
+ })
+ log(`esbuild-wasm: src/{${BRIDGE_ENTRIES.join(',')}}.ts → lib/`)
+}
+
+/* ------------------------------------------------------------------ *
+ * 阶段 2:扩展三目标
+ * ------------------------------------------------------------------ */
+
+async function buildExtension(esbuild) {
+ if (!esbuild) {
+ fail('扩展构建依赖 esbuild-wasm(JSX/CSS/资源处理)。请在根目录执行:pnpm add -D esbuild-wasm')
+ }
+ rmSync(extOutDir, { recursive: true, force: true })
+ mkdirSync(extOutDir, { recursive: true })
+
+ const define = { 'import.meta.env.EXT_TARGET': JSON.stringify(browserTarget) }
+ const common = {
+ bundle: true,
+ minify: true,
+ target: 'es2022',
+ platform: 'browser',
+ define,
+ sourcemap: false,
+ logLevel: 'info',
+ legalComments: 'none',
+ loader: { '.png': 'file', '.svg': 'file', '.woff': 'file', '.woff2': 'file' },
+ }
+
+ try {
+ // background:chrome 用 ES module service worker,firefox 用 iife
+ const bgFormat = firefox ? 'iife' : 'esm'
+ await esbuild.build({
+ ...common,
+ entryPoints: [join(EXT, 'src', 'background', 'index.ts')],
+ outdir: extOutDir,
+ entryNames: 'background',
+ format: bgFormat,
+ globalName: firefox ? '__dsh_background__' : undefined,
+ })
+ log(`background → ${join(basename(extOutDir), 'background.js')}(${bgFormat})`)
+
+ // content:iife
+ await esbuild.build({
+ ...common,
+ entryPoints: [join(EXT, 'src', 'content', 'index.ts')],
+ outdir: extOutDir,
+ entryNames: 'content',
+ format: 'iife',
+ globalName: '__dsh_content__',
+ })
+ log(`content → ${join(basename(extOutDir), 'content.js')}(iife)`)
+
+ // panel:React iife,CSS 提取到 panel/assets/main.css
+ await esbuild.build({
+ ...common,
+ entryPoints: [join(EXT, 'src', 'panel', 'main.tsx')],
+ outdir: join(extOutDir, 'panel'),
+ entryNames: 'assets/[name]',
+ assetNames: 'assets/[name]',
+ format: 'iife',
+ globalName: '__dsh_panel__',
+ jsx: 'automatic',
+ })
+ log(`panel → ${join(basename(extOutDir), 'panel', 'assets', 'main.js')}(iife + css 提取)`)
+ } catch (e) {
+ fail(`扩展构建失败:${e && e.message ? e.message : e}`)
+ }
+
+ writePanelHtml()
+ copyStaticAssets()
+}
+
+/** panel/index.html → 产物 HTML:脚本/样式指向 assets/ */
+function writePanelHtml() {
+ const src = join(EXT, 'panel', 'index.html')
+ if (!existsSync(src)) { warn('panel/index.html 不存在,跳过 HTML 改写'); return }
+ let html = readFileSync(src, 'utf8')
+ const scriptRe = /`
+ })
+ const out = join(extOutDir, 'panel', 'index.html')
+ mkdirSync(dirname(out), { recursive: true })
+ writeFileSync(out, html, 'utf8')
+ log(`panel/index.html → ${join(basename(extOutDir), 'panel', 'index.html')}(注入 assets 引用)`)
+}
+
+/** manifest、_locales、assets → outDir(对齐 vite copyManifest 插件) */
+function copyStaticAssets() {
+ copyFileToOut(targetManifest, 'manifest.json')
+ copyDirToOut('_locales')
+ copyDirToOut('assets')
+}
+
+function copyFileToOut(srcName, outName) {
+ const src = join(EXT, srcName)
+ if (!existsSync(src)) { warn(`${srcName} 不存在,跳过`); return }
+ mkdirSync(extOutDir, { recursive: true })
+ cpSync(src, join(extOutDir, outName))
+ log(`复制 ${srcName} → ${join(basename(extOutDir), outName)}`)
+}
+
+function copyDirToOut(dirName) {
+ const src = join(EXT, dirName)
+ if (!existsSync(src)) { warn(`${dirName}/ 不存在,跳过`); return }
+ mkdirSync(extOutDir, { recursive: true })
+ cpSync(src, join(extOutDir, dirName), { recursive: true })
+ log(`复制 ${dirName}/ → ${join(basename(extOutDir), dirName)}/`)
+}
+
+/* ------------------------------------------------------------------ *
+ * 原生二进制风险扫描与 GoWin7Fixer 兜底
+ * ------------------------------------------------------------------ */
+
+/** Win7 不存在、Win8+ 才有的 API(Go/Rust 工具链崩溃的典型根因) */
+const WIN8_PLUS_API = ['GetSystemTimePreciseAsFileTime', 'SetThreadDescription', 'QueryUnbiasedInterruptTimePrecise']
+
+async function runNativeStep(invokeFixer) {
+ const risky = scanRiskyExecutables(join(ROOT, 'node_modules'))
+ if (risky.length === 0) {
+ log('原生二进制扫描:node_modules 中未发现 Win8+ API 依赖的 .exe(本构建无需原生二进制)')
+ return
+ }
+ log(`原生二进制扫描:发现 ${risky.length} 个携带 Win8+ API 导入的可执行文件:`)
+ for (const item of risky) log(` - ${item.path}(${item.dll}!${item.api})`)
+ if (!invokeFixer) {
+ log('本 Win7 构建链不加载它们,可安全忽略;如需修补这些文件,请加 --fix-native,或以管理员身份运行并拖拽文件到 GoWin7Fixer.exe。')
+ return
+ }
+ const fixer = locateGoWin7Fixer()
+ if (!fixer) {
+ warn('未找到 GoWin7Fixer.exe(已查找桌面快捷方式、Downloads、PATH、GOWIN7FIXER_PATH)。')
+ warn('请手动启动 GoWin7Fixer.exe 并拖入以下文件:')
+ for (const item of risky) warn(` - ${resolve(ROOT, item.path)}`)
+ return
+ }
+ log(`调用 GoWin7Fixer.exe:${fixer}`)
+ const targets = [...new Set(risky.map((i) => resolve(ROOT, i.path)))]
+ const child = spawn(fixer, targets, { detached: true, stdio: 'ignore' })
+ child.unref()
+ log('已将下列文件作为参数传入 GoWin7Fixer.exe(GUI 程序;如未自动修补,请在其窗口中拖拽确认):')
+ for (const t of targets) log(` - ${t}`)
+}
+
+/** 在 node_modules 中扫描 .exe 的 PE 导入表(纯 Node 解析,不执行目标文件) */
+function scanRiskyExecutables(rootDir) {
+ const found = []
+ if (!existsSync(rootDir)) return found
+ const stack = [{ dir: rootDir, depth: 0 }]
+ const seen = new Set()
+ const MAX_DEPTH = 8, MAX_FILES = 4000
+ let scanned = 0
+ while (stack.length && scanned < MAX_FILES) {
+ const { dir, depth } = stack.pop()
+ if (depth > MAX_DEPTH) continue
+ let names
+ try { names = readdirSync(dir, { withFileTypes: true }) } catch { continue }
+ for (const ent of names) {
+ if (ent.isDirectory()) {
+ if (ent.name === '.git' || ent.name === 'node_modules' && depth > 2) continue
+ stack.push({ dir: join(dir, ent.name), depth: depth + 1 })
+ } else if (ent.isFile() && /\.exe$/i.test(ent.name)) {
+ scanned++
+ const path = join(dir, ent.name)
+ let st
+ try { st = statSync(path) } catch { continue }
+ if (st.size > 256 * 1024 * 1024) continue
+ const hit = findWin8PlusImport(readFileSync(path))
+ if (hit) {
+ const rel = path.replace(/\\/g, '/').replace(/^.*node_modules\//, '')
+ found.push({ path: rel, dll: hit.dll, api: hit.api })
+ }
+ }
+ }
+ if (found.length >= 32) break
+ }
+ return found
+}
+
+/** 解析 PE 导入表,返回命中的 Win8+ API({ dll, api }) */
+function findWin8PlusImport(buf) {
+ try {
+ if (buf.length < 0x40 || buf.readUInt16LE(0) !== 0x5a4d) return null
+ const e_lfanew = buf.readUInt32LE(0x3c)
+ if (e_lfanew + 24 > buf.length || buf.readUInt32LE(e_lfanew) !== 0x00004550) return null
+ const machine = buf.readUInt16LE(e_lfanew + 4)
+ const is64 = machine === 0x8664 || machine === 0xaa64
+ const numSections = buf.readUInt16LE(e_lfanew + 6)
+ const sizeOpt = buf.readUInt16LE(e_lfanew + 20)
+ const opt = e_lfanew + 24
+ const magic = buf.readUInt16LE(opt)
+ if (magic !== 0x10b && magic !== 0x20b) return null
+ const dataDirBase = magic === 0x20b ? opt + 112 : opt + 96
+ const importRva = buf.readUInt32LE(dataDirBase + 8) // directory[1].RVA
+ if (!importRva) return null
+ const sections = []
+ for (let i = 0; i < numSections; i++) {
+ const s = opt + sizeOpt + i * 40
+ if (s + 40 > buf.length) return null
+ const va = buf.readUInt32LE(s + 12)
+ const vsize = buf.readUInt32LE(s + 8)
+ const rawSize = buf.readUInt32LE(s + 16)
+ const rawPtr = buf.readUInt32LE(s + 20)
+ sections.push({ va, end: va + Math.max(vsize, rawSize), rawPtr })
+ }
+ const rvaToOffset = (rva) => {
+ for (const s of sections) if (rva >= s.va && rva < s.end) return rva - s.va + s.rawPtr
+ return null
+ }
+ const readCstring = (offset) => {
+ if (offset === null || offset < 0 || offset >= buf.length) return ''
+ let end = offset
+ while (end < buf.length && buf[end] !== 0) end++
+ return buf.toString('ascii', offset, end)
+ }
+ let descOff = rvaToOffset(importRva)
+ for (let i = 0; i < 512 && descOff !== null && descOff + 20 <= buf.length; i++, descOff += 20) {
+ const olt = buf.readUInt32LE(descOff) // OriginalFirstThunk
+ const nameRva = buf.readUInt32LE(descOff + 12)
+ const iat = buf.readUInt32LE(descOff + 16) // FirstThunk
+ if (!olt && !nameRva && !iat) break
+ const dll = readCstring(rvaToOffset(nameRva)).toLowerCase()
+ if (dll === 'kernel32.dll' || dll === 'kernelbase.dll' || dll === 'api-ms-win-core-sysinfo-l1-1-0.dll' || dll === 'api-ms-win-core-sysinfo-l1-2-0.dll' || dll === 'api-ms-win-core-threadpool-l1-2-0.dll') {
+ let thunkRva = olt || iat
+ let thunkOff = rvaToOffset(thunkRva)
+ const step = is64 ? 8 : 4
+ for (let j = 0; thunkOff !== null && thunkOff + step <= buf.length; j++) {
+ const entry = is64 ? Number(buf.readBigUInt64LE(thunkOff)) : buf.readUInt32LE(thunkOff)
+ if (entry === 0) break
+ const ordinalFlag = is64 ? 0x8000000000000000n : 0x80000000
+ if ((is64 ? BigInt(entry) : entry) & BigInt(ordinalFlag)) { thunkOff += step; continue }
+ const fn = readCstring(rvaToOffset(entry) === null ? null : rvaToOffset(entry) + 2)
+ if (WIN8_PLUS_API.includes(fn)) return { dll, api: fn }
+ thunkOff += step
+ }
+ }
+ }
+ } catch { /* Not a parseable PE — ignore */ }
+ return null
+}
+
+/** 定位 GoWin7Fixer.exe:环境变量 → 桌面快捷方式 → Downloads → PATH */
+function locateGoWin7Fixer() {
+ const envPath = process.env.GOWIN7FIXER_PATH
+ if (envPath && existsSync(envPath)) return envPath
+ const home = process.env.USERPROFILE || process.env.HOME
+ if (home) {
+ const desktop = join(home, 'Desktop')
+ for (const name of ['GoWin7Fixer.exe', 'GoWin7Fixer.exe.lnk']) {
+ const p = join(desktop, name)
+ if (existsSync(p)) {
+ if (/\.exe$/i.test(p)) return p
+ const target = parseLnkTarget(p)
+ if (target && existsSync(target)) return target
+ }
+ }
+ }
+ for (const fixed of ['D:\\Downloads\\GoWin7Fixer.exe', 'C:\\Users\\Administrator\\Downloads\\GoWin7Fixer.exe']) {
+ if (existsSync(fixed)) return fixed
+ }
+ const onPath = which('GoWin7Fixer.exe')
+ if (onPath) return onPath
+ return null
+}
+
+function parseLnkTarget(lnkPath) {
+ try {
+ const text = readFileSync(lnkPath, 'utf16le')
+ const m = text.match(/([A-Za-z]:\\[^\u0000-\u001f<>|?*]+\.exe)/i)
+ return m ? m[1] : null
+ } catch { return null }
+}
+
+function which(name) {
+ const res = spawnSync('where', [name], { windowsVerbatimArguments: false })
+ if (res.status === 0) return String(res.stdout).trim().split(/\r?\n/)[0] || null
+ return null
+}
+
+/* ------------------------------------------------------------------ *
+ * 产物报告
+ * ------------------------------------------------------------------ */
+
+function reportOutputs() {
+ const items = []
+ const walk = (dir, prefix = '') => {
+ let names
+ try { names = readdirSync(dir, { withFileTypes: true }) } catch { return }
+ for (const ent of names) {
+ const p = join(dir, ent.name)
+ if (ent.isDirectory()) walk(p, `${prefix}${ent.name}/`)
+ else items.push({ path: `${prefix}${ent.name}`, size: statSync(p).size })
+ }
+ }
+ walk(join(BRIDGE, 'lib'))
+ walk(extOutDir)
+ const total = items.reduce((sum, i) => sum + i.size, 0)
+ for (const i of items) console.log(` ${bytes(i.size).padStart(10)} ${i.path}`)
+ log(`产物共 ${items.length} 个文件,合计 ${bytes(total)}(bridge lib/ + ${basename(extOutDir)}/)`)
+}
+
+main().catch((err) => {
+ console.error(`[win7:error] 未捕获错误:${err && err.stack ? err.stack : err}`)
+ process.exit(1)
+})
diff --git a/package.json b/package.json
index 5301807b..b7c7eaf4 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,8 @@
"packageManager": "pnpm@11.7.0",
"scripts": {
"build": "pnpm --filter @yuxianglin/dsh-bridge-browser run build && pnpm --filter dsh-browser-extension run build",
+ "build:win7": "node build-win7.mjs",
+ "build:win7:firefox": "node build-win7.mjs --firefox",
"start": "dsh web",
"test": "pnpm --filter @yuxianglin/dsh-bridge-browser run test && pnpm --filter dsh-browser-extension run test",
"typecheck": "pnpm --filter @yuxianglin/dsh-bridge-browser run typecheck && pnpm --filter dsh-browser-extension run typecheck",
@@ -50,5 +52,9 @@
"@deepseek-ai/dsh-util-time": "0.2.0-rc.2",
"@deepseek-ai/dsh-util-workspace-path": "0.2.0-rc.2",
"@deepseek-ai/dsh-workflow": "0.2.0-rc.2"
+ },
+ "devDependencies": {
+ "@rollup/wasm-node": "^4.18.0",
+ "esbuild-wasm": "^0.25.12"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2b3c3d4f..d396c9c4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -113,6 +113,13 @@ importers:
'@deepseek-ai/dsh-workflow':
specifier: 0.2.0-rc.2
version: 0.2.0-rc.2(@deepseek-ai/cordis@4.0.4)(@deepseek-ai/dsh-agent@0.2.0-rc.2(fee0e653b72a3883e0f0da2d9a46bb50))(@deepseek-ai/dsh-brand@0.2.0-rc.2(@deepseek-ai/cordis@4.0.4))(@deepseek-ai/dsh-invariants@0.2.0-rc.2(@deepseek-ai/cordis@4.0.4))(@deepseek-ai/dsh-llm@0.2.0-rc.2(@deepseek-ai/cordis@4.0.4))(@deepseek-ai/dsh-session@0.2.0-rc.2(@deepseek-ai/cordis@4.0.4)(@deepseek-ai/dsh-scope@0.2.0-rc.2(@deepseek-ai/cordis@4.0.4)(@deepseek-ai/dsh-invariants@0.2.0-rc.2(@deepseek-ai/cordis@4.0.4))))
+ devDependencies:
+ '@rollup/wasm-node':
+ specifier: ^4.18.0
+ version: 4.66.0
+ esbuild-wasm:
+ specifier: ^0.25.12
+ version: 0.25.12
extensions/dsh-browser:
dependencies:
@@ -161,7 +168,7 @@ importers:
version: 6.1.1(typescript@5.9.3)(vite@6.4.3(@types/node@22.20.1)(yaml@2.9.0))
vitest:
specifier: ^4.1.11
- version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@22.20.1)(jsdom@26.1.0)(vite@6.4.3(@types/node@22.20.1)(yaml@2.9.0))
+ version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@22.20.1)(jsdom@26.1.0)(yaml@2.9.0)
packages/browser/bridge-browser:
dependencies:
@@ -249,7 +256,7 @@ importers:
version: 22.20.1
'@types/ws':
specifier: ^8.18.1
- version: 8.18.1
+ version: 8.18.2
playwright-core:
specifier: ^1.61.1
version: 1.62.1
@@ -261,7 +268,7 @@ importers:
version: 6.0.3
vitest:
specifier: ^4.1.11
- version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@22.20.1)(jsdom@26.1.0)(vite@6.4.3(@types/node@22.20.1)(yaml@2.9.0))
+ version: 4.1.11(@opentelemetry/api@1.9.1)(@types/node@22.20.1)(jsdom@26.1.0)(yaml@2.9.0)
packages:
@@ -3745,6 +3752,11 @@ packages:
cpu: [x64]
os: [win32]
+ '@rollup/wasm-node@4.66.0':
+ resolution: {integrity: sha512-iZ7VKtuFJNDH+dAxK4v8gXdnSrZxbGVv/yRYY0B6bQFmb4tUQpOwOkr4f+CNf/ZvmJphDHDQ8sLCSe5ZbQYxgA==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
'@sec-ant/readable-stream@0.4.1':
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
@@ -3863,8 +3875,8 @@ packages:
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
- '@types/ws@8.18.1':
- resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
+ '@types/ws@8.18.2':
+ resolution: {integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==}
'@vitejs/plugin-react@4.7.0':
resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==}
@@ -4188,6 +4200,11 @@ packages:
es-module-lexer@2.3.1:
resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==}
+ esbuild-wasm@0.25.12:
+ resolution: {integrity: sha512-rZqkjL3Y6FwLpSHzLnaEy8Ps6veCNo1kZa9EOfJvmWtBq5dJH4iVjfmOO6Mlkv9B0tt9WFPFmb/VxlgJOnueNg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
esbuild@0.25.12:
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
engines: {node: '>=18'}
@@ -4313,8 +4330,8 @@ packages:
resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
engines: {node: '>=18'}
- http-cache-semantics@4.2.0:
- resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==}
+ http-cache-semantics@4.3.0:
+ resolution: {integrity: sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==}
http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
@@ -4722,8 +4739,8 @@ packages:
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
- resolve.exports@2.0.3:
- resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==}
+ resolve.exports@2.0.4:
+ resolution: {integrity: sha512-zepuGL0ulf+nt+XaFrlKeaiTJeyPFPLLY62+ncq1/VTHBdt3HsfXOH60priZ6tnXQozdGcDeIF5JVTanvIcYWQ==}
engines: {node: '>=10'}
responselike@4.0.2:
@@ -4852,8 +4869,8 @@ packages:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
- source-map-js@1.2.1:
- resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ source-map-js@1.2.2:
+ resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==}
engines: {node: '>=0.10.0'}
stackback@0.0.2:
@@ -5072,7 +5089,6 @@ packages:
'@vitest/ui': 4.1.11
happy-dom: '*'
jsdom: '*'
- vite: ^6.0.0 || ^7.0.0 || ^8.0.0
peerDependenciesMeta:
'@edge-runtime/vm':
optional: true
@@ -5847,7 +5863,7 @@ snapshots:
ajv: 8.20.0
js-yaml: 4.3.2
node-addon-require-builtin: 0.1.6
- resolve.exports: 2.0.3
+ resolve.exports: 2.0.4
semver: 7.8.5
'@deepseek-ai/dsh-atomic-write@0.2.0-rc.2(@deepseek-ai/cordis@4.0.4)':
@@ -9237,6 +9253,13 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.62.4':
optional: true
+ '@rollup/wasm-node@4.66.0':
+ dependencies:
+ '@types/estree': 1.0.9
+ optionalDependencies:
+ '@napi-rs/lzma-linux-x64-gnu': 1.5.1
+ fsevents: 2.3.3
+
'@sec-ant/readable-stream@0.4.1': {}
'@sindresorhus/is@7.2.0': {}
@@ -9371,7 +9394,7 @@ snapshots:
'@types/trusted-types@2.0.7':
optional: true
- '@types/ws@8.18.1':
+ '@types/ws@8.18.2':
dependencies:
'@types/node': 22.20.1
@@ -9546,7 +9569,7 @@ snapshots:
dependencies:
'@types/http-cache-semantics': 4.2.0
get-stream: 9.0.1
- http-cache-semantics: 4.2.0
+ http-cache-semantics: 4.3.0
keyv: 5.6.0
mimic-response: 4.0.0
normalize-url: 8.1.1
@@ -9660,6 +9683,8 @@ snapshots:
es-module-lexer@2.3.1: {}
+ esbuild-wasm@0.25.12: {}
+
esbuild@0.25.12:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.12
@@ -9829,7 +9854,7 @@ snapshots:
dependencies:
whatwg-encoding: 3.1.1
- http-cache-semantics@4.2.0: {}
+ http-cache-semantics@4.3.0: {}
http-proxy-agent@7.0.2:
dependencies:
@@ -10152,7 +10177,7 @@ snapshots:
dependencies:
nanoid: 3.3.18
picocolors: 1.1.1
- source-map-js: 1.2.1
+ source-map-js: 1.2.2
powershell-utils@0.1.0: {}
@@ -10206,7 +10231,7 @@ snapshots:
resolve-pkg-maps@1.0.0: {}
- resolve.exports@2.0.3: {}
+ resolve.exports@2.0.4: {}
responselike@4.0.2:
dependencies:
@@ -10374,7 +10399,7 @@ snapshots:
signal-exit@4.1.0: {}
- source-map-js@1.2.1: {}
+ source-map-js@1.2.2: {}
stackback@0.0.2: {}
@@ -10515,7 +10540,7 @@ snapshots:
fsevents: 2.3.3
yaml: 2.9.0
- vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@22.20.1)(jsdom@26.1.0)(vite@6.4.3(@types/node@22.20.1)(yaml@2.9.0)):
+ vitest@4.1.11(@opentelemetry/api@1.9.1)(@types/node@22.20.1)(jsdom@26.1.0)(yaml@2.9.0):
dependencies:
'@vitest/expect': 4.1.11
'@vitest/mocker': 4.1.11(vite@6.4.3(@types/node@22.20.1)(yaml@2.9.0))
@@ -10542,7 +10567,17 @@ snapshots:
'@types/node': 22.20.1
jsdom: 26.1.0
transitivePeerDependencies:
+ - jiti
+ - less
+ - lightningcss
- msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - terser
+ - tsx
+ - yaml
w3c-xmlserializer@5.0.0:
dependencies: