@open-wxa/wxa-runner-core 0.12.1 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,6 +28,8 @@ interface AppInfo {
28
28
  device: DeviceProfile;
29
29
  /** 开启了 --devtools-login */
30
30
  devtoolsLogin?: boolean;
31
+ /** 开发服务器提供 WebSocket 请求头转发;静态查看器未提供时保持直连 */
32
+ socketProxy?: boolean;
31
33
  /** false 表示没有开发服务器,不建立热更新和报错转发连接 */
32
34
  liveReload?: boolean;
33
35
  /** 使用查看器已经注册的 Service Worker,宿主不能用 sw.js 替换它 */
@@ -28,6 +28,8 @@ interface AppInfo {
28
28
  device: DeviceProfile;
29
29
  /** 开启了 --devtools-login */
30
30
  devtoolsLogin?: boolean;
31
+ /** 开发服务器提供 WebSocket 请求头转发;静态查看器未提供时保持直连 */
32
+ socketProxy?: boolean;
31
33
  /** false 表示没有开发服务器,不建立热更新和报错转发连接 */
32
34
  liveReload?: boolean;
33
35
  /** 使用查看器已经注册的 Service Worker,宿主不能用 sw.js 替换它 */
package/dist/setup.d.mts CHANGED
@@ -1 +1 @@
1
- export { K as KnownMocks, M as MockConfig, d as MockRequest, e as MockResponse, f as MockValue, N as NetworkConfig, R as RouteHandler, S as SetupApi, g as SetupConfig, h as SetupContext, i as SetupDefinition, k as SetupEnv, U as UserInfo, l as defineSetup } from './setup-D0ubZt2C.mjs';
1
+ export { K as KnownMocks, M as MockConfig, d as MockRequest, e as MockResponse, f as MockValue, N as NetworkConfig, R as RouteHandler, S as SetupApi, g as SetupConfig, h as SetupContext, i as SetupDefinition, k as SetupEnv, U as UserInfo, l as defineSetup } from './setup--EIOgQvG.mjs';
package/dist/setup.d.ts CHANGED
@@ -1 +1 @@
1
- export { K as KnownMocks, M as MockConfig, d as MockRequest, e as MockResponse, f as MockValue, N as NetworkConfig, R as RouteHandler, S as SetupApi, g as SetupConfig, h as SetupContext, i as SetupDefinition, k as SetupEnv, U as UserInfo, l as defineSetup } from './setup-D0ubZt2C.js';
1
+ export { K as KnownMocks, M as MockConfig, d as MockRequest, e as MockResponse, f as MockValue, N as NetworkConfig, R as RouteHandler, S as SetupApi, g as SetupConfig, h as SetupContext, i as SetupDefinition, k as SetupEnv, U as UserInfo, l as defineSetup } from './setup--EIOgQvG.js';
@@ -1,5 +1,5 @@
1
- import { a as WxaTestApi } from './testApi-x2Yph6M2.mjs';
2
- import './setup-D0ubZt2C.mjs';
1
+ import { a as WxaTestApi } from './testApi-CoYw0cPm.mjs';
2
+ import './setup--EIOgQvG.mjs';
3
3
 
4
4
  /**
5
5
  * window.__wxa_test_api__ 的全局类型声明,供测试代码按需引用:
@@ -1,5 +1,5 @@
1
- import { a as WxaTestApi } from './testApi-Df0ltBI6.js';
2
- import './setup-D0ubZt2C.js';
1
+ import { a as WxaTestApi } from './testApi-BfzCTOhg.js';
2
+ import './setup--EIOgQvG.js';
3
3
 
4
4
  /**
5
5
  * window.__wxa_test_api__ 的全局类型声明,供测试代码按需引用:
@@ -1,4 +1,4 @@
1
- import { d as MockRequest, j as FrameKind, a as ApiHandler, b as ApiResult, R as RouteHandler } from './setup-D0ubZt2C.js';
1
+ import { d as MockRequest, j as FrameKind, a as ApiHandler, b as ApiResult, R as RouteHandler } from './setup--EIOgQvG.js';
2
2
 
3
3
  /**
4
4
  * 网络 API:wx.request / downloadFile / uploadFile / connectSocket
@@ -1,4 +1,4 @@
1
- import { d as MockRequest, j as FrameKind, a as ApiHandler, b as ApiResult, R as RouteHandler } from './setup-D0ubZt2C.mjs';
1
+ import { d as MockRequest, j as FrameKind, a as ApiHandler, b as ApiResult, R as RouteHandler } from './setup--EIOgQvG.mjs';
2
2
 
3
3
  /**
4
4
  * 网络 API:wx.request / downloadFile / uploadFile / connectSocket
package/dist/web.d.mts CHANGED
@@ -21,6 +21,8 @@ interface AppInfo {
21
21
  device: DeviceProfile;
22
22
  /** 开启了 --devtools-login */
23
23
  devtoolsLogin?: boolean;
24
+ /** 开发服务器提供 WebSocket 请求头转发;静态查看器未提供时保持直连 */
25
+ socketProxy?: boolean;
24
26
  /** false 表示没有开发服务器,不建立热更新和报错转发连接 */
25
27
  liveReload?: boolean;
26
28
  /** 使用查看器已经注册的 Service Worker,宿主不能用 sw.js 替换它 */
@@ -96,6 +98,7 @@ interface BuildResult {
96
98
  * /__wxa__/usr/<会话>/<路径> 用户目录(USER_DATA_PATH)下的文件,image 等组件和 uploadFile 按地址读取
97
99
  * /__wxa__/region/<kind> 地区选择器用的行政区划数据(districts / streets),取自本机开发者工具
98
100
  * /__wxa__/api/* --devtools-login:wx.login 的 code、手机号,服务市场、插件的请求转发,只允许本机访问
101
+ * /__wxa__/socket 带自定义请求头的 WebSocket 转发,只接受同源连接
99
102
  * /__wxa__/ws 文件变化通知:热更新(hot)或整页刷新(reload);开启 forwardConsole 时浏览器从这里发来报错
100
103
  * middlewares / 扩展挂载的路径 排在 runner 自己的路由之前
101
104
  * 其余路径 小程序目录下的静态资源
package/dist/web.d.ts CHANGED
@@ -21,6 +21,8 @@ interface AppInfo {
21
21
  device: DeviceProfile;
22
22
  /** 开启了 --devtools-login */
23
23
  devtoolsLogin?: boolean;
24
+ /** 开发服务器提供 WebSocket 请求头转发;静态查看器未提供时保持直连 */
25
+ socketProxy?: boolean;
24
26
  /** false 表示没有开发服务器,不建立热更新和报错转发连接 */
25
27
  liveReload?: boolean;
26
28
  /** 使用查看器已经注册的 Service Worker,宿主不能用 sw.js 替换它 */
@@ -96,6 +98,7 @@ interface BuildResult {
96
98
  * /__wxa__/usr/<会话>/<路径> 用户目录(USER_DATA_PATH)下的文件,image 等组件和 uploadFile 按地址读取
97
99
  * /__wxa__/region/<kind> 地区选择器用的行政区划数据(districts / streets),取自本机开发者工具
98
100
  * /__wxa__/api/* --devtools-login:wx.login 的 code、手机号,服务市场、插件的请求转发,只允许本机访问
101
+ * /__wxa__/socket 带自定义请求头的 WebSocket 转发,只接受同源连接
99
102
  * /__wxa__/ws 文件变化通知:热更新(hot)或整页刷新(reload);开启 forwardConsole 时浏览器从这里发来报错
100
103
  * middlewares / 扩展挂载的路径 排在 runner 自己的路由之前
101
104
  * 其余路径 小程序目录下的静态资源
package/dist/web.js CHANGED
@@ -433,6 +433,26 @@ var FileStore = class {
433
433
  this.write(destPath, this.read(srcPath));
434
434
  return {};
435
435
  },
436
+ rename: ({ oldPath, newPath }) => {
437
+ const source = this.normalize(oldPath);
438
+ const target = this.normalize(newPath);
439
+ const node = this.existing(oldPath);
440
+ this.assertNotRoot(source);
441
+ this.assertNotRoot(target);
442
+ if (source === target) return {};
443
+ const parent = target.slice(0, target.lastIndexOf("/")) || "/";
444
+ if (!this.nodes.get(parent)?.dir) throw new Error(`no such file or directory ${newPath}`);
445
+ if (node.dir && target.startsWith(source + "/")) throw new Error("invalid argument: directory moved into itself");
446
+ const destination = this.nodes.get(target);
447
+ if (destination) {
448
+ if (destination.dir !== node.dir) throw new Error(`incompatible file type ${newPath}`);
449
+ if (destination.dir && this.readdir(target).length) throw new Error(`directory not empty ${newPath}`);
450
+ }
451
+ const entries = [...this.nodes].filter(([key]) => key === source || node.dir && key.startsWith(source + "/"));
452
+ for (const [key] of entries) this.nodes.delete(key);
453
+ for (const [key, value] of entries) this.nodes.set(target + key.slice(source.length), value);
454
+ return {};
455
+ },
436
456
  access: ({ path }) => {
437
457
  this.existing(path);
438
458
  return {};
package/dist/web.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/web.ts","../src/project/toJs.ts","../src/server/pluginCode.ts","../src/server/html.ts","../src/server/appRoutes.ts","../src/server/FileStore.ts","../src/runtime/networkPanel.ts","../src/server/proxyFetch.ts","../src/runtime/mockResponse.ts","../src/runtime/setupModule.ts"],"sourcesContent":["/**\n * wxa-runner-core/web:不依赖 Node 的公开接口,给在浏览器、Service Worker 或其他 Fetch API 运行时里托管小程序的宿主用\n * (如 @open-wxa/wxa-viewer)。开发服务器用的也是同一份实现,两边生成的页面和行为一致。\n */\n\nexport { DEFAULT_DEVICE, extname, mimeType, routeApp } from './server/appRoutes';\nexport type { AppSource, RouteResponse } from './server/appRoutes';\nexport { hostHtml } from './server/html';\nexport type { AppBuild, PluginCode } from './server/pluginCode';\nexport { FileStore } from './server/FileStore';\nexport { proxyFetch } from './server/proxyFetch';\nexport { mockedResponse } from './runtime/mockResponse';\nexport type { MockClient } from './runtime/mockResponse';\nexport { MOCK_ID_HEADER, MOCK_PREFIX, PROXY_PREFIX } from './runtime/networkPanel';\nexport { SETUP_MODULE, SETUP_MODULE_SOURCE } from './runtime/setupModule';\nexport type { BuildResult } from './project/AppBuilder';\nexport type { AppInfo, DeviceProfile } from './runtime/types';\n","/** JSON 序列化并转义 </script> 与行分隔符,结果可安全嵌入脚本。不依赖 Node:查看器的 Service Worker 也用它生成页面 */\nexport function toJs(value: unknown): string {\n return JSON.stringify(value)\n .replace(/</g, '\\\\u003c')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n}\n","/**\n * 插件代码的数据结构与页面查找,下载见 plugins.ts。\n * 不依赖 Node:查看器的 Service Worker 也按同样的结构生成页面\n */\n\nimport type { BuildResult } from '../project/AppBuilder';\n\nexport interface PluginCode {\n /** app.json 里的引用名,plugin://<alias>/页面名 */\n alias: string;\n /** 插件 appid */\n provider: string;\n version: string;\n /** 包内路径(/ 开头)→ 内容 */\n files: Map<string, Uint8Array>;\n /** 插件页面的路径,如 __plugin__/wx…/pages/index/index */\n pages: string[];\n /** 插件页面的配置,键同 __wxConfig.page(路径加 .html) */\n pageConfigs: Record<string, unknown>;\n}\n\n/** 构建结果加上用到的插件:插件页面并入 pages,配置并入 __wxConfig.page */\nexport interface AppBuild extends BuildResult {\n plugins: PluginCode[];\n}\n\n/** 插件页面并入 pages,配置并入 __wxConfig.page */\nexport function withPlugins(build: BuildResult, plugins: PluginCode[]): AppBuild {\n if (!plugins.length) return { ...build, plugins };\n return {\n ...build,\n plugins,\n pages: [...build.pages, ...plugins.flatMap(plugin => plugin.pages)],\n wxConfig: {\n ...build.wxConfig,\n page: { ...build.wxConfig.page, ...Object.assign({}, ...plugins.map(plugin => plugin.pageConfigs)) }\n }\n };\n}\n\n/** 插件页面所属的插件,以及页面在插件包里的路径(不带扩展名) */\nexport function findPluginPage(build: AppBuild, pagePath: string): { plugin: PluginCode; innerPath: string } | null {\n for (const plugin of build.plugins) {\n const prefix = `__plugin__/${plugin.provider}/`;\n if (plugin.pages.includes(pagePath)) return { plugin, innerPath: pagePath.slice(prefix.length) };\n }\n return null;\n}\n\nexport function pluginScriptUrl(plugin: PluginCode, file: 'appservice.js' | 'pageframe.js'): string {\n return `/__wxa__/plugin/${encodeURIComponent(plugin.provider)}/${encodeURIComponent(plugin.version)}/${file}`;\n}\n","/**\n * 宿主、逻辑层、渲染层的 HTML,以及 Worker 的入口脚本\n *\n * 不依赖 Node:查看器的 Service Worker 也用这些函数生成页面\n */\n\nimport type { BuildResult } from '../project/AppBuilder';\nimport { toJs } from '../project/toJs';\nimport type { WebviewScript } from './DevServer';\nimport { AppBuild, PluginCode, pluginScriptUrl } from './pluginCode';\n\nexport function hostHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>wxa-runner</title>\n</head>\n<body>\n <div id=\"wxa-root\"></div>\n <script src=\"/__wxa__/runtime/host.js\"></script>\n</body>\n</html>`;\n}\n\n/**\n * 打开基础库的热更新开关(runtime/host/hmr.ts 依赖它):Page / Component / 模板注册时登记更新回调。\n * 必须紧跟在基础库之后、业务代码注册之前执行;只在监听文件变化时打开\n */\nfunction enableHotUpdate(hot: boolean): string {\n return hot\n ? `<script>if (typeof __virtualDOM__ !== 'undefined' && __virtualDOM__.enableUpdateWxAppCode) __virtualDOM__.enableUpdateWxAppCode();</script>\\n`\n : '';\n}\n\n/**\n * isolated:新版基础库(3.7.1 起)。逻辑层 iframe 只是主上下文,业务代码由基础库通过 WeixinJSContext\n * 放进子上下文执行(见 runtime/context.ts),逻辑层就绪的通知也由那边发出\n */\nexport function appserviceHtml(\n build: AppBuild,\n launch: { path: string; query: Record<string, string> },\n options: { hot: boolean; isolated?: boolean }\n): string {\n const wxConfig = {\n ...build.wxConfig,\n appLaunchInfo: { scene: 1001, path: launch.path, query: launch.query },\n ...(options.isolated ? { isIsolateContext: true } : {})\n };\n const v = build.version;\n // 插件在业务代码之前注册:业务代码执行时就能 requirePlugin、跳转插件页面。\n // 插件脚本也排在业务模块的 define 之前,与开发者工具一致:插件初始化时调用 requireMiniProgram() 会因导出模块还没 define 而失败,开发者工具里同样如此\n const pluginUrls = build.plugins.map(plugin => pluginScriptUrl(plugin, 'appservice.js'));\n const moduleUrls = build.modules.map(id => `/__wxa__/module/${encodeURI(id)}?v=${v}`);\n const bootstrapUrl = `/__wxa__/service-bootstrap.js?v=${v}`;\n\n if (options.isolated) {\n const scripts = [...pluginUrls, ...moduleUrls, bootstrapUrl];\n return `<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<title>appservice</title>\n<script>var __wxConfig = ${toJs(wxConfig)};</script>\n<script src=\"/__wxa__/runtime/frame.js\" data-kind=\"service\" data-app-service=\"${escapeHtml(JSON.stringify(scripts))}\"${options.hot ? ' data-hot=\"1\"' : ''}></script>\n<script src=\"/__baselib__/WAServiceMainContext.js\"></script>\n</head>\n<body></body>\n</html>`;\n }\n\n const modules = [...pluginUrls, ...moduleUrls].map(src => `<script src=\"${src}\"></script>`).join('\\n');\n return `<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<title>appservice</title>\n<script>var __wxConfig = ${toJs(wxConfig)};</script>\n<script src=\"/__wxa__/runtime/frame.js\" data-kind=\"service\"></script>\n<script src=\"/__baselib__/WAService.js\"></script>\n${enableHotUpdate(options.hot)}<script>\nif (typeof define !== 'function') {\n console.error('[wxa-runner] 基础库没有完成初始化(define 不存在),当前基础库版本可能不受支持');\n}\n</script>\n${modules}\n<script src=\"${bootstrapUrl}\"></script>\n<script>parent.__wxaHost.frameLoaded('service', 0);</script>\n</head>\n<body></body>\n</html>`;\n}\n\n/**\n * 主上下文通过 WeixinJSContext 创建的子上下文(runtime/context.ts)。\n * empty 对应 allocEmpty:不预先加载 WASubContext.js,由基础库随后 loadLibFiles\n */\nexport function subcontextHtml(id: number, empty: boolean): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<title>subcontext ${id}</title>\n<script src=\"/__wxa__/runtime/frame.js\" data-kind=\"service\" data-context-id=\"${id}\"></script>\n${empty ? '' : '<script src=\"/__baselib__/WASubContext.js\"></script>\\n'}<script>parent.__wxaContexts.loaded(${id});</script>\n</head>\n<body></body>\n</html>`;\n}\n\nexport function webviewHtml(build: AppBuild, pagePath: string, webviewId: number, options: { hot: boolean; scripts?: WebviewScript[] }): string {\n const id = Number(webviewId) || 0;\n return `<!DOCTYPE html>\n<html>\n<head>\n${webviewHead(build, pagePath, id, options.scripts)}\n${enableHotUpdate(options.hot)}<script src=\"/__wxa__/page-frame.js?v=${build.version}\"></script>\n${pageWindow(build, pagePath).disableScroll ? '<style>body{overflow-y:hidden}</style>' : ''}\n</head>\n<body>\n${scriptTags(options.scripts, 'body')}<script>\n(function () {\n var path = ${toJs(pagePath)};\n if (typeof __wxAppCode__[path + '.wxss'] === 'function') __wxAppCode__[path + '.wxss']();\n var generateFunc = $gwx('./' + path + '.wxml');\n if (generateFunc) {\n document.dispatchEvent(new CustomEvent('generateFuncReady', { detail: { generateFunc: generateFunc } }));\n } else {\n document.body.innerText = './' + path + '.wxml not found';\n console.error('[wxa-runner] ./' + path + '.wxml not found');\n }\n parent.__wxaHost.frameLoaded('webview', ${id});\n})();\n</script>\n</body>\n</html>`;\n}\n\n/**\n * Worker 的入口(/__wxa__/worker.js?id=),由逻辑层的原生 WeixinWorker 创建(runtime/workers.ts)。\n * 和开发者工具一样先放 __wxConfig 和原生接口(runtime/worker.ts),再加载 Worker 基础库和 workers 目录下的模块;\n * 模块逐个加载,DevTools 里保留原文件名和行号。\n * Worker 基础库把 console 换成转发给逻辑层的版本,转发时对象被转成文本;开发者工具加载完基础库后换回原来的,日志留在 Worker 里。\n * 基础库目录里没有 WAWorker.js 时(如只放了逻辑层和渲染层文件的旧版内置基础库),直接抛出异常说明缺少的文件,由逻辑层的 Worker 对象记入 errors()(runtime/workers.ts)\n */\nexport function workerScript(build: BuildResult, baseLib: { version: string; hasWorker: boolean }): string {\n const modules = build.workerModules.map(id => `/__wxa__/module/${encodeURI(id)}?v=${build.version}`);\n const prelude = `var __wxConfig = ${toJs(build.wxConfig)}; __wxConfig.env.USER_DATA_PATH = location.origin + \"/__wxa__/usr/\" + new URL(location.href).searchParams.get(\"session\");\nimportScripts('/__wxa__/runtime/worker.js');\n`;\n if (!baseLib.hasWorker) {\n const message = `[wxa-runner] 基础库 ${baseLib.version} 里没有 WAWorker.js,Worker 不能运行。换用开发者工具下载的基础库,或升级 wxa-runner 带上新的内置基础库`;\n return `${prelude}throw new Error(${toJs(message)});\n`;\n }\n return `${prelude}var __wxaConsole = self.console;\nimportScripts('/__baselib__/WAWorker.js');\nself.console = __wxaConsole;\nimportScripts(${modules.map(url => toJs(url)).join(', ')});\n`;\n}\n\n/**\n * 插件页面:只加载插件自己的模板和样式(pageframe.js),不加载小程序的,与开发者工具一致。\n * 页面 html 是插件包里编译好的片段,自己注入样式、取模板并派发 generateFuncReady\n */\nexport function pluginWebviewHtml(\n build: AppBuild,\n plugin: PluginCode,\n innerPath: string,\n pagePath: string,\n webviewId: number,\n options: { scripts?: WebviewScript[] } = {}\n): string {\n const id = Number(webviewId) || 0;\n const html = plugin.files.get(`/${innerPath}.html`);\n const page = html ? new TextDecoder().decode(html) : `<script>console.error(${JSON.stringify(`[wxa-runner] 插件 ${plugin.alias} 里没有页面 ${innerPath}`)})</script>`;\n return `<!DOCTYPE html>\n<html>\n<head>\n${webviewHead(build, pagePath, id, options.scripts)}\n<script src=\"${pluginScriptUrl(plugin, 'pageframe.js')}\"></script>\n${pageWindow(build, pagePath).disableScroll ? '<style>body{overflow-y:hidden}</style>' : ''}\n</head>\n<body>\n${scriptTags(options.scripts, 'body')}${page}\n<script>parent.__wxaHost.frameLoaded('webview', ${id});</script>\n</body>\n</html>`;\n}\n\nfunction pageWindow(build: AppBuild, pagePath: string): Record<string, any> {\n return { ...build.wxConfig.global?.window, ...build.wxConfig.page?.[pagePath + '.html']?.window };\n}\n\n/** 渲染层公共的 head:页面配置、桥和基础库;position 为 head 的注入脚本在桥之后、基础库之前(body 的见 WebviewScript) */\nfunction webviewHead(build: AppBuild, pagePath: string, id: number, scripts?: WebviewScript[]): string {\n const wxConfig = { ...build.wxConfig, window: pageWindow(build, pagePath) };\n return `<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">\n<title>${escapeHtml(pagePath)}</title>\n<!-- 小程序渲染层的默认字号是 16px;不声明的话会继承浏览器设置里的默认字号。放在最前面,基础库和页面样式都能覆盖它 -->\n<style>html{font-size:16px}</style>\n<!-- 真机的滚动条是不占布局的浮层;桌面浏览器的常规滚动条和自定义 ::-webkit-scrollbar 会挤窄页面,一律隐藏。\n scrollbar-width 优先于 ::-webkit-scrollbar,页面样式不会再把滚动条加回来 -->\n<style>*{scrollbar-width:none}::-webkit-scrollbar{display:none}</style>\n<!-- 逻辑层建的 2d、webgl 画布搬进 wx-canvas 后铺满它(见 frame.ts 的 moveCanvasesIntoPages)。\n 基础库还会按逻辑层里的布局改它的位置和显隐,这里一律压住;触摸照常交给 wx-canvas 自己的 canvas -->\n<style>.wxa-service-canvas{position:absolute!important;left:0!important;top:0!important;width:100%!important;height:100%!important;visibility:inherit!important;pointer-events:none}</style>\n<script>var __wxConfig = ${toJs(wxConfig)};</script>\n<script src=\"/__wxa__/runtime/frame.js\" data-kind=\"webview\" data-webview-id=\"${id}\"></script>\n${scriptTags(scripts, 'head')}<script src=\"/__baselib__/WAWebview.js\"></script>`;\n}\n\n/** 注入脚本的 <script> 标签,每个一行;内联内容里的 </script 转义,不会提前结束标签 */\nfunction scriptTags(scripts: WebviewScript[] | undefined, position: 'head' | 'body'): string {\n return (scripts ?? [])\n .filter(script => (script.position ?? 'body') === position)\n .map(script => script.src !== undefined\n ? `<script src=\"${escapeHtml(script.src)}\"></script>\\n`\n : `<script>${script.content.replace(/<\\/script/gi, '<\\\\/script')}</script>\\n`)\n .join('');\n}\n\nfunction escapeHtml(text: string): string {\n return text.replace(/[&<>\"]/g, ch => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;' }[ch]!));\n}\n","/**\n * 小程序页面与代码的路由:逻辑层、子上下文、渲染层的 HTML,业务模块、模板、样式和插件代码\n *\n * 开发服务器和查看器(wxapack)的 Service Worker 共用这一份,两边生成的页面才不会走样,所以不依赖 Node。\n * app-info、setup、静态资源、文件接口等和运行环境有关的路由各自实现。\n */\n\nimport type { DeviceProfile } from '../runtime/types';\nimport type { WebviewScript } from './DevServer';\nimport { appserviceHtml, pluginWebviewHtml, subcontextHtml, webviewHtml, workerScript } from './html';\nimport { AppBuild, findPluginPage } from './pluginCode';\n\nexport const DEFAULT_DEVICE: DeviceProfile = {\n model: 'iPhone X',\n screenWidth: 375,\n screenHeight: 812,\n pixelRatio: 3,\n statusBarHeight: 44,\n safeAreaBottom: 34,\n system: 'iOS 16.0',\n platform: 'devtools'\n};\n\nexport interface AppSource {\n build: AppBuild;\n /** 业务模块的 define 脚本,id 已确认在 modules 或 workerModules 里 */\n moduleScript(id: string): string;\n baseLib: { version: string; hasWorker: boolean };\n /** 新版基础库的隔离上下文,见 html.ts 的 appserviceHtml */\n isolated: boolean;\n /** 打开基础库的热更新开关 */\n hot: boolean;\n webviewScripts?: WebviewScript[];\n}\n\nexport interface RouteResponse {\n status: number;\n /** express 的 res.type() 能识别的写法:'html'、'js'、'text' 或带点的扩展名;在 Service Worker 里用 mimeType() 换成 Content-Type */\n type: string;\n body: string | Uint8Array;\n}\n\nconst PLUGIN_SCRIPT = /^\\/__wxa__\\/plugin\\/([^/]+)\\/([^/]+)\\/(appservice\\.js|pageframe\\.js)$/;\n\n/** @returns 不归这里管、或请求的页面和文件不存在时为 null,交给后面的路由 */\nexport function routeApp(source: AppSource, url: URL): RouteResponse | null {\n const { build } = source;\n const pathname = safeDecode(url.pathname);\n if (pathname === null) return null;\n const query = url.searchParams;\n\n switch (pathname) {\n case '/__wxa__/appservice.html': {\n const launchPath = (query.get('path') || '').replace(/^\\//, '');\n const launch = build.pages.includes(launchPath)\n ? { path: launchPath, query: Object.fromEntries(new URLSearchParams(query.get('query') || '')) }\n : { path: build.pages[0], query: {} };\n return html(appserviceHtml(build, launch, { hot: source.hot, isolated: source.isolated }));\n }\n case '/__wxa__/subcontext.html': {\n const id = Number(query.get('id'));\n if (!Number.isInteger(id) || id <= 0) return { status: 400, type: 'text', body: '子上下文 id 无效' };\n return html(subcontextHtml(id, query.get('empty') === '1'));\n }\n case '/__wxa__/worker.js':\n return js(workerScript(build, source.baseLib));\n case '/__wxa__/service-bootstrap.js':\n return js(build.serviceBootstrap);\n case '/__wxa__/service-templates.js':\n return js(build.serviceTemplates);\n case '/__wxa__/page-templates.js':\n return js(build.pageTemplates);\n case '/__wxa__/page-styles.js':\n return js(build.pageStyles);\n case '/__wxa__/page-frame.js':\n return js(build.pageFrame);\n }\n\n const webview = /^\\/__wxa__\\/webview\\/(.+)\\.html$/.exec(pathname);\n if (webview) {\n const pagePath = webview[1];\n if (!build.pages.includes(pagePath)) return null;\n const webviewId = Number(query.get('id'));\n const pluginPage = findPluginPage(build, pagePath);\n return html(pluginPage\n ? pluginWebviewHtml(build, pluginPage.plugin, pluginPage.innerPath, pagePath, webviewId, { scripts: source.webviewScripts })\n : webviewHtml(build, pagePath, webviewId, { hot: source.hot, scripts: source.webviewScripts }));\n }\n\n // 插件的逻辑层、渲染层代码\n const pluginScript = PLUGIN_SCRIPT.exec(pathname);\n if (pluginScript) {\n const [, provider, version, file] = pluginScript;\n const code = build.plugins.find(plugin => plugin.provider === provider && plugin.version === version)?.files.get(`/${file}`);\n return code ? { status: 200, type: 'js', body: code } : null;\n }\n // 插件页面里的图片等资源:基础库把插件里的相对路径和 / 开头的路径都改写成站点根下的 /__plugin__/<插件 appid>/…\n const pluginFile = /^\\/__plugin__\\/([^/]+)(\\/.+)$/.exec(pathname);\n if (pluginFile) {\n const file = build.plugins.find(plugin => plugin.provider === pluginFile[1])?.files.get(pluginFile[2]);\n return file ? { status: 200, type: extname(pluginFile[2]) || 'application/octet-stream', body: file } : null;\n }\n\n const module = /^\\/__wxa__\\/module\\/(.+\\.js)$/.exec(pathname);\n if (module) {\n const id = module[1];\n if (!build.modules.includes(id) && !build.workerModules.includes(id)) {\n return { status: 404, type: 'js', body: `console.error(${JSON.stringify(`[wxa-runner] 模块 ${id} 不存在`)})` };\n }\n return js(source.moduleScript(id));\n }\n return null;\n}\n\nconst MIME_TYPES: Record<string, string> = {\n html: 'text/html; charset=utf-8',\n js: 'application/javascript; charset=utf-8',\n text: 'text/plain; charset=utf-8',\n json: 'application/json; charset=utf-8',\n css: 'text/css; charset=utf-8',\n wxss: 'text/plain; charset=utf-8',\n txt: 'text/plain; charset=utf-8',\n svg: 'image/svg+xml',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n bmp: 'image/bmp',\n ico: 'image/x-icon',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n aac: 'audio/aac',\n m4a: 'audio/mp4',\n ogg: 'audio/ogg',\n mp4: 'video/mp4',\n webm: 'video/webm',\n mov: 'video/quicktime',\n woff: 'font/woff',\n woff2: 'font/woff2',\n ttf: 'font/ttf',\n otf: 'font/otf',\n wasm: 'application/wasm'\n};\n\n/** RouteResponse.type 或文件扩展名对应的 Content-Type;认不出的按二进制处理 */\nexport function mimeType(type: string): string {\n if (type.includes('/')) return type;\n return MIME_TYPES[type.replace(/^\\./, '').toLowerCase()] ?? 'application/octet-stream';\n}\n\n/** 带点的扩展名,没有时为空串 */\nexport function extname(filePath: string): string {\n const match = /\\.[^./]+$/.exec(filePath);\n return match ? match[0] : '';\n}\n\nfunction html(body: string): RouteResponse {\n return { status: 200, type: 'html', body };\n}\n\nfunction js(body: string): RouteResponse {\n return { status: 200, type: 'js', body };\n}\n\n/** 地址里有单独的 %(如 /discount/50%)时 decodeURIComponent 会抛错,这种地址不归这里管 */\nfunction safeDecode(pathname: string): string | null {\n try {\n return decodeURIComponent(pathname);\n } catch {\n return null;\n }\n}\n","/** 文件 API、下载、上传和资源 URL 共用这些字节;不依赖 Node,以便 Service Worker 也能直接使用。 */\n\nlet storeSequence = 0;\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder('utf-8', { ignoreBOM: true });\n\nfunction normalizeEncoding(encoding: string): string {\n switch (String(encoding).toLowerCase()) {\n case 'utf8':\n case 'utf-8':\n return 'utf8';\n case 'utf16le':\n case 'utf-16le':\n case 'ucs2':\n case 'ucs-2':\n return 'utf16le';\n case 'latin1':\n case 'binary':\n return 'latin1';\n case 'ascii':\n return 'ascii';\n case 'base64':\n case 'base64url':\n return String(encoding).toLowerCase();\n case 'hex':\n return 'hex';\n default:\n throw new Error(`Unknown encoding: ${encoding}`);\n }\n}\n\nfunction encodeText(text: string, encoding: string): Uint8Array {\n const format = normalizeEncoding(encoding);\n if (format === 'utf8') return textEncoder.encode(text);\n if (format === 'base64' || format === 'base64url') {\n // 文件接口沿用宽松解析:接受 URL 字母表、忽略杂字符,遇到填充符即停止。\n let base64 = text\n .split('=', 1)[0]\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n .replace(/[^A-Za-z0-9+/]/g, '');\n if (base64.length % 4 === 1) base64 = base64.slice(0, -1);\n base64 = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n return Uint8Array.from(atob(base64), char => char.charCodeAt(0));\n }\n if (format === 'hex') {\n const bytes: number[] = [];\n for (let index = 0; index + 1 < text.length; index += 2) {\n const pair = text.slice(index, index + 2);\n if (!/^[\\da-f]{2}$/i.test(pair)) break;\n bytes.push(parseInt(pair, 16));\n }\n return new Uint8Array(bytes);\n }\n const bytes = new Uint8Array(text.length * (format === 'utf16le' ? 2 : 1));\n for (let index = 0; index < text.length; index++) {\n const code = text.charCodeAt(index);\n if (format === 'utf16le') {\n bytes[index * 2] = code & 0xff;\n bytes[index * 2 + 1] = code >>> 8;\n } else bytes[index] = code & 0xff;\n }\n return bytes;\n}\n\nfunction decodeText(bytes: Uint8Array, encoding: string): string {\n const format = normalizeEncoding(encoding);\n if (format === 'utf8') return textDecoder.decode(bytes);\n if (format === 'hex') return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');\n const chunks: string[] = [];\n // 分块转换避免大文件超出函数参数个数限制;UTF-16 按码元读取以保留孤立代理项。\n for (let offset = 0; offset < bytes.length; offset += 0x8000) {\n const chunk = bytes.subarray(offset, offset + 0x8000);\n const codes: number[] = [];\n for (let index = 0; index < chunk.length; index++) {\n if (format === 'utf16le') {\n if (index + 1 < chunk.length) codes.push(chunk[index] | (chunk[++index] << 8));\n } else codes.push(format === 'ascii' ? chunk[index] & 0x7f : chunk[index]);\n }\n chunks.push(String.fromCharCode(...codes));\n }\n const text = chunks.join('');\n if (format === 'base64') return btoa(text);\n if (format === 'base64url') return btoa(text).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n return text;\n}\n\ninterface FsNode {\n dir: boolean;\n data: Uint8Array;\n mtime: number;\n etag: string;\n}\n\n/** 基础库会往 http://usr/ 写运行日志;业务代码也可以通过 FileSystemManager 读写 */\nexport class FileStore {\n private readonly etagPrefix =\n globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${++storeSequence}-${Math.random().toString(36).slice(2)}`;\n private version = 0;\n private nodes = new Map<string, FsNode>([['/', { dir: true, data: new Uint8Array(0), mtime: Date.now(), etag: '' }]]);\n private readonly roots: string[];\n\n /** @param roots 预先创建的目录(如用户目录)。rmdir 不能删除它们和它们的上级目录:删掉之后再写 USER_DATA_PATH 都会失败 */\n constructor(roots: string[] = []) {\n this.roots = roots.map(root => this.normalize(root));\n for (const root of this.roots) this.mkdir(root, true);\n }\n\n normalize(filePath: string): string {\n const stripped = String(filePath || '').replace(/^[a-z]+:\\/\\/[^/]*/i, '');\n const parts: string[] = [];\n for (const part of stripped.split('/')) {\n if (!part || part === '.') continue;\n if (part === '..') parts.pop();\n else parts.push(part);\n }\n return '/' + parts.join('/');\n }\n\n stat(filePath: string): FsNode | undefined {\n return this.nodes.get(this.normalize(filePath));\n }\n\n mkdir(dirPath: string, recursive = false) {\n const target = this.normalize(dirPath);\n if (this.nodes.has(target)) throw new Error(`file already exists ${dirPath}`);\n const parent = target.slice(0, target.lastIndexOf('/')) || '/';\n if (!this.nodes.get(parent)?.dir) {\n if (!recursive) throw new Error(`no such file or directory ${dirPath}`);\n this.mkdir(parent, true);\n }\n this.nodes.set(target, { dir: true, data: new Uint8Array(0), mtime: Date.now(), etag: '' });\n }\n\n write(filePath: string, input: string | Uint8Array, append = false) {\n const data = typeof input === 'string' ? textEncoder.encode(input) : new Uint8Array(input);\n const target = this.normalize(filePath);\n const parent = target.slice(0, target.lastIndexOf('/')) || '/';\n if (!this.nodes.get(parent)?.dir) throw new Error(`no such file or directory ${filePath}`);\n const existing = this.nodes.get(target);\n if (existing?.dir) throw new Error(`illegal operation on a directory ${filePath}`);\n let contents = data;\n if (append && existing) {\n contents = new Uint8Array(existing.data.length + data.length);\n contents.set(existing.data);\n contents.set(data, existing.data.length);\n }\n this.nodes.set(target, { dir: false, data: contents, mtime: Date.now(), etag: `${this.etagPrefix}-${++this.version}` });\n }\n\n read(filePath: string): Uint8Array {\n const node = this.stat(filePath);\n if (!node) throw new Error(`no such file or directory, open ${filePath}`);\n if (node.dir) throw new Error(`illegal operation on a directory ${filePath}`);\n return node.data;\n }\n\n readdir(dirPath: string): string[] {\n const target = this.normalize(dirPath);\n if (!this.nodes.get(target)?.dir) throw new Error(`no such file or directory ${dirPath}`);\n const prefix = target === '/' ? '/' : target + '/';\n return [...this.nodes.keys()]\n .filter(key => key.startsWith(prefix) && key !== target && !key.slice(prefix.length).includes('/'))\n .map(key => key.slice(prefix.length));\n }\n\n remove(filePath: string, options: { dir: boolean; recursive?: boolean }) {\n const target = this.normalize(filePath);\n const node = this.nodes.get(target);\n if (!node) throw new Error(`no such file or directory ${filePath}`);\n if (node.dir !== options.dir) throw new Error(options.dir ? `not a directory ${filePath}` : `operation not permitted ${filePath}`);\n if (node.dir) {\n const children = this.readdir(target);\n if (children.length && !options.recursive) throw new Error(`directory not empty ${filePath}`);\n for (const key of [...this.nodes.keys()]) if (key.startsWith(target + '/')) this.nodes.delete(key);\n }\n this.nodes.delete(target);\n }\n\n /** 文件 API 经开发服务器调用的操作,参数和返回值的字段名与基础库一致 */\n execute(operation: string, args: any): Record<string, any> {\n if (!Object.prototype.hasOwnProperty.call(this.operations, operation)) {\n throw new Error(`unsupported filesystem operation ${operation}`);\n }\n return this.operations[operation](args);\n }\n\n private readonly operations: Record<string, (args: any) => Record<string, any>> = {\n readFile: ({ filePath, encoding }) => {\n const data = this.read(filePath);\n return encoding === undefined ? { data: decodeText(data, 'base64'), binary: true } : { data: decodeText(data, encoding) };\n },\n writeFile: args => this.writeEncoded(args, false),\n appendFile: args => this.writeEncoded(args, true),\n copyFile: ({ srcPath, destPath }) => {\n this.write(destPath, this.read(srcPath));\n return {};\n },\n access: ({ path }) => {\n this.existing(path);\n return {};\n },\n stat: ({ path }) => {\n const node = this.existing(path);\n const stats = {\n mode: node.dir ? 0o40755 : 0o100644,\n size: node.data.length,\n lastAccessedTime: Math.floor(node.mtime / 1000),\n lastModifiedTime: Math.floor(node.mtime / 1000)\n };\n return { stats, ...stats };\n },\n getFileInfo: ({ filePath }) => ({ size: this.read(filePath).length }),\n mkdir: ({ dirPath, recursive }) => {\n this.mkdir(dirPath, !!recursive);\n return {};\n },\n readdir: ({ dirPath }) => ({ files: this.readdir(dirPath) }),\n unlink: ({ filePath }) => {\n this.remove(filePath, { dir: false });\n return {};\n },\n rmdir: ({ dirPath, recursive }) => {\n this.assertNotRoot(dirPath);\n this.remove(dirPath, { dir: true, recursive: !!recursive });\n return {};\n }\n };\n\n private writeEncoded(args: { filePath: string; data?: string; binary?: boolean; encoding?: string }, append: boolean) {\n this.write(args.filePath, encodeText(args.data ?? '', args.binary ? 'base64' : args.encoding || 'utf8'), append);\n return {};\n }\n\n private existing(filePath: string): FsNode {\n const node = this.stat(filePath);\n if (!node) throw new Error(`no such file or directory ${filePath}`);\n return node;\n }\n\n private assertNotRoot(dirPath: string) {\n const target = this.normalize(dirPath);\n const prefix = target === '/' ? '/' : target + '/';\n if (this.roots.some(root => root === target || root.startsWith(prefix))) {\n throw new Error(`operation not permitted ${dirPath}`);\n }\n }\n}\n","/**\n * 小程序的请求在浏览器 Network 面板里的样子,宿主、Service Worker 和开发服务器共用\n *\n * 原地址放在路径里:/__wxa__/proxy/https/api.example.com/user/info?id=1。Name 列显示接口名(info?id=1),\n * 按域名、路径过滤也直接可用;放在查询参数里时每一行都是 proxy?url=https%3A%2F%2F…,分不出是哪个接口。\n *\n * - 经开发服务器转发的请求:/__wxa__/proxy/…\n * - 被拦截规则拦下的请求:/__wxa__/mock/…,由 Service Worker 返回规则给出的响应,只用于显示(见 sw.ts)\n */\n\nimport type { MockedResponse } from './host/api/routes';\n\nexport const PROXY_PREFIX = '/__wxa__/proxy/';\nexport const MOCK_PREFIX = '/__wxa__/mock/';\n\n/** 显示被拦截请求的 fetch 带上这个头,Service Worker 凭它向宿主要对应的响应 */\nexport const MOCK_ID_HEADER = 'x-wxa-mock-id';\n\n/** Service Worker 向宿主要响应时发的消息,附带一个 MessagePort,宿主从这个端口回复 MockReply */\nexport interface MockQuery {\n type: 'wxa-mock-response';\n id: string;\n}\n\n/** 请求被取消、拦截规则出错,或宿主已经不认识这个 id 时为 null,Network 面板里显示为失败 */\nexport type MockReply = MockedResponse | null;\n\n/**\n * 原地址在开发服务器上对应的路径,prefix 是 PROXY_PREFIX 或 MOCK_PREFIX。不是 http(s) 地址时返回 null\n *\n * 先按 URL 规范化:路径里的 .. 要在拼接前解析掉,否则浏览器会在代理路径上解析,把域名那一段也吃掉\n */\nexport function toServerPath(prefix: string, url: string): string | null {\n let target: URL;\n try {\n target = new URL(url);\n } catch {\n return null;\n }\n if (target.protocol !== 'http:' && target.protocol !== 'https:') return null;\n return `${prefix}${target.protocol.slice(0, -1)}/${target.host}${target.pathname}${target.search}`;\n}\n\n/**\n * toServerPath 的逆运算,格式不对或还原出的地址不合法时返回 null\n *\n * 只校验不规范化,返回拼出的原样字符串:路径里的 %2F 等编码要原样转发\n */\nexport function fromServerPath(prefix: string, serverPath: string): string | null {\n if (!serverPath.startsWith(prefix)) return null;\n const match = /^(https?)\\/([^/?#]+)(.*)$/.exec(serverPath.slice(prefix.length));\n if (!match) return null;\n const url = `${match[1]}://${match[2]}${match[3]}`;\n try {\n new URL(url);\n return url;\n } catch {\n return null;\n }\n}\n\n/**\n * 用于显示的请求、响应头。浏览器不允许设置的(cookie、set-cookie 等)会被忽略,\n * 名字或值不合法的(如值里有中文)跳过:它们只影响 Network 面板里的显示,小程序拿到的头不受影响。\n * 值为 undefined、null 的和代理一样不发\n */\nexport function toHeaders(header: Record<string, unknown>): Headers {\n const headers = new Headers();\n for (const [name, value] of Object.entries(header)) {\n if (value === undefined || value === null) continue;\n try {\n headers.set(name, String(value));\n } catch {\n // 跳过\n }\n }\n return headers;\n}\n","/**\n * 网络代理:浏览器端的 wx.request 经由这里转发到真实服务器\n *\n * - 原地址写在路径里:/__wxa__/proxy/https/api.example.com/a?b=1(见 networkPanel.ts)\n * - 绕开 CORS\n * - 小程序设置的请求头放在 x-wxa-headers(JSON)里传过来,\n * 这样 cookie / referer / user-agent 等浏览器禁止设置的头也能带上\n * - 响应头和 set-cookie 以 URL 编码的 JSON 返回,浏览器端可以完整读取\n *\n * 只用标准 Fetch API:开发服务器(proxy.ts)和其他支持 Fetch API 的运行时共用。\n */\n\nimport { fromServerPath, PROXY_PREFIX } from '../runtime/networkPanel';\n\n// fetch 不允许或无意义的请求头\nconst DROPPED_REQUEST_HEADERS = new Set(['host', 'connection', 'content-length', 'transfer-encoding', 'keep-alive', 'upgrade', 'expect']);\n// 响应体已被解压/重新分块,这些头不再成立\nconst DROPPED_RESPONSE_HEADERS = new Set(['content-encoding', 'content-length', 'transfer-encoding', 'connection', 'set-cookie']);\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\n\nexport async function proxyFetch(\n request: Request,\n options: { fetch?: typeof fetch; isAllowed?: (url: URL) => boolean } = {}\n): Promise<Response> {\n const url = new URL(request.url);\n // pathname 保留 %2F 等编码,解码会改变上游地址。\n const path = url.pathname + url.search;\n const target = fromServerPath(PROXY_PREFIX, path);\n if (!target) return proxyError(400, `invalid url \"${path}\"`);\n\n try {\n let current = new URL(target);\n if (options.isAllowed && !options.isAllowed(current)) return forbiddenHost(current, 0);\n const headers = new Headers(\n forwardedRequestHeaders(request.headers.get('x-wxa-headers') ?? undefined, request.headers.get('content-type') ?? '')\n );\n let method = request.method;\n // 手动重定向时 307/308 需要重发同一个请求体;响应体始终直接透传。\n let body = request.body && !['GET', 'HEAD'].includes(method) ? await request.arrayBuffer() : undefined;\n const send = options.fetch ?? fetch;\n let upstream: Response;\n for (let hop = 0; ; hop++) {\n upstream = await send(current.href, {\n method,\n headers,\n body,\n redirect: options.isAllowed ? 'manual' : 'follow',\n signal: request.signal\n });\n const location = upstream.headers.get('location');\n if (!options.isAllowed || !REDIRECT_STATUSES.has(upstream.status) || location === null) break;\n await upstream.body?.cancel();\n const next = new URL(location, current);\n if (hop >= 20) return proxyError(403, `host \"${next.hostname}\": redirect hop ${hop + 1} exceeds limit 20`);\n if (!['http:', 'https:'].includes(next.protocol) || !options.isAllowed(next)) return forbiddenHost(next, hop + 1);\n if (\n (upstream.status === 303 && method !== 'GET' && method !== 'HEAD') ||\n ((upstream.status === 301 || upstream.status === 302) && method === 'POST')\n ) {\n method = 'GET';\n body = undefined;\n for (const name of ['content-type', 'content-encoding', 'content-language', 'content-location']) headers.delete(name);\n }\n // 跨源跳转不能把原站点凭据交给另一个白名单域名。\n if (next.origin !== current.origin) {\n for (const name of ['authorization', 'proxy-authorization', 'cookie']) headers.delete(name);\n }\n current = next;\n }\n return relayResponse(upstream);\n } catch (error) {\n return proxyError(502, fetchErrorMessage(error));\n }\n}\n\nfunction forbiddenHost(url: URL, hop: number): Response {\n return proxyError(403, `host \"${url.hostname}\" is not in the allowlist (redirect hop ${hop})`);\n}\n\nfunction proxyError(status: number, message: string): Response {\n return new Response(message, { status, headers: { 'x-wxa-proxy-error': '1', 'content-type': 'text/html; charset=utf-8' } });\n}\n\n/** fetch 连不上时 message 只有 \"fetch failed\",具体原因(ECONNREFUSED 等)在 cause 里 */\nfunction fetchErrorMessage(error: unknown): string {\n const detail = error as { cause?: { message?: string }; message?: string } | null;\n return detail?.cause?.message || detail?.message || String(error);\n}\n\n/**\n * 小程序设置的请求头,去掉 fetch 不允许或无意义的。上传文件时 multipart 的 boundary 只有浏览器生成的\n * content-type 里才有,所以这时 content-type 用浏览器的\n */\nfunction forwardedRequestHeaders(encoded: string | undefined, browserContentType = ''): Record<string, string> {\n const multipart = browserContentType.startsWith('multipart/form-data');\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(parseHeaders(encoded))) {\n const name = key.toLowerCase();\n if (DROPPED_REQUEST_HEADERS.has(name) || (multipart && name === 'content-type') || value === undefined || value === null) continue;\n headers[key] = String(value);\n }\n if (multipart) headers['content-type'] = browserContentType;\n return headers;\n}\n\n/** 头格式错误时按无自定义头处理 */\nfunction parseHeaders(encoded = '{}'): Record<string, unknown> {\n try {\n const parsed = JSON.parse(encoded);\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};\n } catch {\n return {};\n }\n}\n\nfunction relayResponse(upstream: Response): Response {\n const responseHeaders: Record<string, string> = {};\n const headers = new Headers();\n upstream.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n if (!DROPPED_RESPONSE_HEADERS.has(key)) headers.set(key, value);\n });\n headers.set('x-wxa-headers', encodeURIComponent(JSON.stringify(responseHeaders)));\n const setCookies = typeof upstream.headers.getSetCookie === 'function' ? upstream.headers.getSetCookie() : [];\n headers.set('x-wxa-set-cookie', encodeURIComponent(JSON.stringify(setCookies)));\n return new Response(upstream.body, { status: upstream.status, statusText: upstream.statusText, headers });\n}\n","/**\n * 被拦截的 wx.request 在 Network 面板里的响应:Service Worker 向发出请求的页面要拦截规则给出的结果。\n * 开发服务器的 sw.ts 和查看器的 packSw.ts 共用\n */\n\nimport { MockQuery, MockReply, toHeaders } from './networkPanel';\n\nexport interface MockClient {\n postMessage(message: MockQuery, transfer: Transferable[]): void;\n}\n\nconst NULL_BODY_STATUSES = new Set([204, 205, 304]);\n\n/** 向发起请求的页面索取拦截响应;页面失效或响应无法表示时让 Network 面板显示请求失败。 */\nexport async function mockedResponse(client: MockClient | undefined, id: string): Promise<Response> {\n if (!client) return Response.error();\n const reply = await new Promise<MockReply>(resolve => {\n const channel = new MessageChannel();\n channel.port1.onmessage = event => {\n channel.port1.close();\n resolve(event.data);\n };\n client.postMessage({ type: 'wxa-mock-response', id }, [channel.port2]);\n });\n if (!reply) return Response.error();\n try {\n return new Response(NULL_BODY_STATUSES.has(reply.statusCode) ? null : reply.body, {\n status: reply.statusCode,\n headers: toHeaders(reply.header)\n });\n } catch {\n // 小程序允许的部分状态码超出浏览器 Response 能表示的范围。\n return Response.error();\n }\n}\n","/** setup 打包时对 wxa-runner-core/setup 的替换:开发服务器和在浏览器里编译 setup 的宿主(如查看器)共用同一实现;defineSetup 只提供类型提示,运行时原样返回。 */\nexport const SETUP_MODULE = '@open-wxa/wxa-runner-core/setup';\nexport const SETUP_MODULE_SOURCE = 'export function defineSetup(setup) { return setup; }';\n"],"mappings":"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","names":["html","module","bytes"]}
1
+ {"version":3,"sources":["../src/web.ts","../src/project/toJs.ts","../src/server/pluginCode.ts","../src/server/html.ts","../src/server/appRoutes.ts","../src/server/FileStore.ts","../src/runtime/networkPanel.ts","../src/server/proxyFetch.ts","../src/runtime/mockResponse.ts","../src/runtime/setupModule.ts"],"sourcesContent":["/**\n * wxa-runner-core/web:不依赖 Node 的公开接口,给在浏览器、Service Worker 或其他 Fetch API 运行时里托管小程序的宿主用\n * (如 @open-wxa/wxa-viewer)。开发服务器用的也是同一份实现,两边生成的页面和行为一致。\n */\n\nexport { DEFAULT_DEVICE, extname, mimeType, routeApp } from './server/appRoutes';\nexport type { AppSource, RouteResponse } from './server/appRoutes';\nexport { hostHtml } from './server/html';\nexport type { AppBuild, PluginCode } from './server/pluginCode';\nexport { FileStore } from './server/FileStore';\nexport { proxyFetch } from './server/proxyFetch';\nexport { mockedResponse } from './runtime/mockResponse';\nexport type { MockClient } from './runtime/mockResponse';\nexport { MOCK_ID_HEADER, MOCK_PREFIX, PROXY_PREFIX } from './runtime/networkPanel';\nexport { SETUP_MODULE, SETUP_MODULE_SOURCE } from './runtime/setupModule';\nexport type { BuildResult } from './project/AppBuilder';\nexport type { AppInfo, DeviceProfile } from './runtime/types';\n","/** JSON 序列化并转义 </script> 与行分隔符,结果可安全嵌入脚本。不依赖 Node:查看器的 Service Worker 也用它生成页面 */\nexport function toJs(value: unknown): string {\n return JSON.stringify(value)\n .replace(/</g, '\\\\u003c')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n}\n","/**\n * 插件代码的数据结构与页面查找,下载见 plugins.ts。\n * 不依赖 Node:查看器的 Service Worker 也按同样的结构生成页面\n */\n\nimport type { BuildResult } from '../project/AppBuilder';\n\nexport interface PluginCode {\n /** app.json 里的引用名,plugin://<alias>/页面名 */\n alias: string;\n /** 插件 appid */\n provider: string;\n version: string;\n /** 包内路径(/ 开头)→ 内容 */\n files: Map<string, Uint8Array>;\n /** 插件页面的路径,如 __plugin__/wx…/pages/index/index */\n pages: string[];\n /** 插件页面的配置,键同 __wxConfig.page(路径加 .html) */\n pageConfigs: Record<string, unknown>;\n}\n\n/** 构建结果加上用到的插件:插件页面并入 pages,配置并入 __wxConfig.page */\nexport interface AppBuild extends BuildResult {\n plugins: PluginCode[];\n}\n\n/** 插件页面并入 pages,配置并入 __wxConfig.page */\nexport function withPlugins(build: BuildResult, plugins: PluginCode[]): AppBuild {\n if (!plugins.length) return { ...build, plugins };\n return {\n ...build,\n plugins,\n pages: [...build.pages, ...plugins.flatMap(plugin => plugin.pages)],\n wxConfig: {\n ...build.wxConfig,\n page: { ...build.wxConfig.page, ...Object.assign({}, ...plugins.map(plugin => plugin.pageConfigs)) }\n }\n };\n}\n\n/** 插件页面所属的插件,以及页面在插件包里的路径(不带扩展名) */\nexport function findPluginPage(build: AppBuild, pagePath: string): { plugin: PluginCode; innerPath: string } | null {\n for (const plugin of build.plugins) {\n const prefix = `__plugin__/${plugin.provider}/`;\n if (plugin.pages.includes(pagePath)) return { plugin, innerPath: pagePath.slice(prefix.length) };\n }\n return null;\n}\n\nexport function pluginScriptUrl(plugin: PluginCode, file: 'appservice.js' | 'pageframe.js'): string {\n return `/__wxa__/plugin/${encodeURIComponent(plugin.provider)}/${encodeURIComponent(plugin.version)}/${file}`;\n}\n","/**\n * 宿主、逻辑层、渲染层的 HTML,以及 Worker 的入口脚本\n *\n * 不依赖 Node:查看器的 Service Worker 也用这些函数生成页面\n */\n\nimport type { BuildResult } from '../project/AppBuilder';\nimport { toJs } from '../project/toJs';\nimport type { WebviewScript } from './DevServer';\nimport { AppBuild, PluginCode, pluginScriptUrl } from './pluginCode';\n\nexport function hostHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>wxa-runner</title>\n</head>\n<body>\n <div id=\"wxa-root\"></div>\n <script src=\"/__wxa__/runtime/host.js\"></script>\n</body>\n</html>`;\n}\n\n/**\n * 打开基础库的热更新开关(runtime/host/hmr.ts 依赖它):Page / Component / 模板注册时登记更新回调。\n * 必须紧跟在基础库之后、业务代码注册之前执行;只在监听文件变化时打开\n */\nfunction enableHotUpdate(hot: boolean): string {\n return hot\n ? `<script>if (typeof __virtualDOM__ !== 'undefined' && __virtualDOM__.enableUpdateWxAppCode) __virtualDOM__.enableUpdateWxAppCode();</script>\\n`\n : '';\n}\n\n/**\n * isolated:新版基础库(3.7.1 起)。逻辑层 iframe 只是主上下文,业务代码由基础库通过 WeixinJSContext\n * 放进子上下文执行(见 runtime/context.ts),逻辑层就绪的通知也由那边发出\n */\nexport function appserviceHtml(\n build: AppBuild,\n launch: { path: string; query: Record<string, string> },\n options: { hot: boolean; isolated?: boolean }\n): string {\n const wxConfig = {\n ...build.wxConfig,\n appLaunchInfo: { scene: 1001, path: launch.path, query: launch.query },\n ...(options.isolated ? { isIsolateContext: true } : {})\n };\n const v = build.version;\n // 插件在业务代码之前注册:业务代码执行时就能 requirePlugin、跳转插件页面。\n // 插件脚本也排在业务模块的 define 之前,与开发者工具一致:插件初始化时调用 requireMiniProgram() 会因导出模块还没 define 而失败,开发者工具里同样如此\n const pluginUrls = build.plugins.map(plugin => pluginScriptUrl(plugin, 'appservice.js'));\n const moduleUrls = build.modules.map(id => `/__wxa__/module/${encodeURI(id)}?v=${v}`);\n const bootstrapUrl = `/__wxa__/service-bootstrap.js?v=${v}`;\n\n if (options.isolated) {\n const scripts = [...pluginUrls, ...moduleUrls, bootstrapUrl];\n return `<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<title>appservice</title>\n<script>var __wxConfig = ${toJs(wxConfig)};</script>\n<script src=\"/__wxa__/runtime/frame.js\" data-kind=\"service\" data-app-service=\"${escapeHtml(JSON.stringify(scripts))}\"${options.hot ? ' data-hot=\"1\"' : ''}></script>\n<script src=\"/__baselib__/WAServiceMainContext.js\"></script>\n</head>\n<body></body>\n</html>`;\n }\n\n const modules = [...pluginUrls, ...moduleUrls].map(src => `<script src=\"${src}\"></script>`).join('\\n');\n return `<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<title>appservice</title>\n<script>var __wxConfig = ${toJs(wxConfig)};</script>\n<script src=\"/__wxa__/runtime/frame.js\" data-kind=\"service\"></script>\n<script src=\"/__baselib__/WAService.js\"></script>\n${enableHotUpdate(options.hot)}<script>\nif (typeof define !== 'function') {\n console.error('[wxa-runner] 基础库没有完成初始化(define 不存在),当前基础库版本可能不受支持');\n}\n</script>\n${modules}\n<script src=\"${bootstrapUrl}\"></script>\n<script>parent.__wxaHost.frameLoaded('service', 0);</script>\n</head>\n<body></body>\n</html>`;\n}\n\n/**\n * 主上下文通过 WeixinJSContext 创建的子上下文(runtime/context.ts)。\n * empty 对应 allocEmpty:不预先加载 WASubContext.js,由基础库随后 loadLibFiles\n */\nexport function subcontextHtml(id: number, empty: boolean): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<title>subcontext ${id}</title>\n<script src=\"/__wxa__/runtime/frame.js\" data-kind=\"service\" data-context-id=\"${id}\"></script>\n${empty ? '' : '<script src=\"/__baselib__/WASubContext.js\"></script>\\n'}<script>parent.__wxaContexts.loaded(${id});</script>\n</head>\n<body></body>\n</html>`;\n}\n\nexport function webviewHtml(build: AppBuild, pagePath: string, webviewId: number, options: { hot: boolean; scripts?: WebviewScript[] }): string {\n const id = Number(webviewId) || 0;\n return `<!DOCTYPE html>\n<html>\n<head>\n${webviewHead(build, pagePath, id, options.scripts)}\n${enableHotUpdate(options.hot)}<script src=\"/__wxa__/page-frame.js?v=${build.version}\"></script>\n${pageWindow(build, pagePath).disableScroll ? '<style>body{overflow-y:hidden}</style>' : ''}\n</head>\n<body>\n${scriptTags(options.scripts, 'body')}<script>\n(function () {\n var path = ${toJs(pagePath)};\n if (typeof __wxAppCode__[path + '.wxss'] === 'function') __wxAppCode__[path + '.wxss']();\n var generateFunc = $gwx('./' + path + '.wxml');\n if (generateFunc) {\n document.dispatchEvent(new CustomEvent('generateFuncReady', { detail: { generateFunc: generateFunc } }));\n } else {\n document.body.innerText = './' + path + '.wxml not found';\n console.error('[wxa-runner] ./' + path + '.wxml not found');\n }\n parent.__wxaHost.frameLoaded('webview', ${id});\n})();\n</script>\n</body>\n</html>`;\n}\n\n/**\n * Worker 的入口(/__wxa__/worker.js?id=),由逻辑层的原生 WeixinWorker 创建(runtime/workers.ts)。\n * 和开发者工具一样先放 __wxConfig 和原生接口(runtime/worker.ts),再加载 Worker 基础库和 workers 目录下的模块;\n * 模块逐个加载,DevTools 里保留原文件名和行号。\n * Worker 基础库把 console 换成转发给逻辑层的版本,转发时对象被转成文本;开发者工具加载完基础库后换回原来的,日志留在 Worker 里。\n * 基础库目录里没有 WAWorker.js 时(如只放了逻辑层和渲染层文件的旧版内置基础库),直接抛出异常说明缺少的文件,由逻辑层的 Worker 对象记入 errors()(runtime/workers.ts)\n */\nexport function workerScript(build: BuildResult, baseLib: { version: string; hasWorker: boolean }): string {\n const modules = build.workerModules.map(id => `/__wxa__/module/${encodeURI(id)}?v=${build.version}`);\n const prelude = `var __wxConfig = ${toJs(build.wxConfig)}; __wxConfig.env.USER_DATA_PATH = location.origin + \"/__wxa__/usr/\" + new URL(location.href).searchParams.get(\"session\");\nimportScripts('/__wxa__/runtime/worker.js');\n`;\n if (!baseLib.hasWorker) {\n const message = `[wxa-runner] 基础库 ${baseLib.version} 里没有 WAWorker.js,Worker 不能运行。换用开发者工具下载的基础库,或升级 wxa-runner 带上新的内置基础库`;\n return `${prelude}throw new Error(${toJs(message)});\n`;\n }\n return `${prelude}var __wxaConsole = self.console;\nimportScripts('/__baselib__/WAWorker.js');\nself.console = __wxaConsole;\nimportScripts(${modules.map(url => toJs(url)).join(', ')});\n`;\n}\n\n/**\n * 插件页面:只加载插件自己的模板和样式(pageframe.js),不加载小程序的,与开发者工具一致。\n * 页面 html 是插件包里编译好的片段,自己注入样式、取模板并派发 generateFuncReady\n */\nexport function pluginWebviewHtml(\n build: AppBuild,\n plugin: PluginCode,\n innerPath: string,\n pagePath: string,\n webviewId: number,\n options: { scripts?: WebviewScript[] } = {}\n): string {\n const id = Number(webviewId) || 0;\n const html = plugin.files.get(`/${innerPath}.html`);\n const page = html ? new TextDecoder().decode(html) : `<script>console.error(${JSON.stringify(`[wxa-runner] 插件 ${plugin.alias} 里没有页面 ${innerPath}`)})</script>`;\n return `<!DOCTYPE html>\n<html>\n<head>\n${webviewHead(build, pagePath, id, options.scripts)}\n<script src=\"${pluginScriptUrl(plugin, 'pageframe.js')}\"></script>\n${pageWindow(build, pagePath).disableScroll ? '<style>body{overflow-y:hidden}</style>' : ''}\n</head>\n<body>\n${scriptTags(options.scripts, 'body')}${page}\n<script>parent.__wxaHost.frameLoaded('webview', ${id});</script>\n</body>\n</html>`;\n}\n\nfunction pageWindow(build: AppBuild, pagePath: string): Record<string, any> {\n return { ...build.wxConfig.global?.window, ...build.wxConfig.page?.[pagePath + '.html']?.window };\n}\n\n/** 渲染层公共的 head:页面配置、桥和基础库;position 为 head 的注入脚本在桥之后、基础库之前(body 的见 WebviewScript) */\nfunction webviewHead(build: AppBuild, pagePath: string, id: number, scripts?: WebviewScript[]): string {\n const wxConfig = { ...build.wxConfig, window: pageWindow(build, pagePath) };\n return `<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">\n<title>${escapeHtml(pagePath)}</title>\n<!-- 小程序渲染层的默认字号是 16px;不声明的话会继承浏览器设置里的默认字号。放在最前面,基础库和页面样式都能覆盖它 -->\n<style>html{font-size:16px}</style>\n<!-- 真机的滚动条是不占布局的浮层;桌面浏览器的常规滚动条和自定义 ::-webkit-scrollbar 会挤窄页面,一律隐藏。\n scrollbar-width 优先于 ::-webkit-scrollbar,页面样式不会再把滚动条加回来 -->\n<style>*{scrollbar-width:none}::-webkit-scrollbar{display:none}</style>\n<!-- 逻辑层建的 2d、webgl 画布搬进 wx-canvas 后铺满它(见 frame.ts 的 moveCanvasesIntoPages)。\n 基础库还会按逻辑层里的布局改它的位置和显隐,这里一律压住;触摸照常交给 wx-canvas 自己的 canvas -->\n<style>.wxa-service-canvas{position:absolute!important;left:0!important;top:0!important;width:100%!important;height:100%!important;visibility:inherit!important;pointer-events:none}</style>\n<script>var __wxConfig = ${toJs(wxConfig)};</script>\n<script src=\"/__wxa__/runtime/frame.js\" data-kind=\"webview\" data-webview-id=\"${id}\"></script>\n${scriptTags(scripts, 'head')}<script src=\"/__baselib__/WAWebview.js\"></script>`;\n}\n\n/** 注入脚本的 <script> 标签,每个一行;内联内容里的 </script 转义,不会提前结束标签 */\nfunction scriptTags(scripts: WebviewScript[] | undefined, position: 'head' | 'body'): string {\n return (scripts ?? [])\n .filter(script => (script.position ?? 'body') === position)\n .map(script => script.src !== undefined\n ? `<script src=\"${escapeHtml(script.src)}\"></script>\\n`\n : `<script>${script.content.replace(/<\\/script/gi, '<\\\\/script')}</script>\\n`)\n .join('');\n}\n\nfunction escapeHtml(text: string): string {\n return text.replace(/[&<>\"]/g, ch => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;' }[ch]!));\n}\n","/**\n * 小程序页面与代码的路由:逻辑层、子上下文、渲染层的 HTML,业务模块、模板、样式和插件代码\n *\n * 开发服务器和查看器(wxapack)的 Service Worker 共用这一份,两边生成的页面才不会走样,所以不依赖 Node。\n * app-info、setup、静态资源、文件接口等和运行环境有关的路由各自实现。\n */\n\nimport type { DeviceProfile } from '../runtime/types';\nimport type { WebviewScript } from './DevServer';\nimport { appserviceHtml, pluginWebviewHtml, subcontextHtml, webviewHtml, workerScript } from './html';\nimport { AppBuild, findPluginPage } from './pluginCode';\n\nexport const DEFAULT_DEVICE: DeviceProfile = {\n model: 'iPhone X',\n screenWidth: 375,\n screenHeight: 812,\n pixelRatio: 3,\n statusBarHeight: 44,\n safeAreaBottom: 34,\n system: 'iOS 16.0',\n platform: 'devtools'\n};\n\nexport interface AppSource {\n build: AppBuild;\n /** 业务模块的 define 脚本,id 已确认在 modules 或 workerModules 里 */\n moduleScript(id: string): string;\n baseLib: { version: string; hasWorker: boolean };\n /** 新版基础库的隔离上下文,见 html.ts 的 appserviceHtml */\n isolated: boolean;\n /** 打开基础库的热更新开关 */\n hot: boolean;\n webviewScripts?: WebviewScript[];\n}\n\nexport interface RouteResponse {\n status: number;\n /** express 的 res.type() 能识别的写法:'html'、'js'、'text' 或带点的扩展名;在 Service Worker 里用 mimeType() 换成 Content-Type */\n type: string;\n body: string | Uint8Array;\n}\n\nconst PLUGIN_SCRIPT = /^\\/__wxa__\\/plugin\\/([^/]+)\\/([^/]+)\\/(appservice\\.js|pageframe\\.js)$/;\n\n/** @returns 不归这里管、或请求的页面和文件不存在时为 null,交给后面的路由 */\nexport function routeApp(source: AppSource, url: URL): RouteResponse | null {\n const { build } = source;\n const pathname = safeDecode(url.pathname);\n if (pathname === null) return null;\n const query = url.searchParams;\n\n switch (pathname) {\n case '/__wxa__/appservice.html': {\n const launchPath = (query.get('path') || '').replace(/^\\//, '');\n const launch = build.pages.includes(launchPath)\n ? { path: launchPath, query: Object.fromEntries(new URLSearchParams(query.get('query') || '')) }\n : { path: build.pages[0], query: {} };\n return html(appserviceHtml(build, launch, { hot: source.hot, isolated: source.isolated }));\n }\n case '/__wxa__/subcontext.html': {\n const id = Number(query.get('id'));\n if (!Number.isInteger(id) || id <= 0) return { status: 400, type: 'text', body: '子上下文 id 无效' };\n return html(subcontextHtml(id, query.get('empty') === '1'));\n }\n case '/__wxa__/worker.js':\n return js(workerScript(build, source.baseLib));\n case '/__wxa__/service-bootstrap.js':\n return js(build.serviceBootstrap);\n case '/__wxa__/service-templates.js':\n return js(build.serviceTemplates);\n case '/__wxa__/page-templates.js':\n return js(build.pageTemplates);\n case '/__wxa__/page-styles.js':\n return js(build.pageStyles);\n case '/__wxa__/page-frame.js':\n return js(build.pageFrame);\n }\n\n const webview = /^\\/__wxa__\\/webview\\/(.+)\\.html$/.exec(pathname);\n if (webview) {\n const pagePath = webview[1];\n if (!build.pages.includes(pagePath)) return null;\n const webviewId = Number(query.get('id'));\n const pluginPage = findPluginPage(build, pagePath);\n return html(pluginPage\n ? pluginWebviewHtml(build, pluginPage.plugin, pluginPage.innerPath, pagePath, webviewId, { scripts: source.webviewScripts })\n : webviewHtml(build, pagePath, webviewId, { hot: source.hot, scripts: source.webviewScripts }));\n }\n\n // 插件的逻辑层、渲染层代码\n const pluginScript = PLUGIN_SCRIPT.exec(pathname);\n if (pluginScript) {\n const [, provider, version, file] = pluginScript;\n const code = build.plugins.find(plugin => plugin.provider === provider && plugin.version === version)?.files.get(`/${file}`);\n return code ? { status: 200, type: 'js', body: code } : null;\n }\n // 插件页面里的图片等资源:基础库把插件里的相对路径和 / 开头的路径都改写成站点根下的 /__plugin__/<插件 appid>/…\n const pluginFile = /^\\/__plugin__\\/([^/]+)(\\/.+)$/.exec(pathname);\n if (pluginFile) {\n const file = build.plugins.find(plugin => plugin.provider === pluginFile[1])?.files.get(pluginFile[2]);\n return file ? { status: 200, type: extname(pluginFile[2]) || 'application/octet-stream', body: file } : null;\n }\n\n const module = /^\\/__wxa__\\/module\\/(.+\\.js)$/.exec(pathname);\n if (module) {\n const id = module[1];\n if (!build.modules.includes(id) && !build.workerModules.includes(id)) {\n return { status: 404, type: 'js', body: `console.error(${JSON.stringify(`[wxa-runner] 模块 ${id} 不存在`)})` };\n }\n return js(source.moduleScript(id));\n }\n return null;\n}\n\nconst MIME_TYPES: Record<string, string> = {\n html: 'text/html; charset=utf-8',\n js: 'application/javascript; charset=utf-8',\n text: 'text/plain; charset=utf-8',\n json: 'application/json; charset=utf-8',\n css: 'text/css; charset=utf-8',\n wxss: 'text/plain; charset=utf-8',\n txt: 'text/plain; charset=utf-8',\n svg: 'image/svg+xml',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n bmp: 'image/bmp',\n ico: 'image/x-icon',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n aac: 'audio/aac',\n m4a: 'audio/mp4',\n ogg: 'audio/ogg',\n mp4: 'video/mp4',\n webm: 'video/webm',\n mov: 'video/quicktime',\n woff: 'font/woff',\n woff2: 'font/woff2',\n ttf: 'font/ttf',\n otf: 'font/otf',\n wasm: 'application/wasm'\n};\n\n/** RouteResponse.type 或文件扩展名对应的 Content-Type;认不出的按二进制处理 */\nexport function mimeType(type: string): string {\n if (type.includes('/')) return type;\n return MIME_TYPES[type.replace(/^\\./, '').toLowerCase()] ?? 'application/octet-stream';\n}\n\n/** 带点的扩展名,没有时为空串 */\nexport function extname(filePath: string): string {\n const match = /\\.[^./]+$/.exec(filePath);\n return match ? match[0] : '';\n}\n\nfunction html(body: string): RouteResponse {\n return { status: 200, type: 'html', body };\n}\n\nfunction js(body: string): RouteResponse {\n return { status: 200, type: 'js', body };\n}\n\n/** 地址里有单独的 %(如 /discount/50%)时 decodeURIComponent 会抛错,这种地址不归这里管 */\nfunction safeDecode(pathname: string): string | null {\n try {\n return decodeURIComponent(pathname);\n } catch {\n return null;\n }\n}\n","/** 文件 API、下载、上传和资源 URL 共用这些字节;不依赖 Node,以便 Service Worker 也能直接使用。 */\n\nlet storeSequence = 0;\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder('utf-8', { ignoreBOM: true });\n\nfunction normalizeEncoding(encoding: string): string {\n switch (String(encoding).toLowerCase()) {\n case 'utf8':\n case 'utf-8':\n return 'utf8';\n case 'utf16le':\n case 'utf-16le':\n case 'ucs2':\n case 'ucs-2':\n return 'utf16le';\n case 'latin1':\n case 'binary':\n return 'latin1';\n case 'ascii':\n return 'ascii';\n case 'base64':\n case 'base64url':\n return String(encoding).toLowerCase();\n case 'hex':\n return 'hex';\n default:\n throw new Error(`Unknown encoding: ${encoding}`);\n }\n}\n\nfunction encodeText(text: string, encoding: string): Uint8Array {\n const format = normalizeEncoding(encoding);\n if (format === 'utf8') return textEncoder.encode(text);\n if (format === 'base64' || format === 'base64url') {\n // 文件接口沿用宽松解析:接受 URL 字母表、忽略杂字符,遇到填充符即停止。\n let base64 = text\n .split('=', 1)[0]\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n .replace(/[^A-Za-z0-9+/]/g, '');\n if (base64.length % 4 === 1) base64 = base64.slice(0, -1);\n base64 = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n return Uint8Array.from(atob(base64), char => char.charCodeAt(0));\n }\n if (format === 'hex') {\n const bytes: number[] = [];\n for (let index = 0; index + 1 < text.length; index += 2) {\n const pair = text.slice(index, index + 2);\n if (!/^[\\da-f]{2}$/i.test(pair)) break;\n bytes.push(parseInt(pair, 16));\n }\n return new Uint8Array(bytes);\n }\n const bytes = new Uint8Array(text.length * (format === 'utf16le' ? 2 : 1));\n for (let index = 0; index < text.length; index++) {\n const code = text.charCodeAt(index);\n if (format === 'utf16le') {\n bytes[index * 2] = code & 0xff;\n bytes[index * 2 + 1] = code >>> 8;\n } else bytes[index] = code & 0xff;\n }\n return bytes;\n}\n\nfunction decodeText(bytes: Uint8Array, encoding: string): string {\n const format = normalizeEncoding(encoding);\n if (format === 'utf8') return textDecoder.decode(bytes);\n if (format === 'hex') return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');\n const chunks: string[] = [];\n // 分块转换避免大文件超出函数参数个数限制;UTF-16 按码元读取以保留孤立代理项。\n for (let offset = 0; offset < bytes.length; offset += 0x8000) {\n const chunk = bytes.subarray(offset, offset + 0x8000);\n const codes: number[] = [];\n for (let index = 0; index < chunk.length; index++) {\n if (format === 'utf16le') {\n if (index + 1 < chunk.length) codes.push(chunk[index] | (chunk[++index] << 8));\n } else codes.push(format === 'ascii' ? chunk[index] & 0x7f : chunk[index]);\n }\n chunks.push(String.fromCharCode(...codes));\n }\n const text = chunks.join('');\n if (format === 'base64') return btoa(text);\n if (format === 'base64url') return btoa(text).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n return text;\n}\n\ninterface FsNode {\n dir: boolean;\n data: Uint8Array;\n mtime: number;\n etag: string;\n}\n\n/** 基础库会往 http://usr/ 写运行日志;业务代码也可以通过 FileSystemManager 读写 */\nexport class FileStore {\n private readonly etagPrefix =\n globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${++storeSequence}-${Math.random().toString(36).slice(2)}`;\n private version = 0;\n private nodes = new Map<string, FsNode>([['/', { dir: true, data: new Uint8Array(0), mtime: Date.now(), etag: '' }]]);\n private readonly roots: string[];\n\n /** @param roots 预先创建的目录(如用户目录)。rmdir 不能删除它们和它们的上级目录:删掉之后再写 USER_DATA_PATH 都会失败 */\n constructor(roots: string[] = []) {\n this.roots = roots.map(root => this.normalize(root));\n for (const root of this.roots) this.mkdir(root, true);\n }\n\n normalize(filePath: string): string {\n const stripped = String(filePath || '').replace(/^[a-z]+:\\/\\/[^/]*/i, '');\n const parts: string[] = [];\n for (const part of stripped.split('/')) {\n if (!part || part === '.') continue;\n if (part === '..') parts.pop();\n else parts.push(part);\n }\n return '/' + parts.join('/');\n }\n\n stat(filePath: string): FsNode | undefined {\n return this.nodes.get(this.normalize(filePath));\n }\n\n mkdir(dirPath: string, recursive = false) {\n const target = this.normalize(dirPath);\n if (this.nodes.has(target)) throw new Error(`file already exists ${dirPath}`);\n const parent = target.slice(0, target.lastIndexOf('/')) || '/';\n if (!this.nodes.get(parent)?.dir) {\n if (!recursive) throw new Error(`no such file or directory ${dirPath}`);\n this.mkdir(parent, true);\n }\n this.nodes.set(target, { dir: true, data: new Uint8Array(0), mtime: Date.now(), etag: '' });\n }\n\n write(filePath: string, input: string | Uint8Array, append = false) {\n const data = typeof input === 'string' ? textEncoder.encode(input) : new Uint8Array(input);\n const target = this.normalize(filePath);\n const parent = target.slice(0, target.lastIndexOf('/')) || '/';\n if (!this.nodes.get(parent)?.dir) throw new Error(`no such file or directory ${filePath}`);\n const existing = this.nodes.get(target);\n if (existing?.dir) throw new Error(`illegal operation on a directory ${filePath}`);\n let contents = data;\n if (append && existing) {\n contents = new Uint8Array(existing.data.length + data.length);\n contents.set(existing.data);\n contents.set(data, existing.data.length);\n }\n this.nodes.set(target, { dir: false, data: contents, mtime: Date.now(), etag: `${this.etagPrefix}-${++this.version}` });\n }\n\n read(filePath: string): Uint8Array {\n const node = this.stat(filePath);\n if (!node) throw new Error(`no such file or directory, open ${filePath}`);\n if (node.dir) throw new Error(`illegal operation on a directory ${filePath}`);\n return node.data;\n }\n\n readdir(dirPath: string): string[] {\n const target = this.normalize(dirPath);\n if (!this.nodes.get(target)?.dir) throw new Error(`no such file or directory ${dirPath}`);\n const prefix = target === '/' ? '/' : target + '/';\n return [...this.nodes.keys()]\n .filter(key => key.startsWith(prefix) && key !== target && !key.slice(prefix.length).includes('/'))\n .map(key => key.slice(prefix.length));\n }\n\n remove(filePath: string, options: { dir: boolean; recursive?: boolean }) {\n const target = this.normalize(filePath);\n const node = this.nodes.get(target);\n if (!node) throw new Error(`no such file or directory ${filePath}`);\n if (node.dir !== options.dir) throw new Error(options.dir ? `not a directory ${filePath}` : `operation not permitted ${filePath}`);\n if (node.dir) {\n const children = this.readdir(target);\n if (children.length && !options.recursive) throw new Error(`directory not empty ${filePath}`);\n for (const key of [...this.nodes.keys()]) if (key.startsWith(target + '/')) this.nodes.delete(key);\n }\n this.nodes.delete(target);\n }\n\n /** 文件 API 经开发服务器调用的操作,参数和返回值的字段名与基础库一致 */\n execute(operation: string, args: any): Record<string, any> {\n if (!Object.prototype.hasOwnProperty.call(this.operations, operation)) {\n throw new Error(`unsupported filesystem operation ${operation}`);\n }\n return this.operations[operation](args);\n }\n\n private readonly operations: Record<string, (args: any) => Record<string, any>> = {\n readFile: ({ filePath, encoding }) => {\n const data = this.read(filePath);\n return encoding === undefined ? { data: decodeText(data, 'base64'), binary: true } : { data: decodeText(data, encoding) };\n },\n writeFile: args => this.writeEncoded(args, false),\n appendFile: args => this.writeEncoded(args, true),\n copyFile: ({ srcPath, destPath }) => {\n this.write(destPath, this.read(srcPath));\n return {};\n },\n rename: ({ oldPath, newPath }) => {\n const source = this.normalize(oldPath);\n const target = this.normalize(newPath);\n const node = this.existing(oldPath);\n this.assertNotRoot(source);\n this.assertNotRoot(target);\n if (source === target) return {};\n const parent = target.slice(0, target.lastIndexOf('/')) || '/';\n if (!this.nodes.get(parent)?.dir) throw new Error(`no such file or directory ${newPath}`);\n if (node.dir && target.startsWith(source + '/')) throw new Error('invalid argument: directory moved into itself');\n const destination = this.nodes.get(target);\n if (destination) {\n if (destination.dir !== node.dir) throw new Error(`incompatible file type ${newPath}`);\n if (destination.dir && this.readdir(target).length) throw new Error(`directory not empty ${newPath}`);\n }\n // 先验证所有条件,再一次性移动节点;不经复制/删除,保留内容、时间和 ETag。\n const entries = [...this.nodes].filter(([key]) => key === source || (node.dir && key.startsWith(source + '/')));\n for (const [key] of entries) this.nodes.delete(key);\n for (const [key, value] of entries) this.nodes.set(target + key.slice(source.length), value);\n return {};\n },\n access: ({ path }) => {\n this.existing(path);\n return {};\n },\n stat: ({ path }) => {\n const node = this.existing(path);\n const stats = {\n mode: node.dir ? 0o40755 : 0o100644,\n size: node.data.length,\n lastAccessedTime: Math.floor(node.mtime / 1000),\n lastModifiedTime: Math.floor(node.mtime / 1000)\n };\n return { stats, ...stats };\n },\n getFileInfo: ({ filePath }) => ({ size: this.read(filePath).length }),\n mkdir: ({ dirPath, recursive }) => {\n this.mkdir(dirPath, !!recursive);\n return {};\n },\n readdir: ({ dirPath }) => ({ files: this.readdir(dirPath) }),\n unlink: ({ filePath }) => {\n this.remove(filePath, { dir: false });\n return {};\n },\n rmdir: ({ dirPath, recursive }) => {\n this.assertNotRoot(dirPath);\n this.remove(dirPath, { dir: true, recursive: !!recursive });\n return {};\n }\n };\n\n private writeEncoded(args: { filePath: string; data?: string; binary?: boolean; encoding?: string }, append: boolean) {\n this.write(args.filePath, encodeText(args.data ?? '', args.binary ? 'base64' : args.encoding || 'utf8'), append);\n return {};\n }\n\n private existing(filePath: string): FsNode {\n const node = this.stat(filePath);\n if (!node) throw new Error(`no such file or directory ${filePath}`);\n return node;\n }\n\n private assertNotRoot(dirPath: string) {\n const target = this.normalize(dirPath);\n const prefix = target === '/' ? '/' : target + '/';\n if (this.roots.some(root => root === target || root.startsWith(prefix))) {\n throw new Error(`operation not permitted ${dirPath}`);\n }\n }\n}\n","/**\n * 小程序的请求在浏览器 Network 面板里的样子,宿主、Service Worker 和开发服务器共用\n *\n * 原地址放在路径里:/__wxa__/proxy/https/api.example.com/user/info?id=1。Name 列显示接口名(info?id=1),\n * 按域名、路径过滤也直接可用;放在查询参数里时每一行都是 proxy?url=https%3A%2F%2F…,分不出是哪个接口。\n *\n * - 经开发服务器转发的请求:/__wxa__/proxy/…\n * - 被拦截规则拦下的请求:/__wxa__/mock/…,由 Service Worker 返回规则给出的响应,只用于显示(见 sw.ts)\n */\n\nimport type { MockedResponse } from './host/api/routes';\n\nexport const PROXY_PREFIX = '/__wxa__/proxy/';\nexport const MOCK_PREFIX = '/__wxa__/mock/';\n\n/** 显示被拦截请求的 fetch 带上这个头,Service Worker 凭它向宿主要对应的响应 */\nexport const MOCK_ID_HEADER = 'x-wxa-mock-id';\n\n/** Service Worker 向宿主要响应时发的消息,附带一个 MessagePort,宿主从这个端口回复 MockReply */\nexport interface MockQuery {\n type: 'wxa-mock-response';\n id: string;\n}\n\n/** 请求被取消、拦截规则出错,或宿主已经不认识这个 id 时为 null,Network 面板里显示为失败 */\nexport type MockReply = MockedResponse | null;\n\n/**\n * 原地址在开发服务器上对应的路径,prefix 是 PROXY_PREFIX 或 MOCK_PREFIX。不是 http(s) 地址时返回 null\n *\n * 先按 URL 规范化:路径里的 .. 要在拼接前解析掉,否则浏览器会在代理路径上解析,把域名那一段也吃掉\n */\nexport function toServerPath(prefix: string, url: string): string | null {\n let target: URL;\n try {\n target = new URL(url);\n } catch {\n return null;\n }\n if (target.protocol !== 'http:' && target.protocol !== 'https:') return null;\n return `${prefix}${target.protocol.slice(0, -1)}/${target.host}${target.pathname}${target.search}`;\n}\n\n/**\n * toServerPath 的逆运算,格式不对或还原出的地址不合法时返回 null\n *\n * 只校验不规范化,返回拼出的原样字符串:路径里的 %2F 等编码要原样转发\n */\nexport function fromServerPath(prefix: string, serverPath: string): string | null {\n if (!serverPath.startsWith(prefix)) return null;\n const match = /^(https?)\\/([^/?#]+)(.*)$/.exec(serverPath.slice(prefix.length));\n if (!match) return null;\n const url = `${match[1]}://${match[2]}${match[3]}`;\n try {\n new URL(url);\n return url;\n } catch {\n return null;\n }\n}\n\n/**\n * 用于显示的请求、响应头。浏览器不允许设置的(cookie、set-cookie 等)会被忽略,\n * 名字或值不合法的(如值里有中文)跳过:它们只影响 Network 面板里的显示,小程序拿到的头不受影响。\n * 值为 undefined、null 的和代理一样不发\n */\nexport function toHeaders(header: Record<string, unknown>): Headers {\n const headers = new Headers();\n for (const [name, value] of Object.entries(header)) {\n if (value === undefined || value === null) continue;\n try {\n headers.set(name, String(value));\n } catch {\n // 跳过\n }\n }\n return headers;\n}\n","/**\n * 网络代理:浏览器端的 wx.request 经由这里转发到真实服务器\n *\n * - 原地址写在路径里:/__wxa__/proxy/https/api.example.com/a?b=1(见 networkPanel.ts)\n * - 绕开 CORS\n * - 小程序设置的请求头放在 x-wxa-headers(JSON)里传过来,\n * 这样 cookie / referer / user-agent 等浏览器禁止设置的头也能带上\n * - 响应头和 set-cookie 以 URL 编码的 JSON 返回,浏览器端可以完整读取\n *\n * 只用标准 Fetch API:开发服务器(proxy.ts)和其他支持 Fetch API 的运行时共用。\n */\n\nimport { fromServerPath, PROXY_PREFIX } from '../runtime/networkPanel';\n\n// fetch 不允许或无意义的请求头\nconst DROPPED_REQUEST_HEADERS = new Set(['host', 'connection', 'content-length', 'transfer-encoding', 'keep-alive', 'upgrade', 'expect']);\n// 响应体已被解压/重新分块,这些头不再成立\nconst DROPPED_RESPONSE_HEADERS = new Set(['content-encoding', 'content-length', 'transfer-encoding', 'connection', 'set-cookie']);\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\n\nexport async function proxyFetch(\n request: Request,\n options: { fetch?: typeof fetch; isAllowed?: (url: URL) => boolean } = {}\n): Promise<Response> {\n const url = new URL(request.url);\n // pathname 保留 %2F 等编码,解码会改变上游地址。\n const path = url.pathname + url.search;\n const target = fromServerPath(PROXY_PREFIX, path);\n if (!target) return proxyError(400, `invalid url \"${path}\"`);\n\n try {\n let current = new URL(target);\n if (options.isAllowed && !options.isAllowed(current)) return forbiddenHost(current, 0);\n const headers = new Headers(\n forwardedRequestHeaders(request.headers.get('x-wxa-headers') ?? undefined, request.headers.get('content-type') ?? '')\n );\n let method = request.method;\n // 手动重定向时 307/308 需要重发同一个请求体;响应体始终直接透传。\n let body = request.body && !['GET', 'HEAD'].includes(method) ? await request.arrayBuffer() : undefined;\n const send = options.fetch ?? fetch;\n let upstream: Response;\n for (let hop = 0; ; hop++) {\n upstream = await send(current.href, {\n method,\n headers,\n body,\n redirect: options.isAllowed ? 'manual' : 'follow',\n signal: request.signal\n });\n const location = upstream.headers.get('location');\n if (!options.isAllowed || !REDIRECT_STATUSES.has(upstream.status) || location === null) break;\n await upstream.body?.cancel();\n const next = new URL(location, current);\n if (hop >= 20) return proxyError(403, `host \"${next.hostname}\": redirect hop ${hop + 1} exceeds limit 20`);\n if (!['http:', 'https:'].includes(next.protocol) || !options.isAllowed(next)) return forbiddenHost(next, hop + 1);\n if (\n (upstream.status === 303 && method !== 'GET' && method !== 'HEAD') ||\n ((upstream.status === 301 || upstream.status === 302) && method === 'POST')\n ) {\n method = 'GET';\n body = undefined;\n for (const name of ['content-type', 'content-encoding', 'content-language', 'content-location']) headers.delete(name);\n }\n // 跨源跳转不能把原站点凭据交给另一个白名单域名。\n if (next.origin !== current.origin) {\n for (const name of ['authorization', 'proxy-authorization', 'cookie']) headers.delete(name);\n }\n current = next;\n }\n return relayResponse(upstream);\n } catch (error) {\n return proxyError(502, fetchErrorMessage(error));\n }\n}\n\nfunction forbiddenHost(url: URL, hop: number): Response {\n return proxyError(403, `host \"${url.hostname}\" is not in the allowlist (redirect hop ${hop})`);\n}\n\nfunction proxyError(status: number, message: string): Response {\n return new Response(message, { status, headers: { 'x-wxa-proxy-error': '1', 'content-type': 'text/html; charset=utf-8' } });\n}\n\n/** fetch 连不上时 message 只有 \"fetch failed\",具体原因(ECONNREFUSED 等)在 cause 里 */\nfunction fetchErrorMessage(error: unknown): string {\n const detail = error as { cause?: { message?: string }; message?: string } | null;\n return detail?.cause?.message || detail?.message || String(error);\n}\n\n/**\n * 小程序设置的请求头,去掉 fetch 不允许或无意义的。上传文件时 multipart 的 boundary 只有浏览器生成的\n * content-type 里才有,所以这时 content-type 用浏览器的\n */\nfunction forwardedRequestHeaders(encoded: string | undefined, browserContentType = ''): Record<string, string> {\n const multipart = browserContentType.startsWith('multipart/form-data');\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(parseHeaders(encoded))) {\n const name = key.toLowerCase();\n if (DROPPED_REQUEST_HEADERS.has(name) || (multipart && name === 'content-type') || value === undefined || value === null) continue;\n headers[key] = String(value);\n }\n if (multipart) headers['content-type'] = browserContentType;\n return headers;\n}\n\n/** 头格式错误时按无自定义头处理 */\nfunction parseHeaders(encoded = '{}'): Record<string, unknown> {\n try {\n const parsed = JSON.parse(encoded);\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};\n } catch {\n return {};\n }\n}\n\nfunction relayResponse(upstream: Response): Response {\n const responseHeaders: Record<string, string> = {};\n const headers = new Headers();\n upstream.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n if (!DROPPED_RESPONSE_HEADERS.has(key)) headers.set(key, value);\n });\n headers.set('x-wxa-headers', encodeURIComponent(JSON.stringify(responseHeaders)));\n const setCookies = typeof upstream.headers.getSetCookie === 'function' ? upstream.headers.getSetCookie() : [];\n headers.set('x-wxa-set-cookie', encodeURIComponent(JSON.stringify(setCookies)));\n return new Response(upstream.body, { status: upstream.status, statusText: upstream.statusText, headers });\n}\n","/**\n * 被拦截的 wx.request 在 Network 面板里的响应:Service Worker 向发出请求的页面要拦截规则给出的结果。\n * 开发服务器的 sw.ts 和查看器的 packSw.ts 共用\n */\n\nimport { MockQuery, MockReply, toHeaders } from './networkPanel';\n\nexport interface MockClient {\n postMessage(message: MockQuery, transfer: Transferable[]): void;\n}\n\nconst NULL_BODY_STATUSES = new Set([204, 205, 304]);\n\n/** 向发起请求的页面索取拦截响应;页面失效或响应无法表示时让 Network 面板显示请求失败。 */\nexport async function mockedResponse(client: MockClient | undefined, id: string): Promise<Response> {\n if (!client) return Response.error();\n const reply = await new Promise<MockReply>(resolve => {\n const channel = new MessageChannel();\n channel.port1.onmessage = event => {\n channel.port1.close();\n resolve(event.data);\n };\n client.postMessage({ type: 'wxa-mock-response', id }, [channel.port2]);\n });\n if (!reply) return Response.error();\n try {\n return new Response(NULL_BODY_STATUSES.has(reply.statusCode) ? null : reply.body, {\n status: reply.statusCode,\n headers: toHeaders(reply.header)\n });\n } catch {\n // 小程序允许的部分状态码超出浏览器 Response 能表示的范围。\n return Response.error();\n }\n}\n","/** setup 打包时对 wxa-runner-core/setup 的替换:开发服务器和在浏览器里编译 setup 的宿主(如查看器)共用同一实现;defineSetup 只提供类型提示,运行时原样返回。 */\nexport const SETUP_MODULE = '@open-wxa/wxa-runner-core/setup';\nexport const SETUP_MODULE_SOURCE = 'export function defineSetup(setup) { return setup; }';\n"],"mappings":"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","names":["html","module","bytes"]}
package/dist/web.mjs CHANGED
@@ -395,6 +395,26 @@ var FileStore = class {
395
395
  this.write(destPath, this.read(srcPath));
396
396
  return {};
397
397
  },
398
+ rename: ({ oldPath, newPath }) => {
399
+ const source = this.normalize(oldPath);
400
+ const target = this.normalize(newPath);
401
+ const node = this.existing(oldPath);
402
+ this.assertNotRoot(source);
403
+ this.assertNotRoot(target);
404
+ if (source === target) return {};
405
+ const parent = target.slice(0, target.lastIndexOf("/")) || "/";
406
+ if (!this.nodes.get(parent)?.dir) throw new Error(`no such file or directory ${newPath}`);
407
+ if (node.dir && target.startsWith(source + "/")) throw new Error("invalid argument: directory moved into itself");
408
+ const destination = this.nodes.get(target);
409
+ if (destination) {
410
+ if (destination.dir !== node.dir) throw new Error(`incompatible file type ${newPath}`);
411
+ if (destination.dir && this.readdir(target).length) throw new Error(`directory not empty ${newPath}`);
412
+ }
413
+ const entries = [...this.nodes].filter(([key]) => key === source || node.dir && key.startsWith(source + "/"));
414
+ for (const [key] of entries) this.nodes.delete(key);
415
+ for (const [key, value] of entries) this.nodes.set(target + key.slice(source.length), value);
416
+ return {};
417
+ },
398
418
  access: ({ path }) => {
399
419
  this.existing(path);
400
420
  return {};