@dimina-kit/devtools 0.4.0-dev.20260728095034 → 0.4.0-dev.20260728110120
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.
- package/dist/main/index.bundle.js +1 -2
- package/dist/main/ipc/bridge-router.js +1 -2
- package/dist/main/services/views/native-simulator-view.js +4 -0
- package/dist/preload/runtime/native-host.d.ts +8 -0
- package/dist/preload/runtime/native-host.js +2 -0
- package/dist/preload/windows/main.cjs.map +1 -1
- package/dist/preload/windows/simulator.cjs +1 -0
- package/dist/preload/windows/simulator.cjs.map +2 -2
- package/dist/preload/windows/simulator.js +1 -0
- package/dist/render-host/preload.cjs +18 -0
- package/dist/shared/bridge-channels.d.ts +5 -2
- package/dist/simulator/assets/{device-shell-Cs-rmP1f.js → device-shell-Sbkjw8Nb.js} +2 -2
- package/dist/simulator/assets/{simulator-BG-5CADK.js → simulator-CRyLSz1N.js} +3 -3
- package/dist/simulator/assets/{simulator-mini-app-DzfMfnVM.js → simulator-mini-app-w1Bhwkld.js} +2 -2
- package/dist/simulator/simulator.html +1 -1
- package/package.json +5 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/preload/windows/main.ts", "../../../src/shared/bridge-channels.ts", "../../../src/preload/runtime/main-api-runner.ts", "../../../src/simulator/simulator-api-helpers.ts", "../../../src/simulator/simulator-api-storage.ts", "../../../src/simulator/simulator-api-device.ts", "../../../src/simulator/event-bridge.ts", "../../../src/simulator/temp-files.ts", "../../../src/simulator/simulator-api-media.ts", "../../../src/shared/vpath.ts", "../../../src/simulator/simulator-api-fs.ts", "../../../src/simulator/simulator-api-network.ts", "../../../src/simulator/ui-overlay-bus.ts", "../../../src/simulator/simulator-api-ui.ts", "../../../src/simulator/simulator-api.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Preload bridge for the main window, settings window, settings overlay\n * view, and popover overlay view. Exposes a minimal, typed `window.devtools`\n * surface so the renderer never has to call `window.require('electron')`.\n *\n * This file is bundled by esbuild to CJS at `dist/preload/windows/main.js`\n * (see package.json `build:preload`). It must remain a leaf module \u2014 do\n * NOT import other preload files via `.js` ESM specifiers; everything goes\n * through the Electron `contextBridge` / `ipcRenderer` runtime.\n *\n * Channel governance lives in the main process: `sender-policy.ts` plus the\n * per-handler zod schemas decide what's accepted. The preload layer is a\n * thin pass-through \u2014 a renderer inventing channel names will just hit a\n * main-side rejection, so duplicating an allowlist here added no real\n * defense for a devtool that loads user-trusted mini-programs.\n */\nimport { contextBridge, ipcRenderer, type IpcRendererEvent } from 'electron'\nimport { BRIDGE_CHANNELS as C, SIMULATOR_EVENTS as E } from '../../shared/bridge-channels.js'\nimport type {\n ApiCallPayload,\n ApiResponsePayload,\n} from '../../shared/bridge-channels.js'\nimport { runApiAsync } from '../runtime/main-api-runner.js'\nimport { simulatorApis } from '../../simulator/simulator-api.js'\n\nconst api = {\n ipc: {\n invoke: (channel: string, ...args: unknown[]) => ipcRenderer.invoke(channel, ...args),\n // Synchronous round-trip. Blocks the renderer until the main process sets\n // `event.returnValue` \u2014 used only for the editor's beforeunload flush, where\n // the write MUST land before the page is torn down (an async invoke can't).\n sendSync: (channel: string, ...args: unknown[]) => ipcRenderer.sendSync(channel, ...args),\n send: (channel: string, ...args: unknown[]) => {\n ipcRenderer.send(channel, ...args)\n },\n on: (\n channel: string,\n listener: (event: IpcRendererEvent, ...args: unknown[]) => void,\n ) => {\n ipcRenderer.on(channel, listener)\n return () => ipcRenderer.removeListener(channel, listener)\n },\n once: (\n channel: string,\n listener: (event: IpcRendererEvent, ...args: unknown[]) => void,\n ) => {\n ipcRenderer.once(channel, listener)\n },\n removeListener: (\n channel: string,\n listener: (...args: unknown[]) => void,\n ) => {\n ipcRenderer.removeListener(channel, listener)\n },\n },\n}\n\n// All host windows that load this preload run with `contextIsolation: true`\n// (main / settings / view-manager), so `contextBridge` is the path that\n// reaches the renderer's `window`. The simulator <webview> uses a separate\n// preload (`simulator.ts`).\ncontextBridge.exposeInMainWorld('devtools', api)\n\n// Native-host fallback: bridge-router forwards any invokeAPI name not\n// registered in `ctx.simulatorApis` to the simulator's webContents via\n// E.API_CALL. When the *main* window doubles as the simulator (the default\n// path for e2e callers that drive `dmb:spawn` without an explicit\n// simulatorWcId), the listener must live here in the main-window preload\n// because the renderer side has no DeviceShell mounted to install one.\nipcRenderer.on(E.API_CALL, (_event, payload: ApiCallPayload) => {\n void runApiAsync(\n simulatorApis as Record<string, (this: unknown, params?: unknown) => unknown | Promise<unknown>>,\n payload.name,\n payload.params,\n ).then((verdict) => {\n const ack: ApiResponsePayload = {\n appSessionId: payload.appSessionId,\n requestId: payload.requestId,\n ok: verdict.ok,\n result: verdict.result,\n errMsg: verdict.errMsg,\n }\n ipcRenderer.send(C.API_RESPONSE, ack)\n })\n})\n\n// Test-only hatch: e2e helpers emit synthetic IPC events on the renderer's\n// ipcRenderer instance to trigger handlers registered via `devtools.ipc.on`\n// without round-tripping through the main process. Gated on NODE_ENV so\n// production builds never expose it.\nif (process.env.NODE_ENV === 'test') {\n contextBridge.exposeInMainWorld('__testIpc', {\n emit: (channel: string, ...args: unknown[]) => {\n ;(ipcRenderer as unknown as { emit: (c: string, ...a: unknown[]) => void }).emit(channel, ...args)\n },\n })\n}\n\nexport type DevtoolsApi = typeof api\n", "import type { NativeDeviceInfo } from './ipc-channels.js'\n\nexport const BRIDGE_CHANNELS = {\n SPAWN: 'dmb:spawn',\n DISPOSE: 'dmb:dispose',\n PAGE_OPEN: 'dmb:page:open',\n PAGE_CLOSE: 'dmb:page:close',\n PAGE_LIFECYCLE: 'dmb:page:lifecycle',\n NAV_CALLBACK: 'dmb:nav:callback',\n SERVICE_INVOKE: 'dmb:service:invoke',\n SERVICE_PUBLISH: 'dmb:service:publish',\n RENDER_INVOKE: 'dmb:render:invoke',\n RENDER_PUBLISH: 'dmb:render:publish',\n TO_SERVICE: 'dmb:to-service',\n TO_RENDER: 'dmb:to-render',\n SIMULATOR_API: 'dmb:simulator-api',\n /** simulator \u2192 main: ack of an API_CALL request (carries success/fail args). */\n API_RESPONSE: 'dmb:api:response',\n /**\n * simulator webview preload \u2192 main (sendSync): \"is native-host mode on?\".\n * The guest preload can't read the launch `process.env`, so it asks main\n * (which can) at install time. Reply is `e.returnValue = boolean`.\n */\n NATIVE_HOST_ENABLED: 'dmb:native-host-enabled',\n /**\n * simulator (DeviceShell) \u2192 main: the visible top-of-stack page bridgeId.\n * Main has no z-order concept \u2014 the active page lives only in DeviceShell's\n * ShellState \u2014 so devtools panels / automation that must target \"the current\n * page's render webContents\" resolve it through this signal. Fire-and-forget.\n */\n ACTIVE_PAGE: 'dmb:active-page',\n /**\n * simulator (DeviceShell) \u2192 main: the FULL ordered page stack (bottom\u2192top)\n * whenever it changes. Main has no stack of its own (it only learns the\n * active bridgeId via ACTIVE_PAGE), so automation's `App.getPageStack` needs\n * this to report multi-page stacks. Fire-and-forget.\n */\n PAGE_STACK: 'dmb:page-stack',\n} as const\n\nexport const SIMULATOR_EVENTS = {\n DOM_READY: 'simulator:dom-ready',\n NAV_BAR: 'simulator:navigation-bar',\n NAV_ACTION: 'simulator:nav-action',\n TAB_ACTION: 'simulator:tab-action',\n /** main \u2192 simulator: invoke a wx.* API on the simulator-resident MiniApp. */\n API_CALL: 'simulator:api-call',\n /**\n * main \u2192 simulator: the renderer toolbar picked a different device. Carries a\n * NativeDeviceInfo; DeviceShell resizes the bezel + re-renders status bar /\n * notch. The race-free INITIAL device rides NativeHostConfig.device (read\n * synchronously at preload bridge-install); this event covers live changes.\n */\n DEVICE_CHANGE: 'simulator:device-change',\n /**\n * main \u2192 simulator: a watcher rebuild finished; boot a NEW app session for\n * the carried url ({@link RelaunchPayload}) IN PLACE, keeping the live\n * DeviceShell painted, and swap once the new session's root page reports\n * DOM_READY (ready-then-swap soft reload). Main sends this only when the\n * shell is live+ready; otherwise the renderer falls back to the hard\n * attachNativeSimulator rebuild.\n */\n RELAUNCH: 'simulator:relaunch',\n} as const\n\n/**\n * `simulator:relaunch` payload. `url` is a full simulator URL (same format as\n * the simulator page's own location / AttachNative), carrying the appId and\n * the page route the new session must boot at.\n */\nexport interface RelaunchPayload {\n url: string\n}\n\nexport const CHANNELS = BRIDGE_CHANNELS\n\n/**\n * Reply to a `NATIVE_HOST_ENABLED` sendSync. Main supplies the render-host\n * file:// URLs (computed with node:path/url, which the simulator webview's\n * preload lacks) so the preload can build the native-host bridge.\n */\nexport interface NativeHostConfig {\n enabled: boolean\n renderHostHtmlUrl: string\n renderPreloadUrl: string\n /**\n * The currently-selected device, if the renderer already pushed it before the\n * simulator WCV's preload installed (it does \u2014 SetDeviceInfo precedes\n * AttachNative). DeviceShell reads this as its initial device so it never\n * mounts with the wrong bezel size while waiting for the first DEVICE_CHANGE.\n * Absent only on the pre-spawn default path.\n */\n device?: NativeDeviceInfo\n}\n\nexport type BridgeChannel = typeof BRIDGE_CHANNELS[keyof typeof BRIDGE_CHANNELS]\n\nexport type BridgeTarget = 'service' | 'render' | 'container'\n\nexport type BridgeMessageType =\n | 'loadResource'\n | 'serviceResourceLoaded'\n | 'renderResourceLoaded'\n | 'resourceLoaded'\n | 'firstRender'\n | 'appShow'\n | 'appHide'\n | 'stackShow'\n | 'stackHide'\n | 'pageShow'\n | 'pageHide'\n | 'pageReady'\n | 'pageUnload'\n | 'pageScroll'\n | 'pageResize'\n | 'pageRouteDone'\n | 'mC'\n | 'mR'\n | 'mU'\n | 't'\n | 'u'\n | 'ub'\n | 'triggerCallback'\n | 'invokeAPI'\n | 'h5SdkAction'\n | 'componentError'\n | 'domReady'\n | 'print'\n | 'renderHostReady'\n | 'serviceHostError'\n | string\n\nexport interface MessageEnvelope<TBody extends Record<string, unknown> = Record<string, unknown>> {\n type: BridgeMessageType\n target: BridgeTarget\n body: TBody\n}\n\nexport interface HostEnvSnapshot {\n brand: string\n model: string\n platform: string\n system: string\n version: string\n SDKVersion: string\n pixelRatio: number\n screenWidth: number\n screenHeight: number\n windowWidth: number\n windowHeight: number\n statusBarHeight: number\n language: string\n theme: string\n [key: string]: unknown\n}\n\n/**\n * Map the renderer's logical device metrics onto the subset of a\n * HostEnvSnapshot the service-host window's `getSystemInfoSync` consumes.\n * `windowHeight` excludes the status bar (the page area); `windowWidth` has no\n * horizontal chrome. Zoom is intentionally absent \u2014 it is a display scale, not\n * a logical-size change.\n *\n * Single source of truth for the device\u2192host-env mapping, shared by the live\n * `SetDeviceInfo` IPC path (main/ipc/simulator.ts) and the per-spawn host-env\n * seeding (main/ipc/bridge-router.ts). Keeping them identical guarantees a\n * service-host RESPAWN after a device change reports the same dims the live\n * update pushed \u2014 otherwise a respawn would silently revert to the boot device.\n */\nexport function deviceInfoToHostEnv(d: NativeDeviceInfo): Partial<HostEnvSnapshot> {\n return {\n brand: d.brand,\n model: d.model,\n system: d.system,\n platform: d.platform,\n pixelRatio: d.pixelRatio,\n screenWidth: d.screenWidth,\n screenHeight: d.screenHeight,\n windowWidth: d.screenWidth,\n windowHeight: Math.max(0, d.screenHeight - d.statusBarHeight),\n statusBarHeight: d.statusBarHeight,\n }\n}\n\n/**\n * Subset of WeChat page `window` config plus tabBar list, parsed from\n * `app-config.json` (`{app:{window,tabBar,pages,entryPagePath}, modules:{[pagePath]:{window}}}`).\n * Mirrors mergePageConfig in dimina-fe: page-level keys override app-level.\n */\nexport interface PageWindowConfig {\n navigationBarTitleText?: string\n navigationBarBackgroundColor?: string\n navigationBarTextStyle?: 'black' | 'white'\n navigationStyle?: 'default' | 'custom'\n homeButton?: boolean\n backgroundColor?: string\n backgroundTextStyle?: 'dark' | 'light'\n enablePullDownRefresh?: boolean\n disableScroll?: boolean\n [key: string]: unknown\n}\n\nexport interface TabBarItem {\n pagePath: string\n text?: string\n iconPath?: string\n selectedIconPath?: string\n}\n\nexport interface TabBarConfig {\n color?: string\n selectedColor?: string\n backgroundColor?: string\n borderStyle?: 'black' | 'white'\n position?: 'bottom' | 'top'\n custom?: boolean\n list: TabBarItem[]\n}\n\nexport interface AppManifest {\n entryPagePath: string\n pages: string[]\n tabBar?: TabBarConfig\n /**\n * Provenance of this manifest: `'app-config'` when built from a real\n * compiled `app-config.json` (its `pages` list is authoritative, so mount\n * gates can validate membership against it); `'fallback'` when\n * app-config.json was unreachable and the manifest is a single-page stand-in\n * built from the spawn request. Mount gates only enforce against\n * `'app-config'` \u2014 a `'fallback'` manifest has no compiled truth to check\n * membership against, so it must let every page/nav target through.\n */\n source: 'app-config' | 'fallback'\n}\n\nexport interface SpawnRequest {\n simulatorWcId?: number\n appId: string\n bridgeId?: string\n pagePath?: string\n scene?: number\n query?: Record<string, unknown>\n apiNamespaces?: string[]\n hostEnvSnapshot?: Partial<HostEnvSnapshot>\n pkgRoot?: string\n root?: string\n /**\n * Base URL of the dev server that serves the compiled mini-app, i.e. the\n * SAME origin the simulator page was loaded from (`http://localhost:<port>/`).\n * The dev server statically serves `<appId>/<root>/\u2026` (app-config.json,\n * logic.js, page bundles, styles) \u2014 exactly what the default dimina-fe\n * `<webview>` fetches. The native-host render + service hosts source every\n * resource from here, so we don't need a second resource server or a local\n * compiled-output path. Trailing slash expected.\n */\n resourceBaseUrl?: string\n}\n\nexport interface SpawnResult {\n appSessionId: string\n bridgeId: string\n /** The pagePath originally requested (unchanged even when a fallback applied \u2014 see `resolvedPagePath`). */\n pagePath: string\n /**\n * The pagePath the root PageSession was ACTUALLY spawned with. Equal to\n * `pagePath` unless `pageFallbackApplied` is true, in which case the request\n * was absent from `manifest.pages` and this is `manifest.entryPagePath`\n * (or `manifest.pages[0]` when even that isn't a member). Callers that cache\n * \"the current page\" (e.g. `SimulatorMiniApp`) must reconcile against this,\n * not the original request.\n */\n resolvedPagePath: string\n /** Whether `resolvedPagePath` differs from the request because it was absent from the compiled manifest. Always false for a `'fallback'` manifest (nothing to validate against). */\n pageFallbackApplied: boolean\n serviceWcId: number\n resourceBaseUrl: string\n manifest: AppManifest\n rootWindowConfig: PageWindowConfig\n}\n\nexport interface PageOpenRequest {\n appSessionId: string\n pagePath: string\n query?: Record<string, unknown>\n bridgeId?: string\n}\n\nexport interface PageOpenResult {\n bridgeId: string\n pagePath: string\n windowConfig: PageWindowConfig\n isTab: boolean\n}\n\nexport interface PageClosePayload {\n bridgeId: string\n}\n\nexport interface DisposePayload {\n /** AppSession root bridgeId (legacy) or any page bridgeId. */\n bridgeId: string\n}\n\nexport type PageLifecycleEvent =\n | 'pageShow'\n | 'pageHide'\n | 'pageUnload'\n | 'stackShow'\n | 'stackHide'\n | 'appShow'\n | 'appHide'\n\nexport interface PageLifecyclePayload {\n appSessionId: string\n bridgeId: string\n event: PageLifecycleEvent\n}\n\n/**\n * Sent by simulator after a routing action (navigateTo etc.) completes so the\n * main process can call sendCallback() against the original service-issued\n * success/fail/complete ids.\n */\nexport interface NavCallbackPayload {\n appSessionId: string\n ok: boolean\n errMsg: string\n callbacks: { success?: unknown; fail?: unknown; complete?: unknown }\n}\n\nexport interface ServiceInvokePayload {\n bridgeId: string\n msg: MessageEnvelope\n}\n\nexport interface ServicePublishPayload {\n bridgeId: string\n targetBridgeId?: string\n msg: MessageEnvelope\n}\n\n/**\n * `dmb:active-page` \u2014 simulator (DeviceShell) \u2192 main. Records which page is the\n * visible top-of-stack so main-side services (WXML/element-inspect, automation)\n * can resolve \"the active page's render webContents\" by bridgeId.\n */\nexport interface ActivePagePayload {\n appSessionId: string\n bridgeId: string\n}\n\n/**\n * `dmb:page-stack` \u2014 simulator (DeviceShell) \u2192 main. The full ordered page\n * stack (bottom\u2192top) so `App.getPageStack` can report multi-page stacks.\n */\nexport interface PageStackEntry {\n pagePath: string\n query: Record<string, unknown>\n}\n\nexport interface PageStackPayload {\n appSessionId: string\n stack: PageStackEntry[]\n}\n\nexport interface RenderInvokePayload {\n bridgeId: string\n msg: MessageEnvelope\n}\n\nexport interface RenderPublishPayload {\n bridgeId: string\n msg: MessageEnvelope\n}\n\nexport interface ToServicePayload {\n msg: MessageEnvelope\n}\n\nexport interface ToRenderPayload {\n msg: MessageEnvelope\n}\n\nexport interface SimulatorApiInvokePayload {\n bridgeId: string\n name: string\n params: unknown\n}\n\n/**\n * `simulator:nav-action` \u2014 main \u2192 simulator window, carries router/tabBar\n * intentions. Simulator owns the visual stack state and decides whether to\n * push/pop webviews; it then calls back PAGE_LIFECYCLE + NAV_CALLBACK.\n */\nexport interface NavActionPayload {\n appSessionId: string\n bridgeId: string\n name: 'navigateTo' | 'navigateBack' | 'redirectTo' | 'reLaunch' | 'switchTab'\n params: Record<string, unknown>\n callbacks: { success?: unknown; fail?: unknown; complete?: unknown }\n}\n\n/**\n * `simulator:api-call` \u2014 main \u2192 simulator. Fallback path used when an\n * invokeAPI name is not registered in the main-process `ctx.simulatorApis`\n * registry: the simulator-resident MiniApp owns the wx.* handler (it can\n * read DOM, open file pickers, etc.), so we forward the call there and wait\n * for an `API_RESPONSE` ack.\n */\nexport interface ApiCallPayload {\n appSessionId: string\n bridgeId: string\n requestId: string\n name: string\n params: Record<string, unknown>\n callbacks: { success?: unknown; fail?: unknown; complete?: unknown }\n}\n\n/**\n * `dmb:api:response` \u2014 simulator \u2192 main. Ack for an `API_CALL`. Main looks\n * up `requestId` in its pending map and fires the original service-side\n * success/fail/complete callbacks accordingly.\n */\nexport interface ApiResponsePayload {\n appSessionId: string\n requestId: string\n ok: boolean\n /** Argument the simulator-side success/fail callback was invoked with. */\n result?: unknown\n errMsg?: string\n /**\n * Persistent-subscription marker (`keep: true` APIs, e.g.\n * `audioListen`). When set, this response is one of potentially many\n * fires of the same subscription: main re-fires the service-side success\n * callback but keeps the pendingApiCall alive (does not delete it or fire\n * `complete`) so subsequent fires of the same `requestId` are delivered.\n * Cleanup happens on page/app teardown instead.\n */\n keep?: boolean\n}\n\n/**\n * `simulator:tab-action` \u2014 main \u2192 simulator window, carries dynamic TabBar\n * API calls. Simulator updates TabBar React state and acknowledges via\n * NAV_CALLBACK so the service-side wx.* callback fires.\n */\nexport interface TabActionPayload {\n appSessionId: string\n bridgeId: string\n name:\n | 'setTabBarStyle'\n | 'setTabBarItem'\n | 'showTabBar'\n | 'hideTabBar'\n | 'setTabBarBadge'\n | 'removeTabBarBadge'\n | 'showTabBarRedDot'\n | 'hideTabBarRedDot'\n params: Record<string, unknown>\n callbacks: { success?: unknown; fail?: unknown; complete?: unknown }\n}\n", "/**\n * Native-host API_CALL handler installed inside the main-window preload.\n *\n * Goal: let the main devtools window double as a simulator when the\n * bridge-router forwards `simulator:api-call` to its webContents (the\n * default path in e2e tests, where `dmb:spawn` is invoked from mainWindow\n * directly with no explicit `simulatorWcId`).\n *\n * Unlike `simulator/main.tsx` (which boots a full DeviceShell + SimulatorMiniApp),\n * this runner is renderer-agnostic: it just owns a name\u2192handler table plus\n * a sentinel-based callback capture identical in shape to the simulator's\n * `runApiAsync`. The MiniAppContext stub it constructs is the smallest one\n * the built-in handlers expect (`appId`, `createCallbackFunction`, parent\n * with `el.querySelector` returning the simulated viewport rect).\n */\nimport type { MiniAppContext } from '../../simulator/types.js'\n\n// No `this` parameter: handlers are always invoked through `.call(ctx, \u2026)`\n// below with an explicit context object, so the declared type never needs to\n// constrain (or widen away) the caller's `this`.\ntype LooseApiHandler = (params?: unknown) => unknown | Promise<unknown>\n\nexport interface ApiRunVerdict {\n ok: boolean\n result?: unknown\n errMsg?: string\n}\n\ninterface ApiRunnerContext extends MiniAppContext {\n /** Dummy properties some upstream handlers introspect. */\n appId: string\n}\n\nfunction makeBaseContext(): ApiRunnerContext {\n // Minimal viewport rect: enough for handlers that read\n // `parent.el.querySelector('.dimina-native-webview__root').getBoundingClientRect()`\n // (see simulator-api.ts readWindowMetrics).\n const fakeRect = { width: 390, height: 844, x: 0, y: 0, top: 0, left: 0, right: 390, bottom: 844 }\n const fakeRoot = {\n getBoundingClientRect: () => fakeRect,\n } as unknown as Element\n return {\n appId: '',\n createCallbackFunction: () => undefined,\n parent: {\n el: {\n querySelector: () => fakeRoot,\n } as unknown as Element,\n getStatusBarRect: () => ({ height: 0 }),\n },\n }\n}\n\nexport function runApiAsync(\n handlers: Record<string, LooseApiHandler | undefined>,\n name: string,\n params: unknown,\n): Promise<ApiRunVerdict> {\n const handler = handlers[name]\n if (!handler) {\n return Promise.resolve({ ok: false, errMsg: `${name}:fail no handler` })\n }\n\n return new Promise<ApiRunVerdict>((resolve) => {\n let resolved = false\n const finish = (verdict: ApiRunVerdict): void => {\n if (resolved) return\n resolved = true\n resolve(verdict)\n }\n\n const SUCCESS = Symbol('main-cb-success')\n const FAIL = Symbol('main-cb-fail')\n const COMPLETE = Symbol('main-cb-complete')\n\n const base = makeBaseContext()\n const ctx: ApiRunnerContext = {\n ...base,\n createCallbackFunction(id: unknown) {\n if (id === undefined || id === null) return undefined\n return (...args: unknown[]) => {\n const arg = args[0]\n if (id === SUCCESS) {\n finish({ ok: true, result: arg })\n } else if (id === FAIL) {\n const errMsg =\n arg && typeof arg === 'object' && 'errMsg' in (arg as Record<string, unknown>)\n ? String((arg as { errMsg?: unknown }).errMsg)\n : `${name}:fail`\n finish({ ok: false, errMsg, result: arg })\n }\n }\n },\n }\n\n const userParams =\n params && typeof params === 'object' && !Array.isArray(params)\n ? { ...(params as Record<string, unknown>) }\n : {}\n const hadSuccess = userParams.success !== undefined && userParams.success !== null\n const hadFail = userParams.fail !== undefined && userParams.fail !== null\n const hadComplete = userParams.complete !== undefined && userParams.complete !== null\n\n userParams.success = SUCCESS\n userParams.fail = FAIL\n if (hadComplete) userParams.complete = COMPLETE\n\n try {\n const ret = (handler as LooseApiHandler).call(ctx, userParams)\n if (ret && typeof (ret as PromiseLike<unknown>).then === 'function') {\n Promise.resolve(ret as PromiseLike<unknown>).then(\n (r) => finish({ ok: true, result: r }),\n (err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err)\n finish({ ok: false, errMsg: `${name}:fail ${msg}` })\n },\n )\n return\n }\n if (!hadSuccess && !hadFail) {\n finish({ ok: true, result: ret })\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err)\n finish({ ok: false, errMsg: `${name}:fail ${msg}` })\n }\n })\n}\n", "import type { MiniAppContext } from './types'\n\n/**\n * Resolve the success / fail / complete callbacks of a wx.* API options\n * object in one step. Each runs through ctx.createCallbackFunction so it is\n * invoked with the proper this-binding; a missing option yields undefined.\n */\nexport function bindCallbacks(\n ctx: MiniAppContext,\n opts: { success?: unknown; fail?: unknown; complete?: unknown },\n) {\n return {\n onSuccess: ctx.createCallbackFunction(opts.success),\n onFail: ctx.createCallbackFunction(opts.fail),\n onComplete: ctx.createCallbackFunction(opts.complete),\n }\n}\n\n/**\n * Build a wx.* API stub for a capability the simulator cannot provide. The\n * returned function reports `${apiName}:fail not supported in simulator`\n * through the fail callback, then runs complete.\n */\nexport function notSupportedApi(\n apiName: string,\n): (this: MiniAppContext, opts?: { fail?: unknown; complete?: unknown }) => void {\n return function (this: MiniAppContext, opts: { fail?: unknown; complete?: unknown } = {}) {\n const { onFail, onComplete } = bindCallbacks(this, opts)\n onFail?.({ errMsg: `${apiName}:fail not supported in simulator` })\n onComplete?.()\n }\n}\n", "/**\n * DevTools API stubs for wx.xxx storage APIs (sync + async).\n *\n * Each exported function is bound with `this` = MiniApp instance\n * (via AppManager.registerApi \u2192 MiniApp.invokeApi).\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindCallbacks } from './simulator-api-helpers'\n\n// \u2500\u2500\u2500 shared storage primitives \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Every sync/async pair below (set/get/remove/clear/info) shares the same\n// underlying localStorage layout: keys are namespaced per appId, values are\n// JSON-encoded objects / stringified primitives. These helpers are the\n// single authority for that layout so the sync and async variants cannot\n// drift apart.\n\nfunction storageKeyOf(appId: string, key: string): string {\n\treturn `${appId}_${key}`\n}\n\n/** Full (still-prefixed) localStorage keys belonging to `appId`. */\nfunction collectAppKeys(appId: string): string[] {\n\tconst prefix = `${appId}_`\n\tconst keys: string[] = []\n\tfor (let i = 0; i < localStorage.length; i++) {\n\t\tconst k = localStorage.key(i)\n\t\tif (k && k.startsWith(prefix)) keys.push(k)\n\t}\n\treturn keys\n}\n\nfunction writeEntry(appId: string, key: string, data: unknown): void {\n\tconst dataString = typeof data === 'object' ? JSON.stringify(data) : String(data)\n\tlocalStorage.setItem(storageKeyOf(appId, key), dataString)\n}\n\n/** Reads one entry, JSON-parsing when possible. `undefined` means the key was never set \u2014 distinct from a stored empty string. */\nfunction readEntry(appId: string, key: string): { data: unknown } | undefined {\n\tconst raw = localStorage.getItem(storageKeyOf(appId, key))\n\tif (raw === null) return undefined\n\ttry { return { data: JSON.parse(raw) as unknown } } catch { return { data: raw } }\n}\n\nfunction clearAppKeys(appId: string): void {\n\tcollectAppKeys(appId).forEach(k => localStorage.removeItem(k))\n}\n\nfunction storageInfoOf(appId: string): { keys: string[]; currentSize: number; limitSize: number } {\n\tconst prefix = `${appId}_`\n\tconst keys: string[] = []\n\tlet currentSize = 0\n\tfor (const fullKey of collectAppKeys(appId)) {\n\t\tkeys.push(fullKey.slice(prefix.length))\n\t\tconst item = localStorage.getItem(fullKey)\n\t\tcurrentSize += item ? item.length * 2 : 0\n\t}\n\treturn { keys, currentSize, limitSize: 10 * 1024 * 1024 }\n}\n\n// \u2500\u2500\u2500 Storage (sync) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function setStorageSync(this: MiniAppContext, { key, data }: { key: string; data: unknown }) {\n\twriteEntry(this.appId, key, data)\n}\n\nexport function getStorageSync(this: MiniAppContext, { key }: { key: string }) {\n\t// wx \u771F\u673A: a missing key returns {data: ''}, never undefined or a fail.\n\treturn readEntry(this.appId, key) ?? { data: '' }\n}\n\nexport function removeStorageSync(this: MiniAppContext, { key }: { key: string }) {\n\tlocalStorage.removeItem(storageKeyOf(this.appId, key))\n}\n\nexport function clearStorageSync(this: MiniAppContext) {\n\tclearAppKeys(this.appId)\n}\n\nexport function getStorageInfoSync(this: MiniAppContext) {\n\treturn storageInfoOf(this.appId)\n}\n\n// \u2500\u2500\u2500 Storage (async) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function setStorage(\n\tthis: MiniAppContext,\n\t{ key, data, success, fail, complete }: { key: string; data: unknown; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\ttry {\n\t\twriteEntry(this.appId, key, data)\n\t\tonSuccess?.({ errMsg: 'setStorage:ok' })\n\t} catch (e) {\n\t\tonFail?.({ errMsg: `setStorage:fail ${(e as Error).message}` })\n\t}\n\tonComplete?.()\n}\n\nexport function getStorage(\n\tthis: MiniAppContext,\n\t{ key, success, fail, complete }: { key: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\t// Unlike getStorageSync, the async variant fails (not {data: ''}) on a\n\t// missing key \u2014 this is the documented wx.getStorage contract.\n\tconst entry = readEntry(this.appId, key)\n\tif (!entry) {\n\t\tonFail?.({ errMsg: 'getStorage:fail data not found' })\n\t} else {\n\t\tonSuccess?.({ data: entry.data, errMsg: 'getStorage:ok' })\n\t}\n\tonComplete?.()\n}\n\nexport function removeStorage(\n\tthis: MiniAppContext,\n\t{ key, success, fail, complete }: { key: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\ttry {\n\t\tlocalStorage.removeItem(storageKeyOf(this.appId, key))\n\t\tonSuccess?.({ errMsg: 'removeStorage:ok' })\n\t} catch (e) {\n\t\tonFail?.({ errMsg: `removeStorage:fail ${(e as Error).message}` })\n\t}\n\tonComplete?.()\n}\n\nexport function clearStorage(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\tclearAppKeys(this.appId)\n\tonSuccess?.({ errMsg: 'clearStorage:ok' })\n\tonComplete?.()\n}\n\nexport function getStorageInfo(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\tconst info = storageInfoOf(this.appId)\n\tonSuccess?.({ ...info, errMsg: 'getStorageInfo:ok' })\n\tonComplete?.()\n}\n", "/**\n * DevTools API stubs for device-related wx.xxx APIs\n * (keyboard / phone / contact / vibrate / scan).\n *\n * Each exported function is bound with `this` = MiniApp instance\n * (via AppManager.registerApi \u2192 MiniApp.invokeApi).\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindCallbacks, notSupportedApi } from './simulator-api-helpers'\n\n// \u2500\u2500\u2500 Device: Keyboard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function hideKeyboard(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\t// In web, blur the active element to dismiss virtual keyboard\n\tif (document.activeElement && typeof (document.activeElement as HTMLElement).blur === 'function') {\n\t\t;(document.activeElement as HTMLElement).blur()\n\t}\n\tonSuccess?.({ errMsg: 'hideKeyboard:ok' })\n\tonComplete?.()\n}\n\nexport function adjustPosition(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\tonSuccess?.({ errMsg: 'adjustPosition:ok' })\n\tonComplete?.()\n}\n\n// \u2500\u2500\u2500 Device: Phone \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function makePhoneCall(\n\tthis: MiniAppContext,\n\t{ phoneNumber, success, fail, complete }: { phoneNumber: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\n\ttry {\n\t\twindow.open(`tel:${phoneNumber}`)\n\t\tonSuccess?.({ errMsg: 'makePhoneCall:ok' })\n\t} catch (error) {\n\t\tonFail?.({ errMsg: `makePhoneCall:fail ${(error as Error).message}` })\n\t}\n\tonComplete?.()\n}\n\n// \u2500\u2500\u2500 Device: Contact (stub) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const chooseContact = notSupportedApi('chooseContact')\n\nexport const addPhoneContact = notSupportedApi('addPhoneContact')\n\n// \u2500\u2500\u2500 Device: Vibrate (stub) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function vibrateShort(this: MiniAppContext, { success, complete }: { type?: string; success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\t// Navigator.vibrate is available in some browsers\n\tif (navigator.vibrate) navigator.vibrate(15)\n\tonSuccess?.({ errMsg: 'vibrateShort:ok' })\n\tonComplete?.()\n}\n\nexport function vibrateLong(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\tif (navigator.vibrate) navigator.vibrate(400)\n\tonSuccess?.({ errMsg: 'vibrateLong:ok' })\n\tonComplete?.()\n}\n\n// \u2500\u2500\u2500 Device: Scan (stub) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const scanCode = notSupportedApi('scanCode')\n\n// \u2500\u2500\u2500 Device: Clipboard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function getClipboardData(\n\tthis: MiniAppContext,\n\t{ success, fail, complete }: { success?: unknown; fail?: unknown; complete?: unknown } = {},\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\treturn navigator.clipboard.readText().then(\n\t\t(data) => {\n\t\t\tonSuccess?.({ data, errMsg: 'getClipboardData:ok' })\n\t\t\tonComplete?.()\n\t\t},\n\t\t(error: unknown) => {\n\t\t\tonFail?.({ errMsg: `getClipboardData:fail ${(error as Error)?.message ?? error}` })\n\t\t\tonComplete?.()\n\t\t},\n\t)\n}\n\nexport function setClipboardData(\n\tthis: MiniAppContext,\n\t{ data, success, fail, complete }: { data?: string; success?: unknown; fail?: unknown; complete?: unknown } = {},\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\t// Parity with the native ClipboardApi: `data` is required.\n\tif (typeof data !== 'string') {\n\t\tonFail?.({ errMsg: 'setClipboardData:fail data is required' })\n\t\tonComplete?.()\n\t\treturn Promise.resolve()\n\t}\n\treturn navigator.clipboard.writeText(data).then(\n\t\t() => {\n\t\t\tonSuccess?.({ errMsg: 'setClipboardData:ok' })\n\t\t\tonComplete?.()\n\t\t},\n\t\t(error: unknown) => {\n\t\t\tonFail?.({ errMsg: `setClipboardData:fail ${(error as Error)?.message ?? error}` })\n\t\t\tonComplete?.()\n\t\t},\n\t)\n}\n\n// \u2500\u2500\u2500 Device: Network type \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type NetworkType = 'wifi' | '2g' | '3g' | '4g' | '5g' | 'none' | 'unknown'\n\ninterface NetworkInfo {\n\tonLine: boolean\n\t/** NetworkInformation.type (e.g. 'wifi' / 'ethernet' / 'cellular'). */\n\ttype?: string\n\t/** NetworkInformation.effectiveType (e.g. '4g' / '3g' / '2g' / 'slow-2g'). */\n\teffectiveType?: string\n}\n\nfunction mapEffectiveType(effectiveType?: string): NetworkType {\n\tswitch (effectiveType) {\n\t\tcase '4g':\n\t\t\treturn '4g'\n\t\tcase '3g':\n\t\t\treturn '3g'\n\t\tcase '2g':\n\t\tcase 'slow-2g':\n\t\t\treturn '2g'\n\t\tdefault:\n\t\t\treturn 'unknown'\n\t}\n}\n\n/**\n * Best-effort map from the browser's NetworkInformation to the WeChat\n * `getNetworkType` vocabulary. The host machine is almost always on wifi or\n * ethernet, so an online connection with no finer signal reports 'wifi'.\n */\nexport function resolveNetworkType(info: NetworkInfo): NetworkType {\n\tif (!info.onLine) return 'none'\n\tif (info.type === 'wifi' || info.type === 'ethernet') return 'wifi'\n\tif (info.type === 'cellular') return mapEffectiveType(info.effectiveType)\n\tif (info.effectiveType) return mapEffectiveType(info.effectiveType)\n\treturn 'wifi'\n}\n\nexport function getNetworkType(\n\tthis: MiniAppContext,\n\t{ success, complete }: { success?: unknown; complete?: unknown } = {},\n) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\tconst connection = (navigator as Navigator & {\n\t\tconnection?: { type?: string; effectiveType?: string }\n\t}).connection\n\tconst networkType = resolveNetworkType({\n\t\tonLine: navigator.onLine,\n\t\ttype: connection?.type,\n\t\teffectiveType: connection?.effectiveType,\n\t})\n\tonSuccess?.({ networkType, errMsg: 'getNetworkType:ok' })\n\tonComplete?.()\n}\n", "/**\n * Generic DOM-event \u2192 service-callback bridge.\n *\n * Container-side media APIs (audio, and in future recorder / video) need to\n * forward DOM media events on an `HTMLMediaElement` out to the mini-program\n * service layer. This helper centralises the \"bind a set of DOM events, build\n * a typed payload, invoke `fire`, return a disposer\" pattern so each media API\n * does not re-implement it.\n */\n\n/**\n * Builds the payload handed to `fire` for a given mini-program event name.\n * Receives the mini-program event name (already mapped from the DOM name) and\n * the DOM event, and returns the object delivered to the service callback.\n */\nexport type PayloadBuilder<P> = (event: string, domEvent: Event) => P\n\n/** Disposer returned by {@link bindDomEvents}; unbinds every listener. */\nexport type EventBridgeDisposer = () => void\n\n/**\n * Bind a batch of DOM events on `target`.\n *\n * @param target The EventTarget (e.g. an `HTMLAudioElement`) to listen on.\n * @param eventMap Map of DOM event name \u2192 mini-program event name.\n * @param fire The service callback; called once per DOM event with the\n * payload produced by `buildPayload`.\n * @param buildPayload Produces the typed payload for `fire`.\n * @returns A disposer that removes every listener registered by this call.\n */\nexport function bindDomEvents<P>(\n\ttarget: EventTarget,\n\teventMap: Record<string, string>,\n\tfire: (payload: P) => void,\n\tbuildPayload: PayloadBuilder<P>,\n): EventBridgeDisposer {\n\tconst registered: Array<[string, EventListener]> = []\n\n\tfor (const [domName, miniName] of Object.entries(eventMap)) {\n\t\tconst listener: EventListener = (domEvent) => {\n\t\t\tfire(buildPayload(miniName, domEvent))\n\t\t}\n\t\ttarget.addEventListener(domName, listener)\n\t\tregistered.push([domName, listener])\n\t}\n\n\treturn () => {\n\t\tfor (const [domName, listener] of registered) {\n\t\t\ttarget.removeEventListener(domName, listener)\n\t\t}\n\t\tregistered.length = 0\n\t}\n}\n", "/**\n * Devtools simulator temp-file registry.\n *\n * The simulator hands `chooseImage` / `chooseMedia` / `chooseVideo` /\n * `compressImage` etc. results back to mini-program code as paths. Historically\n * those paths were `blob:` URLs allocated via `URL.createObjectURL`, but the\n * blob: scheme is local-to-the-renderer and cannot be served back into the\n * webview's `persist:simulator` session through a `protocol.handle`. We switched\n * to a custom `difile://_tmp/{uuid}` scheme so the main process can\n * register a single `difile://` protocol handler and stream the bytes for any\n * path, regardless of which renderer originally produced it.\n *\n * To keep the renderer-side cache and the main-process byte store in sync,\n * `setTempFileSink` lets the preload bridge inject a sink that mirrors every\n * `write` / `revoke` / `revokeAll` over IPC. Tests inject a stub sink.\n */\n\nexport interface TempFileSink {\n\twrite(path: string, blob: Blob): void\n\trevoke(path: string): void\n\trevokeAll(): void\n}\n\nconst tempFiles = new Map<string, Blob>()\nlet activeSink: TempFileSink | null = null\n\nexport function setTempFileSink(sink: TempFileSink | null): void {\n\tactiveSink = sink\n}\n\nfunction cryptoRandomId(): string {\n\tconst c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto\n\tif (c && typeof c.randomUUID === 'function') return c.randomUUID()\n\treturn `${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\nexport function createTempFilePath(blob: Blob): string {\n\tconst path = `difile://_tmp/${cryptoRandomId()}`\n\ttempFiles.set(path, blob)\n\tactiveSink?.write(path, blob)\n\treturn path\n}\n\nexport function registerTempFilePath(path: string, blob: Blob): void {\n\ttempFiles.set(path, blob)\n\tactiveSink?.write(path, blob)\n}\n\nexport function revokeTempFilePath(path: string): void {\n\ttempFiles.delete(path)\n\tactiveSink?.revoke(path)\n}\n\nexport function revokeAllTempFilePaths(): void {\n\ttempFiles.clear()\n\tactiveSink?.revokeAll()\n}\n\nexport async function resolveTempFilePath(path: string): Promise<Blob> {\n\tconst cached = tempFiles.get(path)\n\tif (cached) return cached\n\n\tconst response = await fetch(path)\n\tif (!response.ok) {\n\t\tthrow new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 ${path}`)\n\t}\n\tconst blob = await response.blob()\n\t// Cache the freshly fetched blob in-memory so subsequent reads are local.\n\t// We bypass `registerTempFilePath` deliberately: this is a renderer-only\n\t// cache fill, the main-process store already has the bytes (otherwise the\n\t// fetch would not have returned them), so triggering sink.write would\n\t// produce a redundant IPC.\n\ttempFiles.set(path, blob)\n\treturn blob\n}\n\nexport function getTempFileName(path: string, blob: Blob, fallback = 'file'): string {\n\tconst named = blob as Blob & { name?: unknown }\n\tif (typeof named.name === 'string' && named.name.trim()) {\n\t\treturn named.name\n\t}\n\n\ttry {\n\t\tconst url = new URL(path, window.location.href)\n\t\tconst segment = url.pathname.split('/').filter(Boolean).pop()\n\t\tif (segment) return decodeURIComponent(segment)\n\t} catch {\n\t\t// Fall through to the caller-provided fallback.\n\t}\n\n\treturn fallback\n}\n", "/**\n * DevTools API stubs for media-related wx.xxx APIs\n * (image / video / audio).\n *\n * Each exported function is bound with `this` = MiniApp instance\n * (via AppManager.registerApi \u2192 MiniApp.invokeApi).\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindDomEvents, type EventBridgeDisposer } from './event-bridge'\nimport { bindCallbacks } from './simulator-api-helpers'\nimport { createTempFilePath } from './temp-files'\n\n// \u2500\u2500\u2500 shared file-picker scaffolding \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// chooseImage / chooseVideo / chooseMedia all drive the browser file picker\n// through a hidden `<input type=file>`: create it, wire accept/multiple/\n// capture from the caller's options, listen for `change`, and report a\n// `${apiName}:fail cancel` when the user closes the dialog without picking\n// anything. `createFilePicker` is the single authority for that scaffold;\n// each API supplies only its accept string, multiplicity, and the\n// (possibly async) processing of the picked files.\n\ntype CancelCallbacks = Pick<ReturnType<typeof bindCallbacks>, 'onFail' | 'onComplete'>\n\ninterface FilePickerConfig {\n\taccept: string\n\tmultiple: boolean\n\t/** `capture` attribute value, or `undefined` to omit it (album source, or no single-camera source requested). */\n\tcapture?: 'user' | 'environment'\n}\n\n/** Resolves the `capture` attribute from `sourceType` + `camera`: only set when the caller asked for camera-only capture. */\nfunction captureAttrFor(sourceType: string[], camera: unknown): 'user' | 'environment' | undefined {\n\tif (sourceType.length !== 1 || sourceType[0] !== 'camera') return undefined\n\treturn camera === 'front' ? 'user' : 'environment'\n}\n\n/**\n * Drives a hidden `<input type=file>` through one pick cycle. Reports\n * `${apiName}:fail cancel` + `complete` when the selection is empty;\n * otherwise hands the raw (un-truncated) `FileList` to `onPick`, which owns\n * success/fail/complete and must call `removeInput` once it is done with the\n * picker element.\n */\nfunction createFilePicker(\n\tapiName: string,\n\tconfig: FilePickerConfig,\n\tcbs: CancelCallbacks,\n\tonPick: (files: File[], removeInput: () => void) => void | Promise<void>,\n): void {\n\tconst input = document.createElement('input')\n\tinput.type = 'file'\n\tinput.accept = config.accept\n\tinput.multiple = config.multiple\n\tif (config.capture) input.setAttribute('capture', config.capture)\n\tinput.style.display = 'none'\n\tdocument.body.appendChild(input)\n\n\tconst removeInput = () => input.remove()\n\n\tinput.addEventListener('change', () => {\n\t\tconst files = Array.from(input.files || [])\n\t\tif (files.length === 0) {\n\t\t\tcbs.onFail?.({ errMsg: `${apiName}:fail cancel` })\n\t\t\tcbs.onComplete?.()\n\t\t\tremoveInput()\n\t\t\treturn\n\t\t}\n\t\tvoid onPick(files, removeInput)\n\t})\n\n\tinput.click()\n}\n\n// \u2500\u2500\u2500 Media: Image \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function chooseImage(\n\tthis: MiniAppContext,\n\t{ count = 9, sourceType, camera, success, fail, complete }: {\n\t\tcount?: number\n\t\tsizeType?: unknown\n\t\tsourceType?: unknown\n\t\tcamera?: unknown\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onComplete } = cbs\n\tconst normalizedCount = normalizeChooseMediaCount(count)\n\tconst normalizedSourceType = normalizeStringArray(sourceType, ['album', 'camera'])\n\n\tcreateFilePicker(\n\t\t'chooseImage',\n\t\t{ accept: 'image/*', multiple: normalizedCount > 1, capture: captureAttrFor(normalizedSourceType, camera) },\n\t\tcbs,\n\t\t(rawFiles, removeInput) => {\n\t\t\tconst files = rawFiles.slice(0, normalizedCount)\n\t\t\tconst tempFilePaths = files.map(f => createTempFilePath(f))\n\t\t\tconst tempFiles = files.map((f, i) => ({ path: tempFilePaths[i], size: f.size }))\n\t\t\tonSuccess?.({ tempFilePaths, tempFiles, errMsg: 'chooseImage:ok' })\n\t\t\tonComplete?.()\n\t\t\tremoveInput()\n\t\t},\n\t)\n}\n\nexport function previewImage(\n\tthis: MiniAppContext,\n\t{ urls, current, success, complete }: { urls?: string[]; current?: string; success?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\n\tif (!urls || urls.length === 0) {\n\t\tonComplete?.()\n\t\treturn\n\t}\n\n\t// Simple overlay preview\n\tconst overlay = document.createElement('div')\n\toverlay.style.cssText =\n\t\t'position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.85);display:flex;align-items:center;justify-content:center;cursor:pointer;'\n\tconst img = document.createElement('img')\n\timg.src = current || urls[0] || ''\n\timg.style.cssText = 'max-width:90%;max-height:90%;object-fit:contain;'\n\toverlay.appendChild(img)\n\toverlay.addEventListener('click', () => overlay.remove())\n\tdocument.body.appendChild(overlay)\n\n\tonSuccess?.({ errMsg: 'previewImage:ok' })\n\tonComplete?.()\n}\n\nexport function compressImage(\n\tthis: MiniAppContext,\n\t{ src, quality = 80, success, fail, complete }: {\n\t\tsrc: string\n\t\tquality?: number\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\n\tconst img = new Image()\n\timg.crossOrigin = 'anonymous'\n\timg.onload = () => {\n\t\ttry {\n\t\t\tconst canvas = document.createElement('canvas')\n\t\t\tcanvas.width = img.naturalWidth\n\t\t\tcanvas.height = img.naturalHeight\n\t\t\tconst ctx = canvas.getContext('2d')!\n\t\t\tctx.drawImage(img, 0, 0)\n\t\t\tcanvas.toBlob(\n\t\t\t\t(blob) => {\n\t\t\t\t\tif (blob) {\n\t\t\t\t\t\tconst tempFilePath = createTempFilePath(blob)\n\t\t\t\t\t\tonSuccess?.({ tempFilePath, errMsg: 'compressImage:ok' })\n\t\t\t\t\t} else {\n\t\t\t\t\t\tonFail?.({ errMsg: 'compressImage:fail compression error' })\n\t\t\t\t\t}\n\t\t\t\t\tonComplete?.()\n\t\t\t\t},\n\t\t\t\t'image/jpeg',\n\t\t\t\tquality / 100,\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tonFail?.({ errMsg: `compressImage:fail ${(error as Error).message}` })\n\t\t\tonComplete?.()\n\t\t}\n\t}\n\timg.onerror = () => {\n\t\tonFail?.({ errMsg: 'compressImage:fail image load error' })\n\t\tonComplete?.()\n\t}\n\timg.src = src\n}\n\nexport function saveCanvasTempFile(\n\tthis: MiniAppContext,\n\t{ dataURL, success, fail, complete }: {\n\t\tdataURL?: string\n\t\tfileType?: string\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\n\tif (!dataURL) {\n\t\tonFail?.({ errMsg: 'canvasToTempFilePath:fail missing dataURL' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\n\tconst match = /^data:([^;]+);base64,(.+)$/.exec(dataURL)\n\tif (!match) {\n\t\tonFail?.({ errMsg: 'canvasToTempFilePath:fail invalid data URL' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\n\ttry {\n\t\tconst mimeType = match[1]!\n\t\tconst raw = atob(match[2]!)\n\t\tconst bytes = new Uint8Array(raw.length)\n\t\tfor (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i)\n\t\tconst blob = new Blob([bytes], { type: mimeType })\n\t\tconst tempFilePath = createTempFilePath(blob)\n\t\tonSuccess?.({ tempFilePath, errMsg: 'canvasToTempFilePath:ok' })\n\t} catch (error) {\n\t\tonFail?.({ errMsg: `canvasToTempFilePath:fail ${(error as Error).message}` })\n\t}\n\tonComplete?.()\n}\n\nexport function saveImageToPhotosAlbum(\n\tthis: MiniAppContext,\n\t{ filePath, success, fail, complete }: { filePath: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\n\ttry {\n\t\tconst a = document.createElement('a')\n\t\ta.href = filePath\n\t\ta.download = 'image'\n\t\ta.click()\n\t\tonSuccess?.({ errMsg: 'saveImageToPhotosAlbum:ok' })\n\t} catch (error) {\n\t\tonFail?.({ errMsg: `saveImageToPhotosAlbum:fail ${(error as Error).message}` })\n\t}\n\tonComplete?.()\n}\n\nexport function getImageInfo(\n\tthis: MiniAppContext,\n\t{ src, success, fail, complete }: { src: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\n\tconst img = new Image()\n\timg.crossOrigin = 'anonymous'\n\timg.onload = () => {\n\t\tonSuccess?.({\n\t\t\twidth: img.naturalWidth,\n\t\t\theight: img.naturalHeight,\n\t\t\tpath: src,\n\t\t\torientation: 'up',\n\t\t\ttype: 'unknown',\n\t\t\terrMsg: 'getImageInfo:ok',\n\t\t})\n\t\tonComplete?.()\n\t}\n\timg.onerror = () => {\n\t\tonFail?.({ errMsg: 'getImageInfo:fail image load error' })\n\t\tonComplete?.()\n\t}\n\timg.src = src\n}\n\n// \u2500\u2500\u2500 Media: Video \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype MediaFileType = 'image' | 'video'\ntype ChooseMediaCamera = 'back' | 'front'\n\nconst VIDEO_METADATA_TIMEOUT_MS = 5000\nconst VIDEO_THUMBNAIL_TIMEOUT_MS = 500\n\ninterface ChooseMediaTempFile {\n\ttempFilePath: string\n\tsize: number\n\tduration: number\n\theight: number\n\twidth: number\n\tthumbTempFilePath: string\n\tfileType: MediaFileType\n\toriginalFileObj: File\n}\n\nfunction normalizeStringArray(value: unknown, fallback: string[]): string[] {\n\treturn Array.isArray(value)\n\t\t? value.filter((item): item is string => typeof item === 'string')\n\t\t: fallback\n}\n\nfunction normalizeChooseMediaCount(value: unknown): number {\n\tconst n = Number(value)\n\tif (!Number.isFinite(n)) return 9\n\treturn Math.max(1, Math.min(20, Math.floor(n)))\n}\n\nfunction getChooseMediaAccept(mediaType: string[]): string {\n\tconst wantsMix = mediaType.includes('mix')\n\tconst wantsImage = wantsMix || mediaType.includes('image')\n\tconst wantsVideo = wantsMix || mediaType.includes('video')\n\tif (wantsImage && wantsVideo) return 'image/*,video/*'\n\treturn wantsVideo ? 'video/*' : 'image/*'\n}\n\nfunction getChooseMediaResultType(files: ChooseMediaTempFile[]): 'image' | 'video' | 'mix' {\n\tconst types = new Set(files.map(file => file.fileType))\n\tif (types.size > 1) return 'mix'\n\treturn files[0]?.fileType ?? 'image'\n}\n\nfunction readImageMetadata(src: string): Promise<{ width: number; height: number }> {\n\treturn new Promise((resolve) => {\n\t\tconst img = new Image()\n\t\timg.onload = () => resolve({ width: img.naturalWidth || 0, height: img.naturalHeight || 0 })\n\t\timg.onerror = () => resolve({ width: 0, height: 0 })\n\t\timg.src = src\n\t})\n}\n\nfunction readVideoMetadata(src: string): Promise<{ width: number; height: number; duration: number; thumbTempFilePath: string }> {\n\treturn new Promise((resolve) => {\n\t\tconst video = document.createElement('video')\n\t\tlet settled = false\n\t\tlet seekTimer: ReturnType<typeof setTimeout> | undefined\n\t\tconst finish = (metadata: { width: number; height: number; duration: number; thumbTempFilePath: string }) => {\n\t\t\tif (settled) return\n\t\t\tsettled = true\n\t\t\tclearTimeout(timer)\n\t\t\tif (seekTimer) clearTimeout(seekTimer)\n\t\t\t// Detach handlers so any late-firing DOM events (e.g. onseeked from a\n\t\t\t// previously queued currentTime change) cannot reach into the now-\n\t\t\t// resolved promise and allocate fresh blob: URLs.\n\t\t\tvideo.onloadedmetadata = null\n\t\t\tvideo.onseeked = null\n\t\t\tvideo.onerror = null\n\t\t\tvideo.removeAttribute('src')\n\t\t\tvideo.load()\n\t\t\tresolve(metadata)\n\t\t}\n\t\tconst drawThumbnail = (width: number, height: number): Promise<string> => {\n\t\t\treturn new Promise((resolveThumb) => {\n\t\t\t\tlet resolved = false\n\t\t\t\tconst done = (value: string) => {\n\t\t\t\t\tif (resolved) return\n\t\t\t\t\tresolved = true\n\t\t\t\t\tresolveThumb(value)\n\t\t\t\t}\n\t\t\t\tlet canvas: HTMLCanvasElement\n\t\t\t\ttry {\n\t\t\t\t\tcanvas = document.createElement('canvas')\n\t\t\t\t\tcanvas.width = width || 1\n\t\t\t\t\tcanvas.height = height || 1\n\t\t\t\t\tconst ctx = canvas.getContext('2d')\n\t\t\t\t\tctx?.drawImage(video, 0, 0, canvas.width, canvas.height)\n\t\t\t\t} catch {\n\t\t\t\t\tdone('')\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst toDataUrlFallback = () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdone(canvas.toDataURL('image/jpeg', 0.8))\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tdone('')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst fallbackTimer = setTimeout(toDataUrlFallback, VIDEO_THUMBNAIL_TIMEOUT_MS)\n\t\t\t\ttry {\n\t\t\t\t\tcanvas.toBlob(\n\t\t\t\t\t\t(blob) => {\n\t\t\t\t\t\t\tclearTimeout(fallbackTimer)\n\t\t\t\t\t\t\t// If the outer readVideoMetadata promise already settled\n\t\t\t\t\t\t\t// (e.g. the seekTimer fired before toBlob's async callback\n\t\t\t\t\t\t\t// arrived), we MUST NOT call createTempFilePath here \u2014 that\n\t\t\t\t\t\t\t// would allocate a fresh blob: URL and register it in the\n\t\t\t\t\t\t\t// temp-files Map with no one ever revoking it. Just drop\n\t\t\t\t\t\t\t// the blob on the floor; the resolved thumbTempFilePath\n\t\t\t\t\t\t\t// has already been chosen by the timeout path.\n\t\t\t\t\t\t\tif (settled || resolved) return\n\t\t\t\t\t\t\tif (blob) {\n\t\t\t\t\t\t\t\tdone(createTempFilePath(blob))\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\ttoDataUrlFallback()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t'image/jpeg',\n\t\t\t\t\t\t0.8,\n\t\t\t\t\t)\n\t\t\t\t} catch {\n\t\t\t\t\tclearTimeout(fallbackTimer)\n\t\t\t\t\ttoDataUrlFallback()\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t\tconst timer = setTimeout(() => finish({ width: 0, height: 0, duration: 0, thumbTempFilePath: '' }), VIDEO_METADATA_TIMEOUT_MS)\n\n\t\tvideo.preload = 'metadata'\n\t\tvideo.muted = true\n\t\tvideo.onloadedmetadata = () => {\n\t\t\tconst width = video.videoWidth || 0\n\t\t\tconst height = video.videoHeight || 0\n\t\t\tconst duration = Number.isFinite(video.duration) ? video.duration : 0\n\t\t\tconst metadata = { width, height, duration, thumbTempFilePath: '' }\n\t\t\tif (!width || !height || duration <= 0) {\n\t\t\t\tfinish(metadata)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvideo.onseeked = async () => {\n\t\t\t\tif (seekTimer) {\n\t\t\t\t\tclearTimeout(seekTimer)\n\t\t\t\t\tseekTimer = undefined\n\t\t\t\t}\n\t\t\t\tconst thumbTempFilePath = await drawThumbnail(width, height)\n\t\t\t\tfinish({ ...metadata, thumbTempFilePath })\n\t\t\t}\n\t\t\tseekTimer = setTimeout(() => finish(metadata), VIDEO_THUMBNAIL_TIMEOUT_MS)\n\t\t\ttry {\n\t\t\t\tvideo.currentTime = Math.min(0.1, Math.max(0, duration - 0.01))\n\t\t\t} catch {\n\t\t\t\tfinish(metadata)\n\t\t\t}\n\t\t}\n\t\tvideo.onerror = () => finish({ width: 0, height: 0, duration: 0, thumbTempFilePath: '' })\n\t\tvideo.src = src\n\t})\n}\n\nasync function buildChooseMediaTempFile(file: File): Promise<ChooseMediaTempFile> {\n\tconst tempFilePath = createTempFilePath(file)\n\tconst fileType: MediaFileType = file.type.startsWith('video') ? 'video' : 'image'\n\n\tif (fileType === 'video') {\n\t\tconst metadata = await readVideoMetadata(tempFilePath)\n\t\treturn {\n\t\t\ttempFilePath,\n\t\t\tsize: file.size,\n\t\t\tduration: metadata.duration,\n\t\t\theight: metadata.height,\n\t\t\twidth: metadata.width,\n\t\t\tthumbTempFilePath: metadata.thumbTempFilePath,\n\t\t\tfileType,\n\t\t\toriginalFileObj: file,\n\t\t}\n\t}\n\n\tconst metadata = await readImageMetadata(tempFilePath)\n\treturn {\n\t\ttempFilePath,\n\t\tsize: file.size,\n\t\tduration: 0,\n\t\theight: metadata.height,\n\t\twidth: metadata.width,\n\t\tthumbTempFilePath: '',\n\t\tfileType,\n\t\toriginalFileObj: file,\n\t}\n}\n\nexport function chooseMedia(\n\tthis: MiniAppContext,\n\t{ count = 9, mediaType = ['image', 'video'], sourceType = ['album', 'camera'], camera = 'back', success, fail, complete }: {\n\t\tcount?: number\n\t\tmediaType?: unknown\n\t\tsourceType?: unknown\n\t\tmaxDuration?: unknown\n\t\tsizeType?: unknown\n\t\tcamera?: ChooseMediaCamera\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onFail, onComplete } = cbs\n\tconst normalizedCount = normalizeChooseMediaCount(count)\n\tconst normalizedMediaType = normalizeStringArray(mediaType, ['image', 'video'])\n\tconst normalizedSourceType = normalizeStringArray(sourceType, ['album', 'camera'])\n\n\tcreateFilePicker(\n\t\t'chooseMedia',\n\t\t{\n\t\t\taccept: getChooseMediaAccept(normalizedMediaType),\n\t\t\tmultiple: normalizedCount > 1,\n\t\t\tcapture: captureAttrFor(normalizedSourceType, camera),\n\t\t},\n\t\tcbs,\n\t\tasync (rawFiles, removeInput) => {\n\t\t\tconst files = rawFiles.slice(0, normalizedCount)\n\t\t\ttry {\n\t\t\t\tconst tempFiles = await Promise.all(files.map(buildChooseMediaTempFile))\n\t\t\t\tonSuccess?.({\n\t\t\t\t\ttempFiles,\n\t\t\t\t\ttype: getChooseMediaResultType(tempFiles),\n\t\t\t\t\tfailedCount: 0,\n\t\t\t\t\terrMsg: 'chooseMedia:ok',\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tonFail?.({ errMsg: `chooseMedia:fail ${(error as Error).message}` })\n\t\t\t} finally {\n\t\t\t\tonComplete?.()\n\t\t\t\tremoveInput()\n\t\t\t}\n\t\t},\n\t)\n}\n\nexport function chooseVideo(\n\tthis: MiniAppContext,\n\t{ sourceType, camera, success, fail, complete }: {\n\t\tsourceType?: unknown\n\t\tcompressed?: unknown\n\t\tmaxDuration?: unknown\n\t\tcamera?: unknown\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onComplete } = cbs\n\tconst normalizedSourceType = normalizeStringArray(sourceType, ['album', 'camera'])\n\n\tcreateFilePicker(\n\t\t'chooseVideo',\n\t\t{ accept: 'video/*', multiple: false, capture: captureAttrFor(normalizedSourceType, camera) },\n\t\tcbs,\n\t\tasync (files, removeInput) => {\n\t\t\tconst file = files[0]!\n\t\t\tconst tempFilePath = createTempFilePath(file)\n\t\t\tconst metadata = await readVideoMetadata(tempFilePath)\n\t\t\tonSuccess?.({\n\t\t\t\ttempFilePath,\n\t\t\t\tduration: metadata.duration,\n\t\t\t\tsize: file.size,\n\t\t\t\twidth: metadata.width,\n\t\t\t\theight: metadata.height,\n\t\t\t\terrMsg: 'chooseVideo:ok',\n\t\t\t})\n\t\t\tonComplete?.()\n\t\t\tremoveInput()\n\t\t},\n\t)\n}\n\n// \u2500\u2500\u2500 Media: Audio (container-side handlers for service-apis/audio) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// service-apis/audio/index.js (injected into the service Worker by the dimina\n// container bundle) calls invokeAPI('audioCreate', { audioId }), etc. DOM media\n// events on the container's HTMLAudioElement are bridged back to the service\n// layer via the `audioListen` handler.\n\n/** Payload delivered to the service-side dispatcher on every audio event. */\ninterface AudioEventPayload {\n\tevent: string\n\tcurrentTime: number\n\tduration: number\n\tbuffered: number\n\tpaused: boolean\n}\n\n/** DOM media event name \u2192 mini-program audio event name. */\nconst AUDIO_EVENT_MAP: Record<string, string> = {\n\tplay: 'play',\n\tpause: 'pause',\n\tended: 'ended',\n\terror: 'error',\n\ttimeupdate: 'timeUpdate',\n\twaiting: 'waiting',\n\tseeking: 'seeking',\n\tseeked: 'seeked',\n\tcanplay: 'canplay',\n}\n\nconst _newAudioInstances = new Map<number, HTMLAudioElement>()\n/** Disposers that unbind the DOM event bridge for a given audio instance. */\nconst _audioEventDisposers = new Map<number, EventBridgeDisposer>()\n/** The service-side dispatcher callback for a given audio instance. */\nconst _audioFire = new Map<number, (payload: AudioEventPayload) => void>()\n\n/** Snapshot the current playback state of an audio element. */\nfunction audioSnapshot(audio: HTMLAudioElement, event: string): AudioEventPayload {\n\treturn {\n\t\tevent,\n\t\tcurrentTime: audio.currentTime || 0,\n\t\tduration: Number.isFinite(audio.duration) ? audio.duration : 0,\n\t\tbuffered: audio.buffered.length ? audio.buffered.end(audio.buffered.length - 1) : 0,\n\t\tpaused: audio.paused,\n\t}\n}\n\nexport function audioCreate(this: MiniAppContext, { audioId }: { audioId: number }) {\n\t_newAudioInstances.set(audioId, new Audio())\n}\n\n/**\n * Persistent event-bridge registration. The service-side InnerAudioContext\n * calls this once at construction with a `keep: true` callback; the container\n * resolves a `fire` callback and binds the DOM media events of the matching\n * audio element to it.\n *\n * The dimina service `invokeAPI` runs every callback through\n * `callback.store(success, keep, evtId)` and delivers the resulting callback\n * id under the `success` field of `params` \u2014 `evtId` itself is consumed by\n * `callback.store` and never reaches the container payload. So the handler\n * resolves `fire` from `success`, exactly like every other media API.\n */\nexport function audioListen(this: MiniAppContext, { audioId, success }: { audioId: number; success: unknown }) {\n\tconst audio = _newAudioInstances.get(audioId)\n\tconst fire = this.createCallbackFunction(success) as ((payload: AudioEventPayload) => void) | undefined\n\tif (!audio || !fire) return\n\n\t_audioFire.set(audioId, fire)\n\n\t// Rebind cleanly if audioListen is somehow called twice for one instance.\n\t_audioEventDisposers.get(audioId)?.()\n\tconst dispose = bindDomEvents<AudioEventPayload>(\n\t\taudio,\n\t\tAUDIO_EVENT_MAP,\n\t\tfire,\n\t\tevent => audioSnapshot(audio, event),\n\t)\n\t_audioEventDisposers.set(audioId, dispose)\n}\n\nexport function audioSetProp(\n\tthis: MiniAppContext,\n\t{ audioId, prop, value, startTime, loop, volume, playbackRate, autoplay }: {\n\t\taudioId: number\n\t\tprop: string\n\t\tvalue: unknown\n\t\tstartTime?: number\n\t\tloop?: boolean\n\t\tvolume?: number\n\t\tplaybackRate?: number\n\t\tautoplay?: boolean\n\t},\n) {\n\tconst audio = _newAudioInstances.get(audioId)\n\tif (!audio) return\n\tswitch (prop) {\n\t\tcase 'src':\n\t\t\taudio.src = value as string\n\t\t\tif (startTime != null) audio.currentTime = startTime\n\t\t\tif (loop != null) audio.loop = loop\n\t\t\tif (volume != null) audio.volume = Math.max(0, Math.min(1, volume))\n\t\t\tif (playbackRate != null) audio.playbackRate = playbackRate\n\t\t\tif (autoplay) audio.play().catch(() => {})\n\t\t\tbreak\n\t\tcase 'startTime': audio.currentTime = Number(value) || 0; break\n\t\tcase 'autoplay': audio.autoplay = !!value; break\n\t\tcase 'loop': audio.loop = !!value; break\n\t\tcase 'volume': audio.volume = Math.max(0, Math.min(1, Number(value) || 0)); break\n\t\tcase 'playbackRate': audio.playbackRate = Number(value) || 1; break\n\t}\n}\n\nexport function audioPlay(this: MiniAppContext, { audioId, src }: { audioId: number; src?: string }) {\n\tconst audio = _newAudioInstances.get(audioId)\n\tif (!audio) return\n\tif (src && audio.src !== src) audio.src = src\n\taudio.play().catch(() => {})\n}\n\nexport function audioPause(this: MiniAppContext, { audioId }: { audioId: number }) {\n\t_newAudioInstances.get(audioId)?.pause()\n}\n\nexport function audioStop(this: MiniAppContext, { audioId }: { audioId: number }) {\n\tconst audio = _newAudioInstances.get(audioId)\n\tif (!audio) return\n\taudio.pause()\n\taudio.currentTime = 0\n\t// `stop` has no DOM equivalent \u2014 synthesise it through the bridge.\n\t_audioFire.get(audioId)?.(audioSnapshot(audio, 'stop'))\n}\n\nexport function audioSeek(this: MiniAppContext, { audioId, position }: { audioId: number; position: number }) {\n\tconst audio = _newAudioInstances.get(audioId)\n\tif (!audio) return\n\taudio.currentTime = position\n}\n\nexport function audioDestroy(this: MiniAppContext, { audioId }: { audioId: number }) {\n\t_audioEventDisposers.get(audioId)?.()\n\t_audioEventDisposers.delete(audioId)\n\t_audioFire.delete(audioId)\n\n\tconst audio = _newAudioInstances.get(audioId)\n\tif (!audio) return\n\taudio.pause()\n\taudio.removeAttribute('src')\n\taudio.load()\n\t_newAudioInstances.delete(audioId)\n}\n", "/**\n * vpath resolver.\n *\n * Spec: `docs/file-system.md` \u2014 single resolver shared by every FSM\n * entry, the renderer-side temp store, and the main-process\n * protocol.handle / disk reader.\n *\n * resolveVPath(url): { kind, writable, realPath? } | null\n *\n * - `difile://_tmp/<id>` \u2192 kind=tmp, writable=false, no realPath\n * - `difile://_store/<id>` \u2192 kind=store, writable=false, realPath in base\n * - `difile://<rel>` \u2192 kind=usr, writable=true, realPath in base\n *\n * Security invariants (the resolver returns `null` instead of throwing):\n * - any non-`difile://` scheme is rejected outright;\n * - any `..` segment (raw or URL-encoded) is rejected before canonicalize;\n * - after `path.normalize` the resulting `realPath` MUST stay inside the\n * sandbox base; otherwise `null`;\n * - reserved prefixes `_tmp/` and `_store/` are case-sensitive \u2014 `_TMP/`,\n * `_Tmp/`, etc. fall through to the user-data namespace.\n */\n\n// Main-process (Node ESM) resolves these natively. Simulator (vite browser\n// bundle) externalizes node builtins as no-op stubs \u2014 that's safe here only\n// because the simulator never enters the `store`/`usr` branches below\n// (renderer-side FSM forwards disk-backed reads/writes to main via IPC).\nimport os from 'node:os'\nimport path from 'node:path'\n\nexport type VPathKind = 'tmp' | 'store' | 'usr'\n\nexport interface ResolvedVPath {\n\tkind: VPathKind\n\t/** `tmp` and `store` are runtime-owned and read-only; `usr` is writable. */\n\twritable: boolean\n\t/**\n\t * Canonical local path on disk for `store` / `usr` kinds (always within the\n\t * USER_DATA_PATH base directory after canonicalize). `tmp` kind has no\n\t * realPath \u2014 the bytes live in the renderer-side Blob Map.\n\t */\n\trealPath?: string\n}\n\nconst DIFILE_PREFIX = 'difile://'\n\n/**\n * USER_DATA_PATH sandbox base. Looked up dynamically on every call so test\n * doubles (e.g. monkey-patching `os.homedir`) and the `DIMINA_HOME` env\n * override apply per invocation. The env override exists for headless test\n * affordance and to keep the renderer/main store sharing one base.\n */\nexport function sandboxBase(): string {\n\tconst env = (typeof process !== 'undefined' && process.env && process.env.DIMINA_HOME) || ''\n\tif (env) {\n\t\treturn path.join(env, 'files')\n\t}\n\treturn path.join(os.homedir(), '.dimina', 'files')\n}\n\nexport function resolveVPath(url: unknown): ResolvedVPath | null {\n\tif (typeof url !== 'string') return null\n\tif (!url.startsWith(DIFILE_PREFIX)) return null\n\n\tconst raw = url.slice(DIFILE_PREFIX.length)\n\tif (raw.length === 0) return null\n\n\t// Decode URL-encoded sequences first so `%2e%2e` style escapes are caught\n\t// by the segment check below.\n\tlet decoded: string\n\ttry {\n\t\tdecoded = decodeURIComponent(raw)\n\t} catch {\n\t\treturn null\n\t}\n\n\t// Reject NUL bytes (raw or %00) \u2014 Node `fs.*` throws TypeError on a NUL\n\t// in the path, bypassing the API's `fail` callback and crashing the\n\t// renderer. Pull the rejection up to the validator.\n\tif (decoded.includes('\\0')) return null\n\n\t// Developer convention: `wx.env.USER_DATA_PATH = 'difile://'` so a write\n\t// like `${USER_DATA_PATH}/foo.txt` produces `difile:///foo.txt`. Strip\n\t// leading slashes so that path-joins cleanly under the sandbox base.\n\tdecoded = decoded.replace(/^[/\\\\]+/, '')\n\tif (decoded.length === 0) return null\n\n\t// Reject any '..' or '.' segment after decoding. We split on both '/' and\n\t// '\\\\' so Windows-style separators cannot smuggle a traversal segment past\n\t// the validator on POSIX hosts.\n\tconst segments = decoded.split(/[/\\\\]+/)\n\tif (segments.some(s => s === '..' || s === '.')) return null\n\n\t// Classify by reserved prefix (case-sensitive \u2014 `_TMP/` is a user file).\n\tlet kind: VPathKind\n\tlet writable: boolean\n\tif (decoded.startsWith('_tmp/')) {\n\t\tkind = 'tmp'\n\t\twritable = false\n\t} else if (decoded.startsWith('_store/')) {\n\t\tkind = 'store'\n\t\twritable = false\n\t} else {\n\t\tkind = 'usr'\n\t\twritable = true\n\t}\n\n\t// `tmp` lives in the renderer Blob Map \u2014 no realPath.\n\tif (kind === 'tmp') {\n\t\treturn { kind, writable }\n\t}\n\n\t// `store` / `usr`: anchor under the sandbox base and assert containment\n\t// after normalization. Defense in depth against any traversal that the\n\t// segment check above missed (e.g. backslash trickery on POSIX).\n\tconst base = sandboxBase()\n\tconst joined = path.join(base, decoded)\n\tconst normalized = path.normalize(joined)\n\tif (normalized !== base && !normalized.startsWith(base + path.sep)) return null\n\n\treturn { kind, writable, realPath: normalized }\n}\n", "/**\n * DevTools API stubs for filesystem wx.xxx APIs\n * (container-side handlers for service-apis/file).\n *\n * Each exported function is bound with `this` = MiniApp instance\n * (via AppManager.registerApi \u2192 MiniApp.invokeApi).\n *\n * Contract (see `docs/file-system.md`):\n * - Every entry point routes its caller-supplied path through `resolveVPath`\n * (single resolver), which rejects non-`difile://` schemes, absolute\n * filesystem paths, and any `..` traversal.\n * - Write-class APIs (writeFile / appendFile / unlink / mkdir / rmdir /\n * truncate / rename(dest) / copyFile(dest)) refuse `difile://_tmp/*` and\n * `difile://_store/*` with `permission denied` \u2014 those namespaces are\n * runtime-owned and read-only, matching wx \u771F\u673A semantics.\n * - `fsSaveFile` returns a `difile://_store/{uuid}.{ext}` vpath, never a\n * real disk path.\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindCallbacks, notSupportedApi } from './simulator-api-helpers'\nimport { resolveVPath, type ResolvedVPath } from '../shared/vpath.js'\nimport { resolveTempFilePath } from './temp-files'\n\n/**\n * Dispatch helper: pull a `_tmp/*` Blob out of the renderer Map and hand back\n * its bytes. Throws an ENOENT-shaped Error if the URL is unknown, so callers\n * can surface a `fail` with a real not-found message.\n */\nasync function _tmpBytes(url: string): Promise<Buffer> {\n\ttry {\n\t\tconst blob = await resolveTempFilePath(url)\n\t\treturn await blobToBuffer(blob)\n\t}\n\tcatch (err) {\n\t\tconst msg = err instanceof Error ? err.message : String(err)\n\t\tthrow new Error(`ENOENT: no such file (${url}): ${msg}`, { cause: err })\n\t}\n}\n\nfunction isArrayBuffer(value: unknown): value is ArrayBuffer {\n\treturn Object.prototype.toString.call(value) === '[object ArrayBuffer]'\n}\n\nasync function blobToBuffer(blob: Blob): Promise<Buffer> {\n\tconst readable = blob as Blob & { arrayBuffer?: () => Promise<ArrayBuffer> }\n\tif (typeof readable.arrayBuffer === 'function') {\n\t\tconst ab = await readable.arrayBuffer()\n\t\treturn Buffer.from(new Uint8Array(ab))\n\t}\n\n\tif (typeof FileReader !== 'function') {\n\t\tthrow new Error('Blob cannot be read as ArrayBuffer')\n\t}\n\n\treturn new Promise((resolve, reject) => {\n\t\tconst reader = new FileReader()\n\t\treader.onerror = () => reject(reader.error ?? new Error('Blob read failed'))\n\t\treader.onload = () => {\n\t\t\tconst result = reader.result\n\t\t\tif (!isArrayBuffer(result)) {\n\t\t\t\treject(new Error('Blob read did not return an ArrayBuffer'))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresolve(Buffer.from(new Uint8Array(result)))\n\t\t}\n\t\treader.readAsArrayBuffer(blob)\n\t})\n}\n\n// In Electron renderer, Node.js built-ins are available via require.\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst _fs: typeof import('fs') = (typeof require !== 'undefined') ? require('fs') : null as unknown as typeof import('fs')\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst _path: typeof import('path') = (typeof require !== 'undefined') ? require('path') : null as unknown as typeof import('path')\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst _crypto: typeof import('crypto') = (typeof require !== 'undefined') ? require('crypto') : null as unknown as typeof import('crypto')\n\ntype FsCallbacks = ReturnType<typeof bindCallbacks>\ntype NodeStats = import('fs').Stats\ntype NodeErr = NodeJS.ErrnoException | Error | null\n\n// \u2500\u2500\u2500 shared fs-API scaffolding \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Every handler below shares the same skeleton: bail if Node `fs` isn't\n// available, resolve the caller path through the single vpath resolver\n// (bailing on invalid/unsafe paths), optionally reject read-only\n// namespaces for write-class APIs, then run a Node fs call and translate its\n// `(err, ...)` callback into the wx success/fail/complete triad. The helpers\n// below are the single authority for each of those steps so per-API bodies\n// only contain what differs between APIs (the real fs call and any extra\n// success payload fields).\n\n/** Bails with `${apiName}:fail not available in browser context` when the Node `fs` binding is absent (e.g. a browser-only build). */\nfunction guardFsAvailable(apiName: string, cbs: FsCallbacks): boolean {\n\tif (_fs) return false\n\tcbs.onFail?.({ errMsg: `${apiName}:fail not available in browser context` })\n\tcbs.onComplete?.()\n\treturn true\n}\n\n/** Resolves `p` via `resolveVPath`, or reports `${apiName}:fail invalid or unsafe path` and runs `complete`. */\nfunction resolveOrBail(p: unknown, apiName: string, cbs: FsCallbacks): ResolvedVPath | undefined {\n\tconst v = resolveVPath(p)\n\tif (v) return v\n\tcbs.onFail?.({ errMsg: `${apiName}:fail invalid or unsafe path` })\n\tcbs.onComplete?.()\n\treturn undefined\n}\n\n/**\n * Type-guards `v` as writable (narrows `realPath` to `string`), or reports\n * `${apiName}:fail permission denied` and runs `complete`. `_tmp` / `_store`\n * are runtime-owned and read-only, matching wx \u771F\u673A semantics.\n */\nfunction ensureWritable(v: ResolvedVPath, apiName: string, cbs: FsCallbacks): v is ResolvedVPath & { realPath: string } {\n\tif (v.writable && v.realPath) return true\n\tcbs.onFail?.({ errMsg: `${apiName}:fail permission denied` })\n\tcbs.onComplete?.()\n\treturn false\n}\n\n/** Fail handler for a `_tmpBytes(...).then(success, ...)` chain: reports `${apiName}:fail <message>` and runs `complete`. */\nfunction tmpFailHandler(apiName: string, cbs: FsCallbacks) {\n\treturn (err: Error) => {\n\t\tcbs.onFail?.({ errMsg: `${apiName}:fail ${err.message}` })\n\t\tcbs.onComplete?.()\n\t}\n}\n\n/**\n * Builds a Node-style `(err, ...args)` callback that translates into the wx\n * success/fail/complete triad: `err` truthy \u2192 fail with `${apiName}:fail\n * <message>`; otherwise success with `${apiName}:ok` plus whatever\n * `buildOk(...args)` contributes.\n */\nfunction nodeComplete<TArgs extends unknown[] = []>(\n\tapiName: string,\n\tcbs: FsCallbacks,\n\tbuildOk?: (...args: TArgs) => Record<string, unknown> | undefined,\n) {\n\treturn (err: NodeErr, ...args: TArgs) => {\n\t\tif (err) {\n\t\t\tcbs.onFail?.({ errMsg: `${apiName}:fail ${err.message}` })\n\t\t} else {\n\t\t\tcbs.onSuccess?.({ ...(buildOk ? buildOk(...args) : undefined), errMsg: `${apiName}:ok` })\n\t\t}\n\t\tcbs.onComplete?.()\n\t}\n}\n\n/**\n * `mkdir -p` the parent of `destReal`, then run `writeFn` (a `writeFile` /\n * `copyFile` call) through `nodeComplete`. Shared by every API that\n * materializes bytes onto disk under a possibly-not-yet-existing directory\n * (fsWriteFile, fsCopyFile's `_tmp` branch, fsSaveFile).\n */\nfunction mkdirpThenWrite(\n\tdestReal: string,\n\tapiName: string,\n\tcbs: FsCallbacks,\n\twriteFn: (done: (err: NodeErr) => void) => void,\n\tokExtra?: Record<string, unknown>,\n): void {\n\t_fs.mkdir(_path.dirname(destReal), { recursive: true }, (mkdirErr) => {\n\t\tif (mkdirErr) {\n\t\t\tcbs.onFail?.({ errMsg: `${apiName}:fail ${mkdirErr.message}` })\n\t\t\tcbs.onComplete?.()\n\t\t\treturn\n\t\t}\n\t\twriteFn(nodeComplete(apiName, cbs, () => okExtra))\n\t})\n}\n\nexport function fsAccess(\n\tthis: MiniAppContext,\n\t{ path, success, fail, complete }: { path: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsAccess', cbs)) return\n\tconst v = resolveOrBail(path, 'fsAccess', cbs)\n\tif (!v) return\n\tif (v.kind === 'tmp') {\n\t\t// _tmp existence check: renderer Map hit \u21D2 ok; miss \u21D2 ENOENT-shaped fail.\n\t\t_tmpBytes(path).then(\n\t\t\t() => { onSuccess?.({ errMsg: 'fsAccess:ok' }); onComplete?.() },\n\t\t\ttmpFailHandler('fsAccess', cbs),\n\t\t)\n\t\treturn\n\t}\n\tif (!v.realPath) {\n\t\tonFail?.({ errMsg: 'fsAccess:fail invalid path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\t_fs.access(v.realPath, _fs.constants.F_OK, nodeComplete('fsAccess', cbs))\n}\n\nexport function fsStat(\n\tthis: MiniAppContext,\n\t{ path, recursive = false, success, fail, complete }: {\n\t\tpath: string\n\t\trecursive?: boolean\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsStat', cbs)) return\n\tconst v = resolveOrBail(path, 'fsStat', cbs)\n\tif (!v) return\n\tif (v.kind === 'tmp') {\n\t\t// _tmp size is known from the Blob; mtime is 0 (no on-disk timestamp).\n\t\t_tmpBytes(path).then(\n\t\t\t(buf) => {\n\t\t\t\tonSuccess?.({\n\t\t\t\t\tstats: {\n\t\t\t\t\t\tsize: buf.length,\n\t\t\t\t\t\tmode: 0,\n\t\t\t\t\t\tlastAccessedTime: 0,\n\t\t\t\t\t\tlastModifiedTime: 0,\n\t\t\t\t\t\tisFile: true,\n\t\t\t\t\t\tisDirectory: false,\n\t\t\t\t\t},\n\t\t\t\t\terrMsg: 'fsStat:ok',\n\t\t\t\t})\n\t\t\t\tonComplete?.()\n\t\t\t},\n\t\t\ttmpFailHandler('fsStat', cbs),\n\t\t)\n\t\treturn\n\t}\n\tif (!v.realPath) {\n\t\tonFail?.({ errMsg: 'fsStat:fail invalid path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\tconst resolved = v.realPath\n\tif (recursive) {\n\t\t// Build a map of path \u2192 stat info for the directory tree\n\t\tconst statsMap: Record<string, unknown> = {}\n\t\tconst walkDir = (dir: string, cb: (err: Error | null) => void) => {\n\t\t\t_fs.readdir(dir, { withFileTypes: true }, (err, entries) => {\n\t\t\t\tif (err) { cb(err); return }\n\t\t\t\tlet pending = entries.length\n\t\t\t\tif (pending === 0) { cb(null); return }\n\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\tconst full = _path.join(dir, entry.name)\n\t\t\t\t\t_fs.stat(full, (statErr, s) => {\n\t\t\t\t\t\tif (!statErr) {\n\t\t\t\t\t\t\tstatsMap[full] = {\n\t\t\t\t\t\t\t\tsize: s.size,\n\t\t\t\t\t\t\t\tmode: s.mode,\n\t\t\t\t\t\t\t\tlastAccessedTime: s.atimeMs,\n\t\t\t\t\t\t\t\tlastModifiedTime: s.mtimeMs,\n\t\t\t\t\t\t\t\tisFile: s.isFile(),\n\t\t\t\t\t\t\t\tisDirectory: s.isDirectory(),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (entry.isDirectory()) {\n\t\t\t\t\t\t\twalkDir(full, () => { if (--pending === 0) cb(null) })\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (--pending === 0) cb(null)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t\twalkDir(resolved, (err) => {\n\t\t\tif (err) {\n\t\t\t\tonFail?.({ errMsg: `fsStat:fail ${err.message}` })\n\t\t\t} else {\n\t\t\t\tonSuccess?.({ stats: statsMap, errMsg: 'fsStat:ok' })\n\t\t\t}\n\t\t\tonComplete?.()\n\t\t})\n\t} else {\n\t\t_fs.stat(resolved, nodeComplete('fsStat', cbs, (s: NodeStats) => ({\n\t\t\tstats: {\n\t\t\t\tsize: s.size,\n\t\t\t\tmode: s.mode,\n\t\t\t\tlastAccessedTime: s.atimeMs,\n\t\t\t\tlastModifiedTime: s.mtimeMs,\n\t\t\t\tisFile: s.isFile(),\n\t\t\t\tisDirectory: s.isDirectory(),\n\t\t\t},\n\t\t})))\n\t}\n}\n\nexport function fsReadFile(\n\tthis: MiniAppContext,\n\t{ filePath, encoding, success, fail, complete }: {\n\t\tfilePath: string\n\t\tencoding?: BufferEncoding\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsReadFile', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsReadFile', cbs)\n\tif (!v) return\n\tif (v.kind === 'tmp') {\n\t\t_tmpBytes(filePath).then(\n\t\t\t(buf) => {\n\t\t\t\tconst data: Buffer | string = encoding ? buf.toString(encoding) : buf\n\t\t\t\tonSuccess?.({ data, errMsg: 'fsReadFile:ok' })\n\t\t\t\tonComplete?.()\n\t\t\t},\n\t\t\ttmpFailHandler('fsReadFile', cbs),\n\t\t)\n\t\treturn\n\t}\n\tif (!v.realPath) {\n\t\tonFail?.({ errMsg: 'fsReadFile:fail invalid path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\t_fs.readFile(v.realPath, encoding || null, nodeComplete('fsReadFile', cbs, (data: Buffer | string) => ({ data })))\n}\n\nexport function fsWriteFile(\n\tthis: MiniAppContext,\n\t{ filePath, data, encoding = 'utf8', success, fail, complete }: {\n\t\tfilePath: string\n\t\tdata: string | Uint8Array\n\t\tencoding?: BufferEncoding\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsWriteFile', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsWriteFile', cbs)\n\tif (!v) return\n\tif (!ensureWritable(v, 'fsWriteFile', cbs)) return\n\tmkdirpThenWrite(v.realPath, 'fsWriteFile', cbs, done => _fs.writeFile(v.realPath, data as string, { encoding }, done))\n}\n\nexport function fsAppendFile(\n\tthis: MiniAppContext,\n\t{ filePath, data, encoding = 'utf8', success, fail, complete }: {\n\t\tfilePath: string\n\t\tdata: string | Uint8Array\n\t\tencoding?: BufferEncoding\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsAppendFile', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsAppendFile', cbs)\n\tif (!v) return\n\tif (!ensureWritable(v, 'fsAppendFile', cbs)) return\n\t_fs.appendFile(v.realPath, data as string, { encoding }, nodeComplete('fsAppendFile', cbs))\n}\n\nexport function fsCopyFile(\n\tthis: MiniAppContext,\n\t{ srcPath, destPath, success, fail, complete }: {\n\t\tsrcPath: string\n\t\tdestPath: string\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsCopyFile', cbs)) return\n\tconst vSrc = resolveOrBail(srcPath, 'fsCopyFile', cbs)\n\tif (!vSrc) return\n\tconst vDest = resolveOrBail(destPath, 'fsCopyFile', cbs)\n\tif (!vDest) return\n\tif (!ensureWritable(vDest, 'fsCopyFile', cbs)) return\n\tif (vSrc.kind === 'tmp') {\n\t\t// Materialize the renderer Blob into the user-data\n\t\t// area. The dest writable check above already rejected _tmp / _store\n\t\t// destinations \u2014 saveFile is the documented route for tmp\u2192store.\n\t\t_tmpBytes(srcPath).then(\n\t\t\tbuf => mkdirpThenWrite(vDest.realPath, 'fsCopyFile', cbs, done => _fs.writeFile(vDest.realPath, buf, done)),\n\t\t\ttmpFailHandler('fsCopyFile', cbs),\n\t\t)\n\t\treturn\n\t}\n\tif (!vSrc.realPath) {\n\t\tonFail?.({ errMsg: 'fsCopyFile:fail invalid src path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\t_fs.copyFile(vSrc.realPath, vDest.realPath, nodeComplete('fsCopyFile', cbs))\n}\n\nexport function fsRename(\n\tthis: MiniAppContext,\n\t{ oldPath, newPath, success, fail, complete }: {\n\t\toldPath: string\n\t\tnewPath: string\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsRename', cbs)) return\n\tconst vOld = resolveOrBail(oldPath, 'fsRename', cbs)\n\tif (!vOld) return\n\tconst vNew = resolveOrBail(newPath, 'fsRename', cbs)\n\tif (!vNew) return\n\t// Rename deletes the source \u2014 both sides must be writable. _tmp / _store\n\t// reject under either role.\n\tif (!ensureWritable(vOld, 'fsRename', cbs)) return\n\tif (!ensureWritable(vNew, 'fsRename', cbs)) return\n\t_fs.rename(vOld.realPath, vNew.realPath, nodeComplete('fsRename', cbs))\n}\n\nexport function fsUnlink(\n\tthis: MiniAppContext,\n\t{ filePath, success, fail, complete }: { filePath: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsUnlink', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsUnlink', cbs)\n\tif (!v) return\n\tif (!ensureWritable(v, 'fsUnlink', cbs)) return\n\t_fs.unlink(v.realPath, nodeComplete('fsUnlink', cbs))\n}\n\nexport function fsMkdir(\n\tthis: MiniAppContext,\n\t{ dirPath, recursive = false, success, fail, complete }: {\n\t\tdirPath: string\n\t\trecursive?: boolean\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsMkdir', cbs)) return\n\tconst v = resolveOrBail(dirPath, 'fsMkdir', cbs)\n\tif (!v) return\n\tif (!ensureWritable(v, 'fsMkdir', cbs)) return\n\t_fs.mkdir(v.realPath, { recursive }, nodeComplete('fsMkdir', cbs))\n}\n\nexport function fsRmdir(\n\tthis: MiniAppContext,\n\t{ dirPath, recursive = false, success, fail, complete }: {\n\t\tdirPath: string\n\t\trecursive?: boolean\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsRmdir', cbs)) return\n\tconst v = resolveOrBail(dirPath, 'fsRmdir', cbs)\n\tif (!v) return\n\tif (!ensureWritable(v, 'fsRmdir', cbs)) return\n\t// Node 14+: rm with recursive; older Node: rmdir with recursive flag\n\tconst rmFn = (_fs as typeof _fs & { rm?: typeof _fs.rmdir }).rm ?? _fs.rmdir\n\trmFn(v.realPath, { recursive } as Parameters<typeof _fs.rmdir>[1], nodeComplete('fsRmdir', cbs))\n}\n\nexport function fsReaddir(\n\tthis: MiniAppContext,\n\t{ dirPath, success, fail, complete }: { dirPath: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsReaddir', cbs)) return\n\tconst v = resolveOrBail(dirPath, 'fsReaddir', cbs)\n\tif (!v) return\n\t// _tmp and _store are flat (id-addressed) namespaces \u2014 readdir is meaningless.\n\tif (v.kind === 'tmp' || v.kind === 'store') {\n\t\tonFail?.({ errMsg: `fsReaddir:fail ${v.kind} is a flat namespace (no dir tree)` })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\tif (!v.realPath) {\n\t\tonFail?.({ errMsg: 'fsReaddir:fail invalid path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\t_fs.readdir(v.realPath, nodeComplete('fsReaddir', cbs, (files: string[]) => ({ files })))\n}\n\nexport function fsGetFileInfo(\n\tthis: MiniAppContext,\n\t{ filePath, digestAlgorithm, success, fail, complete }: {\n\t\tfilePath: string\n\t\tdigestAlgorithm?: string\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsGetFileInfo', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsGetFileInfo', cbs)\n\tif (!v) return\n\tif (v.kind === 'tmp') {\n\t\t_tmpBytes(filePath).then(\n\t\t\t(buf) => {\n\t\t\t\tconst result: Record<string, unknown> = { size: buf.length, errMsg: 'fsGetFileInfo:ok' }\n\t\t\t\tif (digestAlgorithm) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst hash = _crypto.createHash(digestAlgorithm === 'md5' ? 'md5' : 'sha1')\n\t\t\t\t\t\thash.update(buf)\n\t\t\t\t\t\tresult.digest = hash.digest('hex')\n\t\t\t\t\t}\n\t\t\t\t\tcatch {\n\t\t\t\t\t\t// crypto unavailable \u2014 fall through without digest.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tonSuccess?.(result)\n\t\t\t\tonComplete?.()\n\t\t\t},\n\t\t\ttmpFailHandler('fsGetFileInfo', cbs),\n\t\t)\n\t\treturn\n\t}\n\tif (!v.realPath) {\n\t\tonFail?.({ errMsg: 'fsGetFileInfo:fail invalid path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\tconst resolved = v.realPath\n\t_fs.stat(resolved, (err, s) => {\n\t\tif (err) {\n\t\t\tonFail?.({ errMsg: `fsGetFileInfo:fail ${err.message}` })\n\t\t\tonComplete?.()\n\t\t\treturn\n\t\t}\n\t\tconst result: Record<string, unknown> = { size: s.size, errMsg: 'fsGetFileInfo:ok' }\n\t\tif (digestAlgorithm) {\n\t\t\t// Compute digest if crypto is available\n\t\t\ttry {\n\t\t\t\tconst hash = _crypto.createHash(digestAlgorithm === 'md5' ? 'md5' : 'sha1')\n\t\t\t\tconst stream = _fs.createReadStream(resolved)\n\t\t\t\tstream.on('data', (chunk) => hash.update(chunk as Buffer))\n\t\t\t\tstream.on('end', () => {\n\t\t\t\t\tresult.digest = hash.digest('hex')\n\t\t\t\t\tonSuccess?.(result)\n\t\t\t\t\tonComplete?.()\n\t\t\t\t})\n\t\t\t\tstream.on('error', (hashErr) => {\n\t\t\t\t\tonFail?.({ errMsg: `fsGetFileInfo:fail ${hashErr.message}` })\n\t\t\t\t\tonComplete?.()\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t} catch {\n\t\t\t\t// crypto not available, skip digest\n\t\t\t}\n\t\t}\n\t\tonSuccess?.(result)\n\t\tonComplete?.()\n\t})\n}\n\n/**\n * Save a temp file into the read-only `_store/` namespace. Returns a vpath\n * (`difile://_store/{uuid}.{ext}`) rather than a real disk path so callers\n * cannot leak the host filesystem layout.\n *\n * Scope:\n * - source must be a `difile://`-anchored vpath (validator rejects abs paths);\n * - source from `_tmp/` materializes the renderer Blob into `_store/` via the\n * renderer-Blob \u2192 main-fs copy bridge;\n * - source from `_store/` or the user-data area is copied byte-for-byte to a\n * freshly minted `_store/{uuid}.{ext}` entry under the sandbox base.\n */\nexport function fsSaveFile(\n\tthis: MiniAppContext,\n\t{ tempFilePath, filePath: _filePath, success, fail, complete }: {\n\t\ttempFilePath: string\n\t\tfilePath?: string\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tvoid _filePath\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsSaveFile', cbs)) return\n\tconst src = resolveOrBail(tempFilePath, 'fsSaveFile', cbs)\n\tif (!src) return\n\tconst ext = _path.extname(tempFilePath) || ''\n\tconst id = _crypto.randomUUID() + ext\n\tconst savedFilePath = `difile://_store/${id}`\n\tconst destResolved = resolveVPath(savedFilePath)\n\tif (!destResolved || !destResolved.realPath) {\n\t\t// Defensive: a freshly minted vpath must always resolve.\n\t\tonFail?.({ errMsg: 'fsSaveFile:fail unable to allocate destination' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\tconst destReal = destResolved.realPath\n\n\tif (src.kind === 'tmp') {\n\t\t// Materialize the renderer Blob into _store on disk.\n\t\t_tmpBytes(tempFilePath).then(\n\t\t\tbytes => mkdirpThenWrite(destReal, 'fsSaveFile', cbs, done => _fs.writeFile(destReal, bytes, done), { savedFilePath }),\n\t\t\ttmpFailHandler('fsSaveFile', cbs),\n\t\t)\n\t\treturn\n\t}\n\tif (!src.realPath) {\n\t\tonFail?.({ errMsg: 'fsSaveFile:fail invalid src path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\tmkdirpThenWrite(destReal, 'fsSaveFile', cbs, done => _fs.copyFile(src.realPath!, destReal, done), { savedFilePath })\n}\n\n/**\n * List files previously persisted by `fsSaveFile` \u2014 i.e. anything under the\n * `_store/` namespace. Returned `filePath` entries are vpaths so callers can\n * round-trip through `fsReadFile` / `fsRemoveSavedFile` without ever seeing\n * the host filesystem.\n */\nexport function fsGetSavedFileList(\n\tthis: MiniAppContext,\n\t{ success, fail, complete }: { success?: unknown; fail?: unknown; complete?: unknown } = {},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onComplete } = cbs\n\tif (guardFsAvailable('fsGetSavedFileList', cbs)) return\n\tconst storeVpath = resolveVPath('difile://_store/')\n\tconst storeDir = storeVpath?.realPath\n\tif (!storeDir) {\n\t\tonSuccess?.({ fileList: [], errMsg: 'fsGetSavedFileList:ok' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\t_fs.readdir(storeDir, (err, files) => {\n\t\tif (err) {\n\t\t\t// Directory may not exist yet \u2014 return empty list\n\t\t\tonSuccess?.({ fileList: [], errMsg: 'fsGetSavedFileList:ok' })\n\t\t\tonComplete?.()\n\t\t\treturn\n\t\t}\n\t\tlet pending = files.length\n\t\tif (pending === 0) {\n\t\t\tonSuccess?.({ fileList: [], errMsg: 'fsGetSavedFileList:ok' })\n\t\t\tonComplete?.()\n\t\t\treturn\n\t\t}\n\t\tconst fileList: Array<{ filePath: string; size: number; createTime: number }> = []\n\t\tfor (const name of files) {\n\t\t\tconst full = _path.join(storeDir, name)\n\t\t\t_fs.stat(full, (statErr, s) => {\n\t\t\t\tif (!statErr) {\n\t\t\t\t\tfileList.push({\n\t\t\t\t\t\tfilePath: `difile://_store/${name}`,\n\t\t\t\t\t\tsize: s.size,\n\t\t\t\t\t\tcreateTime: s.birthtimeMs,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tif (--pending === 0) {\n\t\t\t\t\tonSuccess?.({ fileList, errMsg: 'fsGetSavedFileList:ok' })\n\t\t\t\t\tonComplete?.()\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nexport function fsRemoveSavedFile(\n\tthis: MiniAppContext,\n\t{ filePath, success, fail, complete }: { filePath: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsRemoveSavedFile', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsRemoveSavedFile', cbs)\n\tif (!v) return\n\t// removeSavedFile is the documented exception to the `_store/` read-only\n\t// rule. Only `_store/*` entries may be removed through this API.\n\tif (v.kind !== 'store' || !v.realPath) {\n\t\tonFail?.({ errMsg: 'fsRemoveSavedFile:fail only _store/ entries may be removed' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\t_fs.unlink(v.realPath, nodeComplete('fsRemoveSavedFile', cbs))\n}\n\nexport function fsTruncate(\n\tthis: MiniAppContext,\n\t{ filePath, length = 0, success, fail, complete }: {\n\t\tfilePath: string\n\t\tlength?: number\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsTruncate', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsTruncate', cbs)\n\tif (!v) return\n\tif (!ensureWritable(v, 'fsTruncate', cbs)) return\n\t_fs.truncate(v.realPath, length, nodeComplete('fsTruncate', cbs))\n}\n\nexport const fsUnzip = notSupportedApi('fsUnzip')\n", "/**\n * DevTools API stubs for network-related wx.xxx APIs.\n *\n * Each exported function is bound with `this` = MiniApp instance\n * (via AppManager.registerApi -> MiniApp.invokeApi).\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindCallbacks } from './simulator-api-helpers'\nimport { createTempFilePath, getTempFileName, resolveTempFilePath } from './temp-files'\n\nexport function downloadFile(\n\tthis: MiniAppContext,\n\t{ url, header = {}, filePath, success, fail, complete }: {\n\t\turl: string\n\t\theader?: Record<string, string>\n\t\tfilePath?: string\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\n\tfetch(url, { headers: header })\n\t\t.then(async (response) => {\n\t\t\tif (!response.ok) throw new Error(response.statusText)\n\t\t\tconst blob = await response.blob()\n\t\t\tconst tempFilePath = createTempFilePath(blob)\n\t\t\tonSuccess?.({\n\t\t\t\ttempFilePath,\n\t\t\t\tfilePath: filePath || tempFilePath,\n\t\t\t\tstatusCode: response.status,\n\t\t\t\terrMsg: 'downloadFile:ok',\n\t\t\t})\n\t\t})\n\t\t.catch((error: Error) => {\n\t\t\tonFail?.({ errMsg: `downloadFile:fail ${error.message}` })\n\t\t})\n\t\t.finally(() => {\n\t\t\tonComplete?.()\n\t\t})\n}\n\ntype UploadFileOptions = {\n\turl: string\n\tfilePath: string\n\tname: string\n\theader?: Record<string, string>\n\tformData?: Record<string, unknown>\n\ttimeout?: number\n\tuploadId?: string\n\tprogress?: unknown\n\theadersReceived?: unknown\n\tsuccess?: unknown\n\tfail?: unknown\n\tcomplete?: unknown\n}\n\nconst uploadRequests = new Map<string, XMLHttpRequest>()\nconst uploadPendingResolves = new Set<string>()\nconst uploadAbortedBeforeStart = new Set<string>()\nlet nextUploadId = 1\n\nfunction createUploadId(): string {\n\treturn `upload_${Date.now()}_${nextUploadId++}`\n}\n\nfunction parseResponseHeaders(raw: string): Record<string, string> {\n\tconst headers: Record<string, string> = {}\n\tfor (const line of raw.trim().split(/[\\r\\n]+/)) {\n\t\tif (!line) continue\n\t\tconst index = line.indexOf(':')\n\t\tif (index <= 0) continue\n\t\tconst key = line.slice(0, index).trim()\n\t\tconst value = line.slice(index + 1).trim()\n\t\tif (key) headers[key] = value\n\t}\n\treturn headers\n}\n\nfunction appendFormData(form: FormData, formData: Record<string, unknown>): void {\n\tfor (const [key, value] of Object.entries(formData)) {\n\t\tif (value == null) continue\n\t\tif (value instanceof Blob) {\n\t\t\tform.append(key, value)\n\t\t} else if (typeof value === 'object') {\n\t\t\t// JSON.stringify for plain objects/arrays; Map/Set/RegExp/TypedArray\n\t\t\t// fall through here too and become '{}' or similar \u2014 caller should\n\t\t\t// pre-stringify if they need a specific form.\n\t\t\tform.append(key, JSON.stringify(value))\n\t\t} else {\n\t\t\tform.append(key, String(value))\n\t\t}\n\t}\n}\n\nexport function uploadFile(\n\tthis: MiniAppContext,\n\t{\n\t\turl,\n\t\tfilePath,\n\t\tname,\n\t\theader = {},\n\t\tformData = {},\n\t\ttimeout,\n\t\tuploadId = createUploadId(),\n\t\tprogress,\n\t\theadersReceived,\n\t\tsuccess,\n\t\tfail,\n\t\tcomplete,\n\t}: UploadFileOptions,\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\tconst onProgress = this.createCallbackFunction(progress)\n\tconst onHeadersReceived = this.createCallbackFunction(headersReceived)\n\n\tconst cleanup = () => {\n\t\tuploadRequests.delete(uploadId)\n\t\tuploadPendingResolves.delete(uploadId)\n\t\tuploadAbortedBeforeStart.delete(uploadId)\n\t}\n\tconst finishFail = (message: string) => {\n\t\tcleanup()\n\t\tconst result = { errMsg: `uploadFile:fail ${message}` }\n\t\tonFail?.(result)\n\t\tonComplete?.(result)\n\t}\n\n\tuploadPendingResolves.add(uploadId)\n\tvoid resolveTempFilePath(filePath)\n\t\t.then((blob) => {\n\t\t\tuploadPendingResolves.delete(uploadId)\n\t\t\tif (uploadAbortedBeforeStart.has(uploadId)) {\n\t\t\t\tfinishFail('abort')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst xhr = new XMLHttpRequest()\n\t\t\tlet settled = false\n\t\t\tlet deliveredHeaders = false\n\t\t\tuploadRequests.set(uploadId, xhr)\n\n\t\t\tconst finishSuccess = () => {\n\t\t\t\tif (settled) return\n\t\t\t\tsettled = true\n\t\t\t\tconst headers = parseResponseHeaders(xhr.getAllResponseHeaders())\n\t\t\t\tcleanup()\n\t\t\t\tconst result = {\n\t\t\t\t\tdata: typeof xhr.response === 'string' ? xhr.response : xhr.responseText,\n\t\t\t\t\tstatusCode: xhr.status,\n\t\t\t\t\theader: headers,\n\t\t\t\t\terrMsg: 'uploadFile:ok',\n\t\t\t\t}\n\t\t\t\tonSuccess?.(result)\n\t\t\t\tonComplete?.(result)\n\t\t\t}\n\t\t\tconst finishXhrFail = (message: string) => {\n\t\t\t\tif (settled) return\n\t\t\t\tsettled = true\n\t\t\t\tfinishFail(message)\n\t\t\t}\n\t\t\tconst emitHeaders = () => {\n\t\t\t\tif (deliveredHeaders) return\n\t\t\t\tdeliveredHeaders = true\n\t\t\t\tonHeadersReceived?.({ header: parseResponseHeaders(xhr.getAllResponseHeaders()) })\n\t\t\t}\n\n\t\t\txhr.open('POST', url, true)\n\t\t\tif (timeout === undefined) {\n\t\t\t\txhr.timeout = 60_000\n\t\t\t} else if (Number(timeout) > 0) {\n\t\t\t\txhr.timeout = Number(timeout)\n\t\t\t}\n\t\t\tfor (const [key, value] of Object.entries(header)) {\n\t\t\t\tif (/^(referer|content-type)$/i.test(key)) continue\n\t\t\t\txhr.setRequestHeader(key, String(value))\n\t\t\t}\n\t\t\txhr.upload.onprogress = (event) => {\n\t\t\t\tconst totalBytesExpectedToSend = event.lengthComputable ? event.total : blob.size\n\t\t\t\tconst progressValue = totalBytesExpectedToSend > 0\n\t\t\t\t\t? Math.round((event.loaded / totalBytesExpectedToSend) * 100)\n\t\t\t\t\t: 0\n\t\t\t\tonProgress?.({\n\t\t\t\t\tprogress: Math.max(0, Math.min(100, progressValue)),\n\t\t\t\t\ttotalBytesSent: event.loaded,\n\t\t\t\t\ttotalBytesExpectedToSend,\n\t\t\t\t})\n\t\t\t}\n\t\t\txhr.onreadystatechange = () => {\n\t\t\t\tif (xhr.readyState >= XMLHttpRequest.HEADERS_RECEIVED) emitHeaders()\n\t\t\t}\n\t\t\txhr.onload = () => {\n\t\t\t\temitHeaders()\n\t\t\t\tfinishSuccess()\n\t\t\t}\n\t\t\txhr.onerror = () => finishXhrFail('network error')\n\t\t\txhr.ontimeout = () => finishXhrFail('timeout')\n\t\t\txhr.onabort = () => finishXhrFail('abort')\n\n\t\t\tconst body = new FormData()\n\t\t\tappendFormData(body, formData)\n\t\t\tbody.append(name, blob, getTempFileName(filePath, blob, name || 'file'))\n\t\t\txhr.send(body)\n\t\t})\n\t\t.catch((error: Error) => {\n\t\t\tfinishFail(error.message)\n\t\t})\n}\n\nexport function uploadFileAbort(this: MiniAppContext, { uploadId }: { uploadId?: string } = {}) {\n\tif (!uploadId) return\n\tconst xhr = uploadRequests.get(uploadId)\n\tif (xhr) {\n\t\txhr.abort()\n\t\treturn\n\t}\n\tif (uploadPendingResolves.has(uploadId)) {\n\t\tuploadAbortedBeforeStart.add(uploadId)\n\t}\n}\n", "/**\n * In-renderer pub/sub store for the simulator's native UI overlays (toast,\n * loading, modal, action sheet).\n *\n * The wx.* UI handlers in `simulator-api-ui.ts` run inside the simulator\n * renderer (via runApiAsync) but cannot touch React state directly. They push\n * the desired overlay into this singleton; the `DeviceShell`-resident\n * `<UiOverlay>` subscribes and renders it inside the device frame (above the\n * page <webview>, clipped to the bezel \u2014 the same layering the status/nav bar\n * already use). User interaction (modal confirm/cancel, action-sheet tap) is\n * routed back to the handler through the `onResult` / `onSelect` callbacks the\n * handler attaches to the dialog state.\n */\n\nexport interface ToastState {\n title: string\n icon: 'success' | 'error' | 'loading' | 'none'\n image?: string\n /** ms before auto-dismiss; Infinity for showLoading (dismissed explicitly). */\n duration: number\n mask: boolean\n}\n\nexport interface ModalDialogState {\n kind: 'modal'\n title: string\n content: string\n showCancel: boolean\n cancelText: string\n cancelColor: string\n confirmText: string\n confirmColor: string\n editable: boolean\n placeholderText: string\n /** The renderer calls this when the user taps confirm/cancel. */\n onResult: (confirmed: boolean, content?: string) => void\n}\n\nexport interface ActionSheetDialogState {\n kind: 'actionSheet'\n itemList: string[]\n itemColor: string\n /** The renderer calls this with the tapped index, or -1 to cancel. */\n onSelect: (index: number) => void\n}\n\nexport interface HalfSheetDialogState {\n kind: 'halfSheet'\n islandName: string\n islandAvatar: string\n memberCount: string\n /** The renderer calls this with `true` (join) or `false` (cancel / mask tap). */\n onResult: (confirmed: boolean) => void\n}\n\nexport interface CapsuleMenuItem {\n label: string\n icon: string\n}\n\nexport interface CapsuleMenuDialogState {\n kind: 'capsuleMenu'\n appName: string\n appAvatar: string\n appVersion: string\n items: CapsuleMenuItem[]\n /** The renderer calls this with the tapped item index, or -1 to cancel. */\n onSelect: (index: number) => void\n}\n\nexport interface ShareDialogState {\n kind: 'share'\n type: 'link' | 'image'\n title: string\n desc: string\n url: string\n cover: string\n image: string\n /** The renderer calls this with the tapped platform index, or -1 to cancel. */\n onSelect: (index: number) => void\n}\n\nexport interface OpenPostDialogState {\n kind: 'openPost'\n islandName: string\n islandImage: string\n /** The renderer calls this with `true` (confirm) or `false` (cancel). */\n onResult: (confirmed: boolean) => void\n}\n\nexport type DialogState =\n | ModalDialogState\n | ActionSheetDialogState\n | HalfSheetDialogState\n | CapsuleMenuDialogState\n | ShareDialogState\n | OpenPostDialogState\n\nexport interface UiOverlayState {\n toast: ToastState | null\n dialog: DialogState | null\n}\n\ntype Listener = (state: UiOverlayState) => void\n\nclass UiOverlayBus {\n private state: UiOverlayState = { toast: null, dialog: null }\n private readonly listeners = new Set<Listener>()\n\n getState(): UiOverlayState {\n return this.state\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n showToast(toast: ToastState): void {\n this.set({ ...this.state, toast })\n }\n\n hideToast(): void {\n this.set({ ...this.state, toast: null })\n }\n\n /**\n * Clear `toast` only if it is still the active one. The toast auto-dismiss\n * timer holds a reference to the toast it scheduled; a newer showToast may\n * have replaced it in the meantime, and a blind `hideToast()` from the stale\n * timer would wrongly clear the new toast.\n */\n dismissToast(toast: ToastState): void {\n if (this.state.toast === toast) this.hideToast()\n }\n\n showDialog(dialog: DialogState): void {\n this.set({ ...this.state, dialog })\n }\n\n hideDialog(): void {\n this.set({ ...this.state, dialog: null })\n }\n\n private set(next: UiOverlayState): void {\n this.state = next\n for (const listener of this.listeners) listener(next)\n }\n}\n\nexport const uiOverlayBus = new UiOverlayBus()\n", "/**\n * DevTools implementations for the WeChat-style interaction APIs that native\n * platforms (iOS / Android / Harmony) provide but the web container lacks:\n * showToast / hideToast / showLoading / hideLoading / showModal /\n * showActionSheet.\n *\n * Each handler is bound with `this` = MiniApp instance (via\n * AppManager.registerApi \u2192 MiniApp.invokeApi). The overlay is rendered by the\n * DeviceShell-resident `<UiOverlay>`; these handlers only push state into\n * `uiOverlayBus` and fire the success/fail/complete callbacks \u2014 immediately for\n * toast/loading, and on user interaction for modal/actionSheet (defaults and\n * verdict strings mirror the Android `InteractionApi`).\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindCallbacks } from './simulator-api-helpers'\nimport { uiOverlayBus } from './ui-overlay-bus'\n\ninterface ToastOpts {\n title?: string\n icon?: 'success' | 'error' | 'loading' | 'none'\n image?: string\n duration?: number\n mask?: boolean\n success?: unknown\n fail?: unknown\n complete?: unknown\n}\n\nexport function showToast(this: MiniAppContext, opts: ToastOpts = {}) {\n const { onSuccess, onComplete } = bindCallbacks(this, opts)\n uiOverlayBus.showToast({\n title: opts.title ?? '',\n icon: opts.icon ?? 'success',\n image: opts.image,\n duration: opts.duration ?? 1500,\n mask: opts.mask ?? false,\n })\n onSuccess?.({ errMsg: 'showToast:ok' })\n onComplete?.()\n}\n\nexport function hideToast(this: MiniAppContext, opts: { success?: unknown; complete?: unknown } = {}) {\n const { onSuccess, onComplete } = bindCallbacks(this, opts)\n uiOverlayBus.hideToast()\n onSuccess?.({ errMsg: 'hideToast:ok' })\n onComplete?.()\n}\n\nexport function showLoading(\n this: MiniAppContext,\n opts: { title?: string; mask?: boolean; success?: unknown; complete?: unknown } = {},\n) {\n const { onSuccess, onComplete } = bindCallbacks(this, opts)\n uiOverlayBus.showToast({\n title: opts.title ?? '',\n icon: 'loading',\n duration: Infinity,\n mask: opts.mask ?? false,\n })\n onSuccess?.({ errMsg: 'showLoading:ok' })\n onComplete?.()\n}\n\nexport function hideLoading(this: MiniAppContext, opts: { success?: unknown; complete?: unknown } = {}) {\n const { onSuccess, onComplete } = bindCallbacks(this, opts)\n uiOverlayBus.hideToast()\n onSuccess?.({ errMsg: 'hideLoading:ok' })\n onComplete?.()\n}\n\ninterface ModalOpts {\n title?: string\n content?: string\n showCancel?: boolean\n cancelText?: string\n cancelColor?: string\n confirmText?: string\n confirmColor?: string\n editable?: boolean\n placeholderText?: string\n success?: unknown\n fail?: unknown\n complete?: unknown\n}\n\nexport function showModal(this: MiniAppContext, opts: ModalOpts = {}) {\n const { onSuccess, onComplete } = bindCallbacks(this, opts)\n const editable = opts.editable ?? false\n // Guard against a double resolution (rapid double-tap / a re-render firing the\n // handler twice) settling the callbacks more than once.\n let settled = false\n uiOverlayBus.showDialog({\n kind: 'modal',\n title: opts.title ?? '',\n content: opts.content ?? '',\n showCancel: opts.showCancel ?? true,\n cancelText: opts.cancelText ?? '\u53D6\u6D88',\n cancelColor: opts.cancelColor ?? '#000000',\n confirmText: opts.confirmText ?? '\u786E\u5B9A',\n confirmColor: opts.confirmColor ?? '#576B95',\n editable,\n placeholderText: opts.placeholderText ?? '',\n onResult: (confirmed, content) => {\n if (settled) return\n settled = true\n uiOverlayBus.hideDialog()\n const result: Record<string, unknown> = {\n confirm: confirmed,\n cancel: !confirmed,\n errMsg: 'showModal:ok',\n }\n if (editable) result.content = content ?? ''\n onSuccess?.(result)\n onComplete?.()\n },\n })\n}\n\ninterface ActionSheetOpts {\n itemList?: string[]\n itemColor?: string\n success?: unknown\n fail?: unknown\n complete?: unknown\n}\n\nexport function showActionSheet(this: MiniAppContext, opts: ActionSheetOpts = {}) {\n const { onSuccess, onFail, onComplete } = bindCallbacks(this, opts)\n let settled = false\n uiOverlayBus.showDialog({\n kind: 'actionSheet',\n itemList: opts.itemList ?? [],\n itemColor: opts.itemColor ?? '#000000',\n onSelect: (index) => {\n if (settled) return\n settled = true\n uiOverlayBus.hideDialog()\n if (index === -1) {\n onFail?.({ errMsg: 'showActionSheet:fail cancel' })\n } else {\n onSuccess?.({ tapIndex: index, errMsg: 'showActionSheet:ok' })\n }\n onComplete?.()\n },\n })\n}\n\ninterface ShareOpts {\n type?: string\n title?: string\n desc?: string\n url?: string\n cover?: string\n image?: string\n success?: unknown\n fail?: unknown\n complete?: unknown\n}\n\nexport function share(this: MiniAppContext, opts: ShareOpts = {}) {\n const { onSuccess, onFail, onComplete } = bindCallbacks(this, opts)\n let settled = false\n const shareType = (opts.type === 'image' ? 'image' : 'link') as 'link' | 'image'\n uiOverlayBus.showDialog({\n kind: 'share',\n type: shareType,\n title: opts.title ?? '',\n desc: opts.desc ?? '',\n url: opts.url ?? '',\n cover: opts.cover ?? '',\n image: opts.image ?? '',\n onSelect: (index) => {\n if (settled) return\n settled = true\n uiOverlayBus.hideDialog()\n if (index === -1) {\n onFail?.({ errMsg: 'share:fail cancel' })\n } else {\n onSuccess?.({ errMsg: 'share:ok' })\n }\n onComplete?.()\n },\n })\n}\n\ninterface OpenPostOpts {\n islandId?: string\n appId?: string\n islandName?: string\n islandImage?: string\n joined?: boolean\n bizData?: string\n spuId?: string\n files?: string\n success?: unknown\n fail?: unknown\n complete?: unknown\n}\n\nexport function openPost(this: MiniAppContext, opts: OpenPostOpts = {}) {\n const { onSuccess, onFail, onComplete } = bindCallbacks(this, opts)\n let settled = false\n uiOverlayBus.showDialog({\n kind: 'openPost',\n islandName: opts.islandName ?? '',\n islandImage: opts.islandImage ?? '',\n onResult: (confirmed) => {\n if (settled) return\n settled = true\n uiOverlayBus.hideDialog()\n if (confirmed) {\n onSuccess?.({ islandId: opts.islandId ?? '', errMsg: 'openPost:ok' })\n } else {\n onFail?.({ errMsg: 'openPost:fail cancel' })\n }\n onComplete?.()\n },\n })\n}\n\ninterface JoinIslandOpts {\n islandName?: string\n islandAvatar?: string\n memberCount?: string\n success?: unknown\n fail?: unknown\n complete?: unknown\n}\n\nexport function joinIsland(this: MiniAppContext, opts: JoinIslandOpts = {}) {\n const { onSuccess, onFail, onComplete } = bindCallbacks(this, opts)\n let settled = false\n uiOverlayBus.showDialog({\n kind: 'halfSheet',\n islandName: opts.islandName ?? 'Mock Island',\n islandAvatar: opts.islandAvatar ?? '',\n memberCount: opts.memberCount ?? '128',\n onResult: (confirmed) => {\n if (settled) return\n settled = true\n uiOverlayBus.hideDialog()\n if (confirmed) {\n onSuccess?.({ errMsg: 'joinIsland:ok' })\n } else {\n onFail?.({ errMsg: 'joinIsland:fail cancel' })\n }\n onComplete?.()\n },\n })\n}\n", "/**\n * DevTools API stubs for wx.xxx APIs that exist on native platforms\n * (iOS / Android / Harmony) but are missing in the web container.\n *\n * Each exported function is bound with `this` = MiniApp instance\n * (via AppManager.registerApi \u2192 MiniApp.invokeApi).\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindCallbacks } from './simulator-api-helpers'\nimport {\n\tsetStorageSync,\n\tgetStorageSync,\n\tremoveStorageSync,\n\tclearStorageSync,\n\tgetStorageInfoSync,\n\tsetStorage,\n\tgetStorage,\n\tremoveStorage,\n\tclearStorage,\n\tgetStorageInfo,\n} from './simulator-api-storage'\nimport {\n\thideKeyboard,\n\tadjustPosition,\n\tmakePhoneCall,\n\tchooseContact,\n\taddPhoneContact,\n\tvibrateShort,\n\tvibrateLong,\n\tscanCode,\n\tgetClipboardData,\n\tsetClipboardData,\n\tgetNetworkType,\n} from './simulator-api-device'\nimport {\n\tchooseImage,\n\tpreviewImage,\n\tcompressImage,\n\tsaveCanvasTempFile,\n\tsaveImageToPhotosAlbum,\n\tgetImageInfo,\n\tchooseMedia,\n\tchooseVideo,\n\taudioCreate,\n\taudioListen,\n\taudioSetProp,\n\taudioPlay,\n\taudioPause,\n\taudioStop,\n\taudioSeek,\n\taudioDestroy,\n} from './simulator-api-media'\nimport {\n\tfsAccess,\n\tfsStat,\n\tfsReadFile,\n\tfsWriteFile,\n\tfsAppendFile,\n\tfsCopyFile,\n\tfsRename,\n\tfsUnlink,\n\tfsMkdir,\n\tfsRmdir,\n\tfsReaddir,\n\tfsGetFileInfo,\n\tfsSaveFile,\n\tfsGetSavedFileList,\n\tfsRemoveSavedFile,\n\tfsTruncate,\n\tfsUnzip,\n} from './simulator-api-fs'\nimport {\n\tdownloadFile,\n\tuploadFile,\n\tuploadFileAbort,\n} from './simulator-api-network'\nexport {\n\tdownloadFile,\n\tuploadFile,\n\tuploadFileAbort,\n} from './simulator-api-network'\nimport {\n\tshowToast,\n\thideToast,\n\tshowLoading,\n\thideLoading,\n\tshowModal,\n\tshowActionSheet,\n\tshare,\n\topenPost,\n\tjoinIsland,\n} from './simulator-api-ui'\n\n// \u2500\u2500\u2500 Base \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function canIUse(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown }) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\t// In devtools all standard APIs are considered available.\n\tonSuccess?.(true)\n\tonComplete?.()\n\t// Also return synchronously for callers that use the return value.\n\treturn true\n}\n\nexport function getWindowInfo(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\n\tconst { wb, di, dev, pixelRatio, screenWidth, screenHeight, windowWidth, windowHeight } = readWindowMetrics(this)\n\tconst bar = this.parent?.getStatusBarRect?.() ?? { height: dev?.statusBarHeight ?? 0 }\n\tconst statusBarHeight = (di['statusBarHeight'] as number | undefined) ?? bar.height\n\n\tconst info = {\n\t\tpixelRatio, screenWidth, screenHeight, windowWidth, windowHeight,\n\t\tstatusBarHeight,\n\t\tsafeArea: {\n\t\t\twidth: wb.width,\n\t\t\theight: wb.height - statusBarHeight,\n\t\t\ttop: statusBarHeight,\n\t\t\tbottom: wb.height,\n\t\t\tleft: 0,\n\t\t\tright: wb.width,\n\t\t},\n\t}\n\tonSuccess?.(info)\n\tonComplete?.()\n\treturn info\n}\n\nexport function getSystemSetting(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\n\tconst info = {\n\t\tbluetoothEnabled: false,\n\t\tlocationEnabled: true,\n\t\twifiEnabled: true,\n\t\tdeviceOrientation: 'portrait',\n\t}\n\tonSuccess?.(info)\n\tonComplete?.()\n\treturn info\n}\n\n// \u2500\u2500\u2500 System Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction readWindowMetrics(miniApp: MiniAppContext) {\n\t// Priority unchanged: __deviceInfo \u2192 host DOM rect. Only the last-resort\n\t// fallback follows the CURRENTLY emulated device (SimulatorMiniApp tracks\n\t// boot config device + live DEVICE_CHANGE) instead of a hardcoded 375x812.\n\tconst dev = miniApp.getDeviceMetrics?.()\n\tconst wb = miniApp.parent?.el?.querySelector('.dimina-native-webview__root')?.getBoundingClientRect()\n\t\t?? { width: dev?.screenWidth ?? 375, height: dev?.screenHeight ?? 812 }\n\tconst di = (window as Window & { __deviceInfo?: Record<string, unknown> }).__deviceInfo || {}\n\treturn {\n\t\twb,\n\t\tdi,\n\t\tdev,\n\t\tpixelRatio: (di['pixelRatio'] as number | undefined) || dev?.pixelRatio || window.devicePixelRatio || 2,\n\t\tscreenWidth: (di['screenWidth'] as number | undefined) || wb.width,\n\t\tscreenHeight: (di['screenHeight'] as number | undefined) || wb.height,\n\t\twindowWidth: wb.width,\n\t\twindowHeight: wb.height,\n\t}\n}\n\nfunction buildSystemInfo(miniApp: MiniAppContext) {\n\tconst { wb, di, dev, pixelRatio, screenWidth, screenHeight, windowWidth, windowHeight } = readWindowMetrics(miniApp)\n\tconst statusBarHeight = (di['statusBarHeight'] as number | undefined) ?? dev?.statusBarHeight ?? 0\n\t// Bottom inset sourced from safeAreaInsets.bottom (the single source \u2014 the\n\t// legacy flat `safeAreaBottom` field is decommissioned).\n\tconst bottomInset = (di['safeAreaInsets'] as { bottom?: number } | undefined)?.bottom\n\t\t?? dev?.safeAreaInsets?.bottom ?? 0\n\n\treturn {\n\t\tbrand: di['brand'] || 'devtools',\n\t\tmodel: di['model'] || 'devtools',\n\t\tpixelRatio, screenWidth, screenHeight, windowWidth, windowHeight,\n\t\tstatusBarHeight,\n\t\tlanguage: 'zh_CN',\n\t\tversion: '8.0.5',\n\t\tsystem: di['system'] || 'iOS 16.0',\n\t\tplatform: di['platform'] || 'ios',\n\t\tfontSizeSetting: 16,\n\t\tSDKVersion: '3.0.0',\n\t\tdeviceOrientation: 'portrait',\n\t\tsafeArea: {\n\t\t\twidth: wb.width,\n\t\t\theight: wb.height - statusBarHeight - bottomInset,\n\t\t\ttop: statusBarHeight,\n\t\t\tbottom: wb.height - bottomInset,\n\t\t\tleft: 0,\n\t\t\tright: wb.width,\n\t\t},\n\t}\n}\n\nexport function getSystemInfoAsync(this: MiniAppContext, opts: { success?: unknown; complete?: unknown }) {\n\tconst { success, complete } = opts\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\tonSuccess?.(buildSystemInfo(this))\n\tonComplete?.()\n}\n\nexport function getSystemInfo(this: MiniAppContext, opts: { success?: unknown; complete?: unknown }) {\n\tgetSystemInfoAsync.call(this, opts)\n}\n\nexport function getSystemInfoSync(this: MiniAppContext) {\n\treturn buildSystemInfo(this)\n}\n\n// \u2500\u2500\u2500 Open API: Account Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function getAccountInfoSync(this: MiniAppContext) {\n\treturn {\n\t\tminiProgram: {\n\t\t\tappId: this.appId || '',\n\t\t\tenvVersion: 'develop',\n\t\t\tversion: '',\n\t\t},\n\t}\n}\n\n// \u2500\u2500\u2500 Collect all APIs into a map \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// `opts: never` (not the wider `unknown`) so every handler below \u2014 each typed\n// with its OWN specific opts shape (`getSystemInfoAsync`'s `{ success?, complete? }`,\n// `canIUse`'s `string`, \u2026) \u2014 remains assignable into this map: a function\n// parameter is checked contravariantly, and `never` is assignable into any\n// concrete opts type, whereas `unknown` (the caller-side \"any value\" type)\n// would reject every narrower handler signature here. Callers of this map\n// always cast to a caller-appropriate handler type before invoking (see\n// simulator-app.tsx / main-api-runner.ts) \u2014 this declaration only has to\n// typecheck the object literal itself.\nexport const simulatorApis: Record<string, (this: MiniAppContext, opts: never) => unknown> = {\n\t// Base\n\tcanIUse,\n\tgetSystemInfo,\n\tgetSystemInfoAsync,\n\tgetSystemInfoSync,\n\tgetWindowInfo,\n\tgetSystemSetting,\n\t// UI: interaction\n\tshowToast,\n\thideToast,\n\tshowLoading,\n\thideLoading,\n\tshowModal,\n\tshowActionSheet,\n\tshare,\n\topenPost,\n\tjoinIsland,\n\t// Network\n\tdownloadFile,\n\tuploadFile,\n\tuploadFileAbort,\n\t// Storage (sync)\n\tsetStorageSync,\n\tgetStorageSync,\n\tremoveStorageSync,\n\tclearStorageSync,\n\tgetStorageInfoSync,\n\t// Storage (async)\n\tsetStorage,\n\tgetStorage,\n\tremoveStorage,\n\tclearStorage,\n\tgetStorageInfo,\n\t// Open API\n\tgetAccountInfoSync,\n\t// Device\n\thideKeyboard,\n\tadjustPosition,\n\tmakePhoneCall,\n\tchooseContact,\n\taddPhoneContact,\n\tvibrateShort,\n\tvibrateLong,\n\tscanCode,\n\tgetClipboardData,\n\tsetClipboardData,\n\tgetNetworkType,\n\t// Media: Image\n\tchooseImage,\n\tpreviewImage,\n\tcompressImage,\n\tsaveCanvasTempFile,\n\tsaveImageToPhotosAlbum,\n\tgetImageInfo,\n\t// Media: Video\n\tchooseMedia,\n\tchooseVideo,\n\t// Media: Audio (service-apis/audio)\n\taudioCreate,\n\taudioListen,\n\taudioSetProp,\n\taudioPlay,\n\taudioPause,\n\taudioStop,\n\taudioSeek,\n\taudioDestroy,\n\t// Filesystem (service-apis/file)\n\tfsAccess,\n\tfsStat,\n\tfsReadFile,\n\tfsWriteFile,\n\tfsAppendFile,\n\tfsCopyFile,\n\tfsRename,\n\tfsUnlink,\n\tfsMkdir,\n\tfsRmdir,\n\tfsReaddir,\n\tfsGetFileInfo,\n\tfsSaveFile,\n\tfsGetSavedFileList,\n\tfsRemoveSavedFile,\n\tfsTruncate,\n\tfsUnzip,\n}\n"],
|
|
4
|
+
"sourcesContent": ["/**\n * Preload bridge for the main window, settings window, settings overlay\n * view, and popover overlay view. Exposes a minimal, typed `window.devtools`\n * surface so the renderer never has to call `window.require('electron')`.\n *\n * This file is bundled by esbuild to CJS at `dist/preload/windows/main.js`\n * (see package.json `build:preload`). It must remain a leaf module \u2014 do\n * NOT import other preload files via `.js` ESM specifiers; everything goes\n * through the Electron `contextBridge` / `ipcRenderer` runtime.\n *\n * Channel governance lives in the main process: `sender-policy.ts` plus the\n * per-handler zod schemas decide what's accepted. The preload layer is a\n * thin pass-through \u2014 a renderer inventing channel names will just hit a\n * main-side rejection, so duplicating an allowlist here added no real\n * defense for a devtool that loads user-trusted mini-programs.\n */\nimport { contextBridge, ipcRenderer, type IpcRendererEvent } from 'electron'\nimport { BRIDGE_CHANNELS as C, SIMULATOR_EVENTS as E } from '../../shared/bridge-channels.js'\nimport type {\n ApiCallPayload,\n ApiResponsePayload,\n} from '../../shared/bridge-channels.js'\nimport { runApiAsync } from '../runtime/main-api-runner.js'\nimport { simulatorApis } from '../../simulator/simulator-api.js'\n\nconst api = {\n ipc: {\n invoke: (channel: string, ...args: unknown[]) => ipcRenderer.invoke(channel, ...args),\n // Synchronous round-trip. Blocks the renderer until the main process sets\n // `event.returnValue` \u2014 used only for the editor's beforeunload flush, where\n // the write MUST land before the page is torn down (an async invoke can't).\n sendSync: (channel: string, ...args: unknown[]) => ipcRenderer.sendSync(channel, ...args),\n send: (channel: string, ...args: unknown[]) => {\n ipcRenderer.send(channel, ...args)\n },\n on: (\n channel: string,\n listener: (event: IpcRendererEvent, ...args: unknown[]) => void,\n ) => {\n ipcRenderer.on(channel, listener)\n return () => ipcRenderer.removeListener(channel, listener)\n },\n once: (\n channel: string,\n listener: (event: IpcRendererEvent, ...args: unknown[]) => void,\n ) => {\n ipcRenderer.once(channel, listener)\n },\n removeListener: (\n channel: string,\n listener: (...args: unknown[]) => void,\n ) => {\n ipcRenderer.removeListener(channel, listener)\n },\n },\n}\n\n// All host windows that load this preload run with `contextIsolation: true`\n// (main / settings / view-manager), so `contextBridge` is the path that\n// reaches the renderer's `window`. The simulator <webview> uses a separate\n// preload (`simulator.ts`).\ncontextBridge.exposeInMainWorld('devtools', api)\n\n// Native-host fallback: bridge-router forwards any invokeAPI name not\n// registered in `ctx.simulatorApis` to the simulator's webContents via\n// E.API_CALL. When the *main* window doubles as the simulator (the default\n// path for e2e callers that drive `dmb:spawn` without an explicit\n// simulatorWcId), the listener must live here in the main-window preload\n// because the renderer side has no DeviceShell mounted to install one.\nipcRenderer.on(E.API_CALL, (_event, payload: ApiCallPayload) => {\n void runApiAsync(\n simulatorApis as Record<string, (this: unknown, params?: unknown) => unknown | Promise<unknown>>,\n payload.name,\n payload.params,\n ).then((verdict) => {\n const ack: ApiResponsePayload = {\n appSessionId: payload.appSessionId,\n requestId: payload.requestId,\n ok: verdict.ok,\n result: verdict.result,\n errMsg: verdict.errMsg,\n }\n ipcRenderer.send(C.API_RESPONSE, ack)\n })\n})\n\n// Test-only hatch: e2e helpers emit synthetic IPC events on the renderer's\n// ipcRenderer instance to trigger handlers registered via `devtools.ipc.on`\n// without round-tripping through the main process. Gated on NODE_ENV so\n// production builds never expose it.\nif (process.env.NODE_ENV === 'test') {\n contextBridge.exposeInMainWorld('__testIpc', {\n emit: (channel: string, ...args: unknown[]) => {\n ;(ipcRenderer as unknown as { emit: (c: string, ...a: unknown[]) => void }).emit(channel, ...args)\n },\n })\n}\n\nexport type DevtoolsApi = typeof api\n", "import type { NativeDeviceInfo } from './ipc-channels.js'\n\nexport const BRIDGE_CHANNELS = {\n SPAWN: 'dmb:spawn',\n DISPOSE: 'dmb:dispose',\n PAGE_OPEN: 'dmb:page:open',\n PAGE_CLOSE: 'dmb:page:close',\n PAGE_LIFECYCLE: 'dmb:page:lifecycle',\n NAV_CALLBACK: 'dmb:nav:callback',\n SERVICE_INVOKE: 'dmb:service:invoke',\n SERVICE_PUBLISH: 'dmb:service:publish',\n RENDER_INVOKE: 'dmb:render:invoke',\n RENDER_PUBLISH: 'dmb:render:publish',\n TO_SERVICE: 'dmb:to-service',\n TO_RENDER: 'dmb:to-render',\n SIMULATOR_API: 'dmb:simulator-api',\n /** simulator \u2192 main: ack of an API_CALL request (carries success/fail args). */\n API_RESPONSE: 'dmb:api:response',\n /**\n * simulator webview preload \u2192 main (sendSync): \"is native-host mode on?\".\n * The guest preload can't read the launch `process.env`, so it asks main\n * (which can) at install time. Reply is `e.returnValue = boolean`.\n */\n NATIVE_HOST_ENABLED: 'dmb:native-host-enabled',\n /**\n * simulator (DeviceShell) \u2192 main: the visible top-of-stack page bridgeId.\n * Main has no z-order concept \u2014 the active page lives only in DeviceShell's\n * ShellState \u2014 so devtools panels / automation that must target \"the current\n * page's render webContents\" resolve it through this signal. Fire-and-forget.\n */\n ACTIVE_PAGE: 'dmb:active-page',\n /**\n * simulator (DeviceShell) \u2192 main: the FULL ordered page stack (bottom\u2192top)\n * whenever it changes. Main has no stack of its own (it only learns the\n * active bridgeId via ACTIVE_PAGE), so automation's `App.getPageStack` needs\n * this to report multi-page stacks. Fire-and-forget.\n */\n PAGE_STACK: 'dmb:page-stack',\n} as const\n\nexport const SIMULATOR_EVENTS = {\n DOM_READY: 'simulator:dom-ready',\n NAV_BAR: 'simulator:navigation-bar',\n NAV_ACTION: 'simulator:nav-action',\n TAB_ACTION: 'simulator:tab-action',\n /** main \u2192 simulator: invoke a wx.* API on the simulator-resident MiniApp. */\n API_CALL: 'simulator:api-call',\n /**\n * main \u2192 simulator: the renderer toolbar picked a different device. Carries a\n * NativeDeviceInfo; DeviceShell resizes the bezel + re-renders status bar /\n * notch. The race-free INITIAL device rides NativeHostConfig.device (read\n * synchronously at preload bridge-install); this event covers live changes.\n */\n DEVICE_CHANGE: 'simulator:device-change',\n /**\n * main \u2192 simulator: a watcher rebuild finished; boot a NEW app session for\n * the carried url ({@link RelaunchPayload}) IN PLACE, keeping the live\n * DeviceShell painted, and swap once the new session's root page reports\n * DOM_READY (ready-then-swap soft reload). Main sends this only when the\n * shell is live+ready; otherwise the renderer falls back to the hard\n * attachNativeSimulator rebuild.\n */\n RELAUNCH: 'simulator:relaunch',\n} as const\n\n/**\n * `simulator:relaunch` payload. `url` is a full simulator URL (same format as\n * the simulator page's own location / AttachNative), carrying the appId and\n * the page route the new session must boot at.\n */\nexport interface RelaunchPayload {\n url: string\n}\n\nexport const CHANNELS = BRIDGE_CHANNELS\n\n/**\n * Reply to a `NATIVE_HOST_ENABLED` sendSync. Main supplies the render-host\n * file:// URLs (computed with node:path/url, which the simulator webview's\n * preload lacks) so the preload can build the native-host bridge.\n */\nexport interface NativeHostConfig {\n enabled: boolean\n renderHostHtmlUrl: string\n renderPreloadUrl: string\n /**\n * The currently-selected device, if the renderer already pushed it before the\n * simulator WCV's preload installed (it does \u2014 SetDeviceInfo precedes\n * AttachNative). DeviceShell reads this as its initial device so it never\n * mounts with the wrong bezel size while waiting for the first DEVICE_CHANGE.\n * Absent only on the pre-spawn default path.\n */\n device?: NativeDeviceInfo\n}\n\nexport type BridgeChannel = typeof BRIDGE_CHANNELS[keyof typeof BRIDGE_CHANNELS]\n\nexport type BridgeTarget = 'service' | 'render' | 'container'\n\nexport type BridgeMessageType =\n | 'loadResource'\n | 'serviceResourceLoaded'\n | 'renderResourceLoaded'\n | 'resourceLoaded'\n | 'firstRender'\n | 'appShow'\n | 'appHide'\n | 'stackShow'\n | 'stackHide'\n | 'pageShow'\n | 'pageHide'\n | 'pageReady'\n | 'pageUnload'\n | 'pageScroll'\n | 'pageResize'\n | 'pageRouteDone'\n | 'mC'\n | 'mR'\n | 'mU'\n | 't'\n | 'u'\n | 'ub'\n | 'triggerCallback'\n | 'invokeAPI'\n | 'h5SdkAction'\n | 'componentError'\n | 'domReady'\n | 'print'\n | 'renderHostReady'\n | 'serviceHostError'\n | string\n\nexport interface MessageEnvelope<TBody extends Record<string, unknown> = Record<string, unknown>> {\n type: BridgeMessageType\n target: BridgeTarget\n body: TBody\n}\n\nexport interface HostEnvSnapshot {\n brand: string\n model: string\n platform: string\n system: string\n version: string\n SDKVersion: string\n pixelRatio: number\n screenWidth: number\n screenHeight: number\n windowWidth: number\n windowHeight: number\n statusBarHeight: number\n language: string\n theme: string\n [key: string]: unknown\n}\n\n/**\n * Map the renderer's logical device metrics onto the subset of a\n * HostEnvSnapshot the service-host window's `getSystemInfoSync` consumes.\n * `windowHeight` excludes the status bar (the page area); `windowWidth` has no\n * horizontal chrome. Zoom is intentionally absent \u2014 it is a display scale, not\n * a logical-size change.\n *\n * Single source of truth for the device\u2192host-env mapping, shared by the live\n * `SetDeviceInfo` IPC path (main/ipc/simulator.ts) and the per-spawn host-env\n * seeding (main/ipc/bridge-router.ts). Keeping them identical guarantees a\n * service-host RESPAWN after a device change reports the same dims the live\n * update pushed \u2014 otherwise a respawn would silently revert to the boot device.\n */\nexport function deviceInfoToHostEnv(d: NativeDeviceInfo): Partial<HostEnvSnapshot> {\n return {\n brand: d.brand,\n model: d.model,\n system: d.system,\n platform: d.platform,\n pixelRatio: d.pixelRatio,\n screenWidth: d.screenWidth,\n screenHeight: d.screenHeight,\n windowWidth: d.screenWidth,\n windowHeight: Math.max(0, d.screenHeight - d.statusBarHeight),\n statusBarHeight: d.statusBarHeight,\n }\n}\n\n/**\n * Subset of WeChat page `window` config plus tabBar list, parsed from\n * `app-config.json` (`{app:{window,tabBar,pages,entryPagePath}, modules:{[pagePath]:{...flat page.json fields,root?}}}`).\n * `modules[pagePath]` is FLAT \u2014 the compiler assigns the page's own `.json` file\n * content as-is (see `dimina/fe/packages/compiler/src/env.js` `collectionPageJson()`),\n * not wrapped under a `.window` key. Mirrors mergePageConfig in dimina-fe:\n * page-level keys override app-level.\n */\nexport interface PageWindowConfig {\n navigationBarTitleText?: string\n navigationBarBackgroundColor?: string\n navigationBarTextStyle?: 'black' | 'white'\n navigationStyle?: 'default' | 'custom'\n homeButton?: boolean\n backgroundColor?: string\n backgroundTextStyle?: 'dark' | 'light'\n enablePullDownRefresh?: boolean\n disableScroll?: boolean\n [key: string]: unknown\n}\n\nexport interface TabBarItem {\n pagePath: string\n text?: string\n iconPath?: string\n selectedIconPath?: string\n}\n\nexport interface TabBarConfig {\n color?: string\n selectedColor?: string\n backgroundColor?: string\n borderStyle?: 'black' | 'white'\n position?: 'bottom' | 'top'\n custom?: boolean\n list: TabBarItem[]\n}\n\nexport interface AppManifest {\n entryPagePath: string\n pages: string[]\n tabBar?: TabBarConfig\n /**\n * Provenance of this manifest: `'app-config'` when built from a real\n * compiled `app-config.json` (its `pages` list is authoritative, so mount\n * gates can validate membership against it); `'fallback'` when\n * app-config.json was unreachable and the manifest is a single-page stand-in\n * built from the spawn request. Mount gates only enforce against\n * `'app-config'` \u2014 a `'fallback'` manifest has no compiled truth to check\n * membership against, so it must let every page/nav target through.\n */\n source: 'app-config' | 'fallback'\n}\n\nexport interface SpawnRequest {\n simulatorWcId?: number\n appId: string\n bridgeId?: string\n pagePath?: string\n scene?: number\n query?: Record<string, unknown>\n apiNamespaces?: string[]\n hostEnvSnapshot?: Partial<HostEnvSnapshot>\n pkgRoot?: string\n root?: string\n /**\n * Base URL of the dev server that serves the compiled mini-app, i.e. the\n * SAME origin the simulator page was loaded from (`http://localhost:<port>/`).\n * The dev server statically serves `<appId>/<root>/\u2026` (app-config.json,\n * logic.js, page bundles, styles) \u2014 exactly what the default dimina-fe\n * `<webview>` fetches. The native-host render + service hosts source every\n * resource from here, so we don't need a second resource server or a local\n * compiled-output path. Trailing slash expected.\n */\n resourceBaseUrl?: string\n}\n\nexport interface SpawnResult {\n appSessionId: string\n bridgeId: string\n /** The pagePath originally requested (unchanged even when a fallback applied \u2014 see `resolvedPagePath`). */\n pagePath: string\n /**\n * The pagePath the root PageSession was ACTUALLY spawned with. Equal to\n * `pagePath` unless `pageFallbackApplied` is true, in which case the request\n * was absent from `manifest.pages` and this is `manifest.entryPagePath`\n * (or `manifest.pages[0]` when even that isn't a member). Callers that cache\n * \"the current page\" (e.g. `SimulatorMiniApp`) must reconcile against this,\n * not the original request.\n */\n resolvedPagePath: string\n /** Whether `resolvedPagePath` differs from the request because it was absent from the compiled manifest. Always false for a `'fallback'` manifest (nothing to validate against). */\n pageFallbackApplied: boolean\n serviceWcId: number\n resourceBaseUrl: string\n manifest: AppManifest\n rootWindowConfig: PageWindowConfig\n}\n\nexport interface PageOpenRequest {\n appSessionId: string\n pagePath: string\n query?: Record<string, unknown>\n bridgeId?: string\n}\n\nexport interface PageOpenResult {\n bridgeId: string\n pagePath: string\n windowConfig: PageWindowConfig\n isTab: boolean\n}\n\nexport interface PageClosePayload {\n bridgeId: string\n}\n\nexport interface DisposePayload {\n /** AppSession root bridgeId (legacy) or any page bridgeId. */\n bridgeId: string\n}\n\nexport type PageLifecycleEvent =\n | 'pageShow'\n | 'pageHide'\n | 'pageUnload'\n | 'stackShow'\n | 'stackHide'\n | 'appShow'\n | 'appHide'\n\nexport interface PageLifecyclePayload {\n appSessionId: string\n bridgeId: string\n event: PageLifecycleEvent\n}\n\n/**\n * Sent by simulator after a routing action (navigateTo etc.) completes so the\n * main process can call sendCallback() against the original service-issued\n * success/fail/complete ids.\n */\nexport interface NavCallbackPayload {\n appSessionId: string\n ok: boolean\n errMsg: string\n callbacks: { success?: unknown; fail?: unknown; complete?: unknown }\n}\n\nexport interface ServiceInvokePayload {\n bridgeId: string\n msg: MessageEnvelope\n}\n\nexport interface ServicePublishPayload {\n bridgeId: string\n targetBridgeId?: string\n msg: MessageEnvelope\n}\n\n/**\n * `dmb:active-page` \u2014 simulator (DeviceShell) \u2192 main. Records which page is the\n * visible top-of-stack so main-side services (WXML/element-inspect, automation)\n * can resolve \"the active page's render webContents\" by bridgeId.\n */\nexport interface ActivePagePayload {\n appSessionId: string\n bridgeId: string\n}\n\n/**\n * `dmb:page-stack` \u2014 simulator (DeviceShell) \u2192 main. The full ordered page\n * stack (bottom\u2192top) so `App.getPageStack` can report multi-page stacks.\n */\nexport interface PageStackEntry {\n pagePath: string\n query: Record<string, unknown>\n}\n\nexport interface PageStackPayload {\n appSessionId: string\n stack: PageStackEntry[]\n}\n\nexport interface RenderInvokePayload {\n bridgeId: string\n msg: MessageEnvelope\n}\n\nexport interface RenderPublishPayload {\n bridgeId: string\n msg: MessageEnvelope\n}\n\nexport interface ToServicePayload {\n msg: MessageEnvelope\n}\n\nexport interface ToRenderPayload {\n msg: MessageEnvelope\n}\n\nexport interface SimulatorApiInvokePayload {\n bridgeId: string\n name: string\n params: unknown\n}\n\n/**\n * `simulator:nav-action` \u2014 main \u2192 simulator window, carries router/tabBar\n * intentions. Simulator owns the visual stack state and decides whether to\n * push/pop webviews; it then calls back PAGE_LIFECYCLE + NAV_CALLBACK.\n */\nexport interface NavActionPayload {\n appSessionId: string\n bridgeId: string\n name: 'navigateTo' | 'navigateBack' | 'redirectTo' | 'reLaunch' | 'switchTab'\n params: Record<string, unknown>\n callbacks: { success?: unknown; fail?: unknown; complete?: unknown }\n}\n\n/**\n * `simulator:api-call` \u2014 main \u2192 simulator. Fallback path used when an\n * invokeAPI name is not registered in the main-process `ctx.simulatorApis`\n * registry: the simulator-resident MiniApp owns the wx.* handler (it can\n * read DOM, open file pickers, etc.), so we forward the call there and wait\n * for an `API_RESPONSE` ack.\n */\nexport interface ApiCallPayload {\n appSessionId: string\n bridgeId: string\n requestId: string\n name: string\n params: Record<string, unknown>\n callbacks: { success?: unknown; fail?: unknown; complete?: unknown }\n}\n\n/**\n * `dmb:api:response` \u2014 simulator \u2192 main. Ack for an `API_CALL`. Main looks\n * up `requestId` in its pending map and fires the original service-side\n * success/fail/complete callbacks accordingly.\n */\nexport interface ApiResponsePayload {\n appSessionId: string\n requestId: string\n ok: boolean\n /** Argument the simulator-side success/fail callback was invoked with. */\n result?: unknown\n errMsg?: string\n /**\n * Persistent-subscription marker (`keep: true` APIs, e.g.\n * `audioListen`). When set, this response is one of potentially many\n * fires of the same subscription: main re-fires the service-side success\n * callback but keeps the pendingApiCall alive (does not delete it or fire\n * `complete`) so subsequent fires of the same `requestId` are delivered.\n * Cleanup happens on page/app teardown instead.\n */\n keep?: boolean\n}\n\n/**\n * `simulator:tab-action` \u2014 main \u2192 simulator window, carries dynamic TabBar\n * API calls. Simulator updates TabBar React state and acknowledges via\n * NAV_CALLBACK so the service-side wx.* callback fires.\n */\nexport interface TabActionPayload {\n appSessionId: string\n bridgeId: string\n name:\n | 'setTabBarStyle'\n | 'setTabBarItem'\n | 'showTabBar'\n | 'hideTabBar'\n | 'setTabBarBadge'\n | 'removeTabBarBadge'\n | 'showTabBarRedDot'\n | 'hideTabBarRedDot'\n params: Record<string, unknown>\n callbacks: { success?: unknown; fail?: unknown; complete?: unknown }\n}\n", "/**\n * Native-host API_CALL handler installed inside the main-window preload.\n *\n * Goal: let the main devtools window double as a simulator when the\n * bridge-router forwards `simulator:api-call` to its webContents (the\n * default path in e2e tests, where `dmb:spawn` is invoked from mainWindow\n * directly with no explicit `simulatorWcId`).\n *\n * Unlike `simulator/main.tsx` (which boots a full DeviceShell + SimulatorMiniApp),\n * this runner is renderer-agnostic: it just owns a name\u2192handler table plus\n * a sentinel-based callback capture identical in shape to the simulator's\n * `runApiAsync`. The MiniAppContext stub it constructs is the smallest one\n * the built-in handlers expect (`appId`, `createCallbackFunction`, parent\n * with `el.querySelector` returning the simulated viewport rect).\n */\nimport type { MiniAppContext } from '../../simulator/types.js'\n\n// No `this` parameter: handlers are always invoked through `.call(ctx, \u2026)`\n// below with an explicit context object, so the declared type never needs to\n// constrain (or widen away) the caller's `this`.\ntype LooseApiHandler = (params?: unknown) => unknown | Promise<unknown>\n\nexport interface ApiRunVerdict {\n ok: boolean\n result?: unknown\n errMsg?: string\n}\n\ninterface ApiRunnerContext extends MiniAppContext {\n /** Dummy properties some upstream handlers introspect. */\n appId: string\n}\n\nfunction makeBaseContext(): ApiRunnerContext {\n // Minimal viewport rect: enough for handlers that read\n // `parent.el.querySelector('.dimina-native-webview__root').getBoundingClientRect()`\n // (see simulator-api.ts readWindowMetrics).\n const fakeRect = { width: 390, height: 844, x: 0, y: 0, top: 0, left: 0, right: 390, bottom: 844 }\n const fakeRoot = {\n getBoundingClientRect: () => fakeRect,\n } as unknown as Element\n return {\n appId: '',\n createCallbackFunction: () => undefined,\n parent: {\n el: {\n querySelector: () => fakeRoot,\n } as unknown as Element,\n getStatusBarRect: () => ({ height: 0 }),\n },\n }\n}\n\nexport function runApiAsync(\n handlers: Record<string, LooseApiHandler | undefined>,\n name: string,\n params: unknown,\n): Promise<ApiRunVerdict> {\n const handler = handlers[name]\n if (!handler) {\n return Promise.resolve({ ok: false, errMsg: `${name}:fail no handler` })\n }\n\n return new Promise<ApiRunVerdict>((resolve) => {\n let resolved = false\n const finish = (verdict: ApiRunVerdict): void => {\n if (resolved) return\n resolved = true\n resolve(verdict)\n }\n\n const SUCCESS = Symbol('main-cb-success')\n const FAIL = Symbol('main-cb-fail')\n const COMPLETE = Symbol('main-cb-complete')\n\n const base = makeBaseContext()\n const ctx: ApiRunnerContext = {\n ...base,\n createCallbackFunction(id: unknown) {\n if (id === undefined || id === null) return undefined\n return (...args: unknown[]) => {\n const arg = args[0]\n if (id === SUCCESS) {\n finish({ ok: true, result: arg })\n } else if (id === FAIL) {\n const errMsg =\n arg && typeof arg === 'object' && 'errMsg' in (arg as Record<string, unknown>)\n ? String((arg as { errMsg?: unknown }).errMsg)\n : `${name}:fail`\n finish({ ok: false, errMsg, result: arg })\n }\n }\n },\n }\n\n const userParams =\n params && typeof params === 'object' && !Array.isArray(params)\n ? { ...(params as Record<string, unknown>) }\n : {}\n const hadSuccess = userParams.success !== undefined && userParams.success !== null\n const hadFail = userParams.fail !== undefined && userParams.fail !== null\n const hadComplete = userParams.complete !== undefined && userParams.complete !== null\n\n userParams.success = SUCCESS\n userParams.fail = FAIL\n if (hadComplete) userParams.complete = COMPLETE\n\n try {\n const ret = (handler as LooseApiHandler).call(ctx, userParams)\n if (ret && typeof (ret as PromiseLike<unknown>).then === 'function') {\n Promise.resolve(ret as PromiseLike<unknown>).then(\n (r) => finish({ ok: true, result: r }),\n (err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err)\n finish({ ok: false, errMsg: `${name}:fail ${msg}` })\n },\n )\n return\n }\n if (!hadSuccess && !hadFail) {\n finish({ ok: true, result: ret })\n }\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err)\n finish({ ok: false, errMsg: `${name}:fail ${msg}` })\n }\n })\n}\n", "import type { MiniAppContext } from './types'\n\n/**\n * Resolve the success / fail / complete callbacks of a wx.* API options\n * object in one step. Each runs through ctx.createCallbackFunction so it is\n * invoked with the proper this-binding; a missing option yields undefined.\n */\nexport function bindCallbacks(\n ctx: MiniAppContext,\n opts: { success?: unknown; fail?: unknown; complete?: unknown },\n) {\n return {\n onSuccess: ctx.createCallbackFunction(opts.success),\n onFail: ctx.createCallbackFunction(opts.fail),\n onComplete: ctx.createCallbackFunction(opts.complete),\n }\n}\n\n/**\n * Build a wx.* API stub for a capability the simulator cannot provide. The\n * returned function reports `${apiName}:fail not supported in simulator`\n * through the fail callback, then runs complete.\n */\nexport function notSupportedApi(\n apiName: string,\n): (this: MiniAppContext, opts?: { fail?: unknown; complete?: unknown }) => void {\n return function (this: MiniAppContext, opts: { fail?: unknown; complete?: unknown } = {}) {\n const { onFail, onComplete } = bindCallbacks(this, opts)\n onFail?.({ errMsg: `${apiName}:fail not supported in simulator` })\n onComplete?.()\n }\n}\n", "/**\n * DevTools API stubs for wx.xxx storage APIs (sync + async).\n *\n * Each exported function is bound with `this` = MiniApp instance\n * (via AppManager.registerApi \u2192 MiniApp.invokeApi).\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindCallbacks } from './simulator-api-helpers'\n\n// \u2500\u2500\u2500 shared storage primitives \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Every sync/async pair below (set/get/remove/clear/info) shares the same\n// underlying localStorage layout: keys are namespaced per appId, values are\n// JSON-encoded objects / stringified primitives. These helpers are the\n// single authority for that layout so the sync and async variants cannot\n// drift apart.\n\nfunction storageKeyOf(appId: string, key: string): string {\n\treturn `${appId}_${key}`\n}\n\n/** Full (still-prefixed) localStorage keys belonging to `appId`. */\nfunction collectAppKeys(appId: string): string[] {\n\tconst prefix = `${appId}_`\n\tconst keys: string[] = []\n\tfor (let i = 0; i < localStorage.length; i++) {\n\t\tconst k = localStorage.key(i)\n\t\tif (k && k.startsWith(prefix)) keys.push(k)\n\t}\n\treturn keys\n}\n\nfunction writeEntry(appId: string, key: string, data: unknown): void {\n\tconst dataString = typeof data === 'object' ? JSON.stringify(data) : String(data)\n\tlocalStorage.setItem(storageKeyOf(appId, key), dataString)\n}\n\n/** Reads one entry, JSON-parsing when possible. `undefined` means the key was never set \u2014 distinct from a stored empty string. */\nfunction readEntry(appId: string, key: string): { data: unknown } | undefined {\n\tconst raw = localStorage.getItem(storageKeyOf(appId, key))\n\tif (raw === null) return undefined\n\ttry { return { data: JSON.parse(raw) as unknown } } catch { return { data: raw } }\n}\n\nfunction clearAppKeys(appId: string): void {\n\tcollectAppKeys(appId).forEach(k => localStorage.removeItem(k))\n}\n\nfunction storageInfoOf(appId: string): { keys: string[]; currentSize: number; limitSize: number } {\n\tconst prefix = `${appId}_`\n\tconst keys: string[] = []\n\tlet currentSize = 0\n\tfor (const fullKey of collectAppKeys(appId)) {\n\t\tkeys.push(fullKey.slice(prefix.length))\n\t\tconst item = localStorage.getItem(fullKey)\n\t\tcurrentSize += item ? item.length * 2 : 0\n\t}\n\treturn { keys, currentSize, limitSize: 10 * 1024 * 1024 }\n}\n\n// \u2500\u2500\u2500 Storage (sync) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function setStorageSync(this: MiniAppContext, { key, data }: { key: string; data: unknown }) {\n\twriteEntry(this.appId, key, data)\n}\n\nexport function getStorageSync(this: MiniAppContext, { key }: { key: string }) {\n\t// wx \u771F\u673A: a missing key returns {data: ''}, never undefined or a fail.\n\treturn readEntry(this.appId, key) ?? { data: '' }\n}\n\nexport function removeStorageSync(this: MiniAppContext, { key }: { key: string }) {\n\tlocalStorage.removeItem(storageKeyOf(this.appId, key))\n}\n\nexport function clearStorageSync(this: MiniAppContext) {\n\tclearAppKeys(this.appId)\n}\n\nexport function getStorageInfoSync(this: MiniAppContext) {\n\treturn storageInfoOf(this.appId)\n}\n\n// \u2500\u2500\u2500 Storage (async) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function setStorage(\n\tthis: MiniAppContext,\n\t{ key, data, success, fail, complete }: { key: string; data: unknown; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\ttry {\n\t\twriteEntry(this.appId, key, data)\n\t\tonSuccess?.({ errMsg: 'setStorage:ok' })\n\t} catch (e) {\n\t\tonFail?.({ errMsg: `setStorage:fail ${(e as Error).message}` })\n\t}\n\tonComplete?.()\n}\n\nexport function getStorage(\n\tthis: MiniAppContext,\n\t{ key, success, fail, complete }: { key: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\t// Unlike getStorageSync, the async variant fails (not {data: ''}) on a\n\t// missing key \u2014 this is the documented wx.getStorage contract.\n\tconst entry = readEntry(this.appId, key)\n\tif (!entry) {\n\t\tonFail?.({ errMsg: 'getStorage:fail data not found' })\n\t} else {\n\t\tonSuccess?.({ data: entry.data, errMsg: 'getStorage:ok' })\n\t}\n\tonComplete?.()\n}\n\nexport function removeStorage(\n\tthis: MiniAppContext,\n\t{ key, success, fail, complete }: { key: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\ttry {\n\t\tlocalStorage.removeItem(storageKeyOf(this.appId, key))\n\t\tonSuccess?.({ errMsg: 'removeStorage:ok' })\n\t} catch (e) {\n\t\tonFail?.({ errMsg: `removeStorage:fail ${(e as Error).message}` })\n\t}\n\tonComplete?.()\n}\n\nexport function clearStorage(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\tclearAppKeys(this.appId)\n\tonSuccess?.({ errMsg: 'clearStorage:ok' })\n\tonComplete?.()\n}\n\nexport function getStorageInfo(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\tconst info = storageInfoOf(this.appId)\n\tonSuccess?.({ ...info, errMsg: 'getStorageInfo:ok' })\n\tonComplete?.()\n}\n", "/**\n * DevTools API stubs for device-related wx.xxx APIs\n * (keyboard / phone / contact / vibrate / scan).\n *\n * Each exported function is bound with `this` = MiniApp instance\n * (via AppManager.registerApi \u2192 MiniApp.invokeApi).\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindCallbacks, notSupportedApi } from './simulator-api-helpers'\n\n// \u2500\u2500\u2500 Device: Keyboard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function hideKeyboard(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\t// In web, blur the active element to dismiss virtual keyboard\n\tif (document.activeElement && typeof (document.activeElement as HTMLElement).blur === 'function') {\n\t\t;(document.activeElement as HTMLElement).blur()\n\t}\n\tonSuccess?.({ errMsg: 'hideKeyboard:ok' })\n\tonComplete?.()\n}\n\nexport function adjustPosition(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\tonSuccess?.({ errMsg: 'adjustPosition:ok' })\n\tonComplete?.()\n}\n\n// \u2500\u2500\u2500 Device: Phone \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function makePhoneCall(\n\tthis: MiniAppContext,\n\t{ phoneNumber, success, fail, complete }: { phoneNumber: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\n\ttry {\n\t\twindow.open(`tel:${phoneNumber}`)\n\t\tonSuccess?.({ errMsg: 'makePhoneCall:ok' })\n\t} catch (error) {\n\t\tonFail?.({ errMsg: `makePhoneCall:fail ${(error as Error).message}` })\n\t}\n\tonComplete?.()\n}\n\n// \u2500\u2500\u2500 Device: Contact (stub) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const chooseContact = notSupportedApi('chooseContact')\n\nexport const addPhoneContact = notSupportedApi('addPhoneContact')\n\n// \u2500\u2500\u2500 Device: Vibrate (stub) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function vibrateShort(this: MiniAppContext, { success, complete }: { type?: string; success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\t// Navigator.vibrate is available in some browsers\n\tif (navigator.vibrate) navigator.vibrate(15)\n\tonSuccess?.({ errMsg: 'vibrateShort:ok' })\n\tonComplete?.()\n}\n\nexport function vibrateLong(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\tif (navigator.vibrate) navigator.vibrate(400)\n\tonSuccess?.({ errMsg: 'vibrateLong:ok' })\n\tonComplete?.()\n}\n\n// \u2500\u2500\u2500 Device: Scan (stub) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport const scanCode = notSupportedApi('scanCode')\n\n// \u2500\u2500\u2500 Device: Clipboard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function getClipboardData(\n\tthis: MiniAppContext,\n\t{ success, fail, complete }: { success?: unknown; fail?: unknown; complete?: unknown } = {},\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\treturn navigator.clipboard.readText().then(\n\t\t(data) => {\n\t\t\tonSuccess?.({ data, errMsg: 'getClipboardData:ok' })\n\t\t\tonComplete?.()\n\t\t},\n\t\t(error: unknown) => {\n\t\t\tonFail?.({ errMsg: `getClipboardData:fail ${(error as Error)?.message ?? error}` })\n\t\t\tonComplete?.()\n\t\t},\n\t)\n}\n\nexport function setClipboardData(\n\tthis: MiniAppContext,\n\t{ data, success, fail, complete }: { data?: string; success?: unknown; fail?: unknown; complete?: unknown } = {},\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\t// Parity with the native ClipboardApi: `data` is required.\n\tif (typeof data !== 'string') {\n\t\tonFail?.({ errMsg: 'setClipboardData:fail data is required' })\n\t\tonComplete?.()\n\t\treturn Promise.resolve()\n\t}\n\treturn navigator.clipboard.writeText(data).then(\n\t\t() => {\n\t\t\tonSuccess?.({ errMsg: 'setClipboardData:ok' })\n\t\t\tonComplete?.()\n\t\t},\n\t\t(error: unknown) => {\n\t\t\tonFail?.({ errMsg: `setClipboardData:fail ${(error as Error)?.message ?? error}` })\n\t\t\tonComplete?.()\n\t\t},\n\t)\n}\n\n// \u2500\u2500\u2500 Device: Network type \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type NetworkType = 'wifi' | '2g' | '3g' | '4g' | '5g' | 'none' | 'unknown'\n\ninterface NetworkInfo {\n\tonLine: boolean\n\t/** NetworkInformation.type (e.g. 'wifi' / 'ethernet' / 'cellular'). */\n\ttype?: string\n\t/** NetworkInformation.effectiveType (e.g. '4g' / '3g' / '2g' / 'slow-2g'). */\n\teffectiveType?: string\n}\n\nfunction mapEffectiveType(effectiveType?: string): NetworkType {\n\tswitch (effectiveType) {\n\t\tcase '4g':\n\t\t\treturn '4g'\n\t\tcase '3g':\n\t\t\treturn '3g'\n\t\tcase '2g':\n\t\tcase 'slow-2g':\n\t\t\treturn '2g'\n\t\tdefault:\n\t\t\treturn 'unknown'\n\t}\n}\n\n/**\n * Best-effort map from the browser's NetworkInformation to the WeChat\n * `getNetworkType` vocabulary. The host machine is almost always on wifi or\n * ethernet, so an online connection with no finer signal reports 'wifi'.\n */\nexport function resolveNetworkType(info: NetworkInfo): NetworkType {\n\tif (!info.onLine) return 'none'\n\tif (info.type === 'wifi' || info.type === 'ethernet') return 'wifi'\n\tif (info.type === 'cellular') return mapEffectiveType(info.effectiveType)\n\tif (info.effectiveType) return mapEffectiveType(info.effectiveType)\n\treturn 'wifi'\n}\n\nexport function getNetworkType(\n\tthis: MiniAppContext,\n\t{ success, complete }: { success?: unknown; complete?: unknown } = {},\n) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\tconst connection = (navigator as Navigator & {\n\t\tconnection?: { type?: string; effectiveType?: string }\n\t}).connection\n\tconst networkType = resolveNetworkType({\n\t\tonLine: navigator.onLine,\n\t\ttype: connection?.type,\n\t\teffectiveType: connection?.effectiveType,\n\t})\n\tonSuccess?.({ networkType, errMsg: 'getNetworkType:ok' })\n\tonComplete?.()\n}\n", "/**\n * Generic DOM-event \u2192 service-callback bridge.\n *\n * Container-side media APIs (audio, and in future recorder / video) need to\n * forward DOM media events on an `HTMLMediaElement` out to the mini-program\n * service layer. This helper centralises the \"bind a set of DOM events, build\n * a typed payload, invoke `fire`, return a disposer\" pattern so each media API\n * does not re-implement it.\n */\n\n/**\n * Builds the payload handed to `fire` for a given mini-program event name.\n * Receives the mini-program event name (already mapped from the DOM name) and\n * the DOM event, and returns the object delivered to the service callback.\n */\nexport type PayloadBuilder<P> = (event: string, domEvent: Event) => P\n\n/** Disposer returned by {@link bindDomEvents}; unbinds every listener. */\nexport type EventBridgeDisposer = () => void\n\n/**\n * Bind a batch of DOM events on `target`.\n *\n * @param target The EventTarget (e.g. an `HTMLAudioElement`) to listen on.\n * @param eventMap Map of DOM event name \u2192 mini-program event name.\n * @param fire The service callback; called once per DOM event with the\n * payload produced by `buildPayload`.\n * @param buildPayload Produces the typed payload for `fire`.\n * @returns A disposer that removes every listener registered by this call.\n */\nexport function bindDomEvents<P>(\n\ttarget: EventTarget,\n\teventMap: Record<string, string>,\n\tfire: (payload: P) => void,\n\tbuildPayload: PayloadBuilder<P>,\n): EventBridgeDisposer {\n\tconst registered: Array<[string, EventListener]> = []\n\n\tfor (const [domName, miniName] of Object.entries(eventMap)) {\n\t\tconst listener: EventListener = (domEvent) => {\n\t\t\tfire(buildPayload(miniName, domEvent))\n\t\t}\n\t\ttarget.addEventListener(domName, listener)\n\t\tregistered.push([domName, listener])\n\t}\n\n\treturn () => {\n\t\tfor (const [domName, listener] of registered) {\n\t\t\ttarget.removeEventListener(domName, listener)\n\t\t}\n\t\tregistered.length = 0\n\t}\n}\n", "/**\n * Devtools simulator temp-file registry.\n *\n * The simulator hands `chooseImage` / `chooseMedia` / `chooseVideo` /\n * `compressImage` etc. results back to mini-program code as paths. Historically\n * those paths were `blob:` URLs allocated via `URL.createObjectURL`, but the\n * blob: scheme is local-to-the-renderer and cannot be served back into the\n * webview's `persist:simulator` session through a `protocol.handle`. We switched\n * to a custom `difile://_tmp/{uuid}` scheme so the main process can\n * register a single `difile://` protocol handler and stream the bytes for any\n * path, regardless of which renderer originally produced it.\n *\n * To keep the renderer-side cache and the main-process byte store in sync,\n * `setTempFileSink` lets the preload bridge inject a sink that mirrors every\n * `write` / `revoke` / `revokeAll` over IPC. Tests inject a stub sink.\n */\n\nexport interface TempFileSink {\n\twrite(path: string, blob: Blob): void\n\trevoke(path: string): void\n\trevokeAll(): void\n}\n\nconst tempFiles = new Map<string, Blob>()\nlet activeSink: TempFileSink | null = null\n\nexport function setTempFileSink(sink: TempFileSink | null): void {\n\tactiveSink = sink\n}\n\nfunction cryptoRandomId(): string {\n\tconst c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto\n\tif (c && typeof c.randomUUID === 'function') return c.randomUUID()\n\treturn `${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\nexport function createTempFilePath(blob: Blob): string {\n\tconst path = `difile://_tmp/${cryptoRandomId()}`\n\ttempFiles.set(path, blob)\n\tactiveSink?.write(path, blob)\n\treturn path\n}\n\nexport function registerTempFilePath(path: string, blob: Blob): void {\n\ttempFiles.set(path, blob)\n\tactiveSink?.write(path, blob)\n}\n\nexport function revokeTempFilePath(path: string): void {\n\ttempFiles.delete(path)\n\tactiveSink?.revoke(path)\n}\n\nexport function revokeAllTempFilePaths(): void {\n\ttempFiles.clear()\n\tactiveSink?.revokeAll()\n}\n\nexport async function resolveTempFilePath(path: string): Promise<Blob> {\n\tconst cached = tempFiles.get(path)\n\tif (cached) return cached\n\n\tconst response = await fetch(path)\n\tif (!response.ok) {\n\t\tthrow new Error(`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6 ${path}`)\n\t}\n\tconst blob = await response.blob()\n\t// Cache the freshly fetched blob in-memory so subsequent reads are local.\n\t// We bypass `registerTempFilePath` deliberately: this is a renderer-only\n\t// cache fill, the main-process store already has the bytes (otherwise the\n\t// fetch would not have returned them), so triggering sink.write would\n\t// produce a redundant IPC.\n\ttempFiles.set(path, blob)\n\treturn blob\n}\n\nexport function getTempFileName(path: string, blob: Blob, fallback = 'file'): string {\n\tconst named = blob as Blob & { name?: unknown }\n\tif (typeof named.name === 'string' && named.name.trim()) {\n\t\treturn named.name\n\t}\n\n\ttry {\n\t\tconst url = new URL(path, window.location.href)\n\t\tconst segment = url.pathname.split('/').filter(Boolean).pop()\n\t\tif (segment) return decodeURIComponent(segment)\n\t} catch {\n\t\t// Fall through to the caller-provided fallback.\n\t}\n\n\treturn fallback\n}\n", "/**\n * DevTools API stubs for media-related wx.xxx APIs\n * (image / video / audio).\n *\n * Each exported function is bound with `this` = MiniApp instance\n * (via AppManager.registerApi \u2192 MiniApp.invokeApi).\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindDomEvents, type EventBridgeDisposer } from './event-bridge'\nimport { bindCallbacks } from './simulator-api-helpers'\nimport { createTempFilePath } from './temp-files'\n\n// \u2500\u2500\u2500 shared file-picker scaffolding \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// chooseImage / chooseVideo / chooseMedia all drive the browser file picker\n// through a hidden `<input type=file>`: create it, wire accept/multiple/\n// capture from the caller's options, listen for `change`, and report a\n// `${apiName}:fail cancel` when the user closes the dialog without picking\n// anything. `createFilePicker` is the single authority for that scaffold;\n// each API supplies only its accept string, multiplicity, and the\n// (possibly async) processing of the picked files.\n\ntype CancelCallbacks = Pick<ReturnType<typeof bindCallbacks>, 'onFail' | 'onComplete'>\n\ninterface FilePickerConfig {\n\taccept: string\n\tmultiple: boolean\n\t/** `capture` attribute value, or `undefined` to omit it (album source, or no single-camera source requested). */\n\tcapture?: 'user' | 'environment'\n}\n\n/** Resolves the `capture` attribute from `sourceType` + `camera`: only set when the caller asked for camera-only capture. */\nfunction captureAttrFor(sourceType: string[], camera: unknown): 'user' | 'environment' | undefined {\n\tif (sourceType.length !== 1 || sourceType[0] !== 'camera') return undefined\n\treturn camera === 'front' ? 'user' : 'environment'\n}\n\n/**\n * Drives a hidden `<input type=file>` through one pick cycle. Reports\n * `${apiName}:fail cancel` + `complete` when the selection is empty;\n * otherwise hands the raw (un-truncated) `FileList` to `onPick`, which owns\n * success/fail/complete and must call `removeInput` once it is done with the\n * picker element.\n */\nfunction createFilePicker(\n\tapiName: string,\n\tconfig: FilePickerConfig,\n\tcbs: CancelCallbacks,\n\tonPick: (files: File[], removeInput: () => void) => void | Promise<void>,\n): void {\n\tconst input = document.createElement('input')\n\tinput.type = 'file'\n\tinput.accept = config.accept\n\tinput.multiple = config.multiple\n\tif (config.capture) input.setAttribute('capture', config.capture)\n\tinput.style.display = 'none'\n\tdocument.body.appendChild(input)\n\n\tconst removeInput = () => input.remove()\n\n\tinput.addEventListener('change', () => {\n\t\tconst files = Array.from(input.files || [])\n\t\tif (files.length === 0) {\n\t\t\tcbs.onFail?.({ errMsg: `${apiName}:fail cancel` })\n\t\t\tcbs.onComplete?.()\n\t\t\tremoveInput()\n\t\t\treturn\n\t\t}\n\t\tvoid onPick(files, removeInput)\n\t})\n\n\tinput.click()\n}\n\n// \u2500\u2500\u2500 Media: Image \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function chooseImage(\n\tthis: MiniAppContext,\n\t{ count = 9, sourceType, camera, success, fail, complete }: {\n\t\tcount?: number\n\t\tsizeType?: unknown\n\t\tsourceType?: unknown\n\t\tcamera?: unknown\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onComplete } = cbs\n\tconst normalizedCount = normalizeChooseMediaCount(count)\n\tconst normalizedSourceType = normalizeStringArray(sourceType, ['album', 'camera'])\n\n\tcreateFilePicker(\n\t\t'chooseImage',\n\t\t{ accept: 'image/*', multiple: normalizedCount > 1, capture: captureAttrFor(normalizedSourceType, camera) },\n\t\tcbs,\n\t\t(rawFiles, removeInput) => {\n\t\t\tconst files = rawFiles.slice(0, normalizedCount)\n\t\t\tconst tempFilePaths = files.map(f => createTempFilePath(f))\n\t\t\tconst tempFiles = files.map((f, i) => ({ path: tempFilePaths[i], size: f.size }))\n\t\t\tonSuccess?.({ tempFilePaths, tempFiles, errMsg: 'chooseImage:ok' })\n\t\t\tonComplete?.()\n\t\t\tremoveInput()\n\t\t},\n\t)\n}\n\nexport function previewImage(\n\tthis: MiniAppContext,\n\t{ urls, current, success, complete }: { urls?: string[]; current?: string; success?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\n\tif (!urls || urls.length === 0) {\n\t\tonComplete?.()\n\t\treturn\n\t}\n\n\t// Simple overlay preview\n\tconst overlay = document.createElement('div')\n\toverlay.style.cssText =\n\t\t'position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.85);display:flex;align-items:center;justify-content:center;cursor:pointer;'\n\tconst img = document.createElement('img')\n\timg.src = current || urls[0] || ''\n\timg.style.cssText = 'max-width:90%;max-height:90%;object-fit:contain;'\n\toverlay.appendChild(img)\n\toverlay.addEventListener('click', () => overlay.remove())\n\tdocument.body.appendChild(overlay)\n\n\tonSuccess?.({ errMsg: 'previewImage:ok' })\n\tonComplete?.()\n}\n\nexport function compressImage(\n\tthis: MiniAppContext,\n\t{ src, quality = 80, success, fail, complete }: {\n\t\tsrc: string\n\t\tquality?: number\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\n\tconst img = new Image()\n\timg.crossOrigin = 'anonymous'\n\timg.onload = () => {\n\t\ttry {\n\t\t\tconst canvas = document.createElement('canvas')\n\t\t\tcanvas.width = img.naturalWidth\n\t\t\tcanvas.height = img.naturalHeight\n\t\t\tconst ctx = canvas.getContext('2d')!\n\t\t\tctx.drawImage(img, 0, 0)\n\t\t\tcanvas.toBlob(\n\t\t\t\t(blob) => {\n\t\t\t\t\tif (blob) {\n\t\t\t\t\t\tconst tempFilePath = createTempFilePath(blob)\n\t\t\t\t\t\tonSuccess?.({ tempFilePath, errMsg: 'compressImage:ok' })\n\t\t\t\t\t} else {\n\t\t\t\t\t\tonFail?.({ errMsg: 'compressImage:fail compression error' })\n\t\t\t\t\t}\n\t\t\t\t\tonComplete?.()\n\t\t\t\t},\n\t\t\t\t'image/jpeg',\n\t\t\t\tquality / 100,\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tonFail?.({ errMsg: `compressImage:fail ${(error as Error).message}` })\n\t\t\tonComplete?.()\n\t\t}\n\t}\n\timg.onerror = () => {\n\t\tonFail?.({ errMsg: 'compressImage:fail image load error' })\n\t\tonComplete?.()\n\t}\n\timg.src = src\n}\n\nexport function saveCanvasTempFile(\n\tthis: MiniAppContext,\n\t{ dataURL, success, fail, complete }: {\n\t\tdataURL?: string\n\t\tfileType?: string\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\n\tif (!dataURL) {\n\t\tonFail?.({ errMsg: 'canvasToTempFilePath:fail missing dataURL' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\n\tconst match = /^data:([^;]+);base64,(.+)$/.exec(dataURL)\n\tif (!match) {\n\t\tonFail?.({ errMsg: 'canvasToTempFilePath:fail invalid data URL' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\n\ttry {\n\t\tconst mimeType = match[1]!\n\t\tconst raw = atob(match[2]!)\n\t\tconst bytes = new Uint8Array(raw.length)\n\t\tfor (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i)\n\t\tconst blob = new Blob([bytes], { type: mimeType })\n\t\tconst tempFilePath = createTempFilePath(blob)\n\t\tonSuccess?.({ tempFilePath, errMsg: 'canvasToTempFilePath:ok' })\n\t} catch (error) {\n\t\tonFail?.({ errMsg: `canvasToTempFilePath:fail ${(error as Error).message}` })\n\t}\n\tonComplete?.()\n}\n\nexport function saveImageToPhotosAlbum(\n\tthis: MiniAppContext,\n\t{ filePath, success, fail, complete }: { filePath: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\n\ttry {\n\t\tconst a = document.createElement('a')\n\t\ta.href = filePath\n\t\ta.download = 'image'\n\t\ta.click()\n\t\tonSuccess?.({ errMsg: 'saveImageToPhotosAlbum:ok' })\n\t} catch (error) {\n\t\tonFail?.({ errMsg: `saveImageToPhotosAlbum:fail ${(error as Error).message}` })\n\t}\n\tonComplete?.()\n}\n\nexport function getImageInfo(\n\tthis: MiniAppContext,\n\t{ src, success, fail, complete }: { src: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\n\tconst img = new Image()\n\timg.crossOrigin = 'anonymous'\n\timg.onload = () => {\n\t\tonSuccess?.({\n\t\t\twidth: img.naturalWidth,\n\t\t\theight: img.naturalHeight,\n\t\t\tpath: src,\n\t\t\torientation: 'up',\n\t\t\ttype: 'unknown',\n\t\t\terrMsg: 'getImageInfo:ok',\n\t\t})\n\t\tonComplete?.()\n\t}\n\timg.onerror = () => {\n\t\tonFail?.({ errMsg: 'getImageInfo:fail image load error' })\n\t\tonComplete?.()\n\t}\n\timg.src = src\n}\n\n// \u2500\u2500\u2500 Media: Video \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype MediaFileType = 'image' | 'video'\ntype ChooseMediaCamera = 'back' | 'front'\n\nconst VIDEO_METADATA_TIMEOUT_MS = 5000\nconst VIDEO_THUMBNAIL_TIMEOUT_MS = 500\n\ninterface ChooseMediaTempFile {\n\ttempFilePath: string\n\tsize: number\n\tduration: number\n\theight: number\n\twidth: number\n\tthumbTempFilePath: string\n\tfileType: MediaFileType\n\toriginalFileObj: File\n}\n\nfunction normalizeStringArray(value: unknown, fallback: string[]): string[] {\n\treturn Array.isArray(value)\n\t\t? value.filter((item): item is string => typeof item === 'string')\n\t\t: fallback\n}\n\nfunction normalizeChooseMediaCount(value: unknown): number {\n\tconst n = Number(value)\n\tif (!Number.isFinite(n)) return 9\n\treturn Math.max(1, Math.min(20, Math.floor(n)))\n}\n\nfunction getChooseMediaAccept(mediaType: string[]): string {\n\tconst wantsMix = mediaType.includes('mix')\n\tconst wantsImage = wantsMix || mediaType.includes('image')\n\tconst wantsVideo = wantsMix || mediaType.includes('video')\n\tif (wantsImage && wantsVideo) return 'image/*,video/*'\n\treturn wantsVideo ? 'video/*' : 'image/*'\n}\n\nfunction getChooseMediaResultType(files: ChooseMediaTempFile[]): 'image' | 'video' | 'mix' {\n\tconst types = new Set(files.map(file => file.fileType))\n\tif (types.size > 1) return 'mix'\n\treturn files[0]?.fileType ?? 'image'\n}\n\nfunction readImageMetadata(src: string): Promise<{ width: number; height: number }> {\n\treturn new Promise((resolve) => {\n\t\tconst img = new Image()\n\t\timg.onload = () => resolve({ width: img.naturalWidth || 0, height: img.naturalHeight || 0 })\n\t\timg.onerror = () => resolve({ width: 0, height: 0 })\n\t\timg.src = src\n\t})\n}\n\nfunction readVideoMetadata(src: string): Promise<{ width: number; height: number; duration: number; thumbTempFilePath: string }> {\n\treturn new Promise((resolve) => {\n\t\tconst video = document.createElement('video')\n\t\tlet settled = false\n\t\tlet seekTimer: ReturnType<typeof setTimeout> | undefined\n\t\tconst finish = (metadata: { width: number; height: number; duration: number; thumbTempFilePath: string }) => {\n\t\t\tif (settled) return\n\t\t\tsettled = true\n\t\t\tclearTimeout(timer)\n\t\t\tif (seekTimer) clearTimeout(seekTimer)\n\t\t\t// Detach handlers so any late-firing DOM events (e.g. onseeked from a\n\t\t\t// previously queued currentTime change) cannot reach into the now-\n\t\t\t// resolved promise and allocate fresh blob: URLs.\n\t\t\tvideo.onloadedmetadata = null\n\t\t\tvideo.onseeked = null\n\t\t\tvideo.onerror = null\n\t\t\tvideo.removeAttribute('src')\n\t\t\tvideo.load()\n\t\t\tresolve(metadata)\n\t\t}\n\t\tconst drawThumbnail = (width: number, height: number): Promise<string> => {\n\t\t\treturn new Promise((resolveThumb) => {\n\t\t\t\tlet resolved = false\n\t\t\t\tconst done = (value: string) => {\n\t\t\t\t\tif (resolved) return\n\t\t\t\t\tresolved = true\n\t\t\t\t\tresolveThumb(value)\n\t\t\t\t}\n\t\t\t\tlet canvas: HTMLCanvasElement\n\t\t\t\ttry {\n\t\t\t\t\tcanvas = document.createElement('canvas')\n\t\t\t\t\tcanvas.width = width || 1\n\t\t\t\t\tcanvas.height = height || 1\n\t\t\t\t\tconst ctx = canvas.getContext('2d')\n\t\t\t\t\tctx?.drawImage(video, 0, 0, canvas.width, canvas.height)\n\t\t\t\t} catch {\n\t\t\t\t\tdone('')\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst toDataUrlFallback = () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdone(canvas.toDataURL('image/jpeg', 0.8))\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tdone('')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst fallbackTimer = setTimeout(toDataUrlFallback, VIDEO_THUMBNAIL_TIMEOUT_MS)\n\t\t\t\ttry {\n\t\t\t\t\tcanvas.toBlob(\n\t\t\t\t\t\t(blob) => {\n\t\t\t\t\t\t\tclearTimeout(fallbackTimer)\n\t\t\t\t\t\t\t// If the outer readVideoMetadata promise already settled\n\t\t\t\t\t\t\t// (e.g. the seekTimer fired before toBlob's async callback\n\t\t\t\t\t\t\t// arrived), we MUST NOT call createTempFilePath here \u2014 that\n\t\t\t\t\t\t\t// would allocate a fresh blob: URL and register it in the\n\t\t\t\t\t\t\t// temp-files Map with no one ever revoking it. Just drop\n\t\t\t\t\t\t\t// the blob on the floor; the resolved thumbTempFilePath\n\t\t\t\t\t\t\t// has already been chosen by the timeout path.\n\t\t\t\t\t\t\tif (settled || resolved) return\n\t\t\t\t\t\t\tif (blob) {\n\t\t\t\t\t\t\t\tdone(createTempFilePath(blob))\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\ttoDataUrlFallback()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t'image/jpeg',\n\t\t\t\t\t\t0.8,\n\t\t\t\t\t)\n\t\t\t\t} catch {\n\t\t\t\t\tclearTimeout(fallbackTimer)\n\t\t\t\t\ttoDataUrlFallback()\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t\tconst timer = setTimeout(() => finish({ width: 0, height: 0, duration: 0, thumbTempFilePath: '' }), VIDEO_METADATA_TIMEOUT_MS)\n\n\t\tvideo.preload = 'metadata'\n\t\tvideo.muted = true\n\t\tvideo.onloadedmetadata = () => {\n\t\t\tconst width = video.videoWidth || 0\n\t\t\tconst height = video.videoHeight || 0\n\t\t\tconst duration = Number.isFinite(video.duration) ? video.duration : 0\n\t\t\tconst metadata = { width, height, duration, thumbTempFilePath: '' }\n\t\t\tif (!width || !height || duration <= 0) {\n\t\t\t\tfinish(metadata)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvideo.onseeked = async () => {\n\t\t\t\tif (seekTimer) {\n\t\t\t\t\tclearTimeout(seekTimer)\n\t\t\t\t\tseekTimer = undefined\n\t\t\t\t}\n\t\t\t\tconst thumbTempFilePath = await drawThumbnail(width, height)\n\t\t\t\tfinish({ ...metadata, thumbTempFilePath })\n\t\t\t}\n\t\t\tseekTimer = setTimeout(() => finish(metadata), VIDEO_THUMBNAIL_TIMEOUT_MS)\n\t\t\ttry {\n\t\t\t\tvideo.currentTime = Math.min(0.1, Math.max(0, duration - 0.01))\n\t\t\t} catch {\n\t\t\t\tfinish(metadata)\n\t\t\t}\n\t\t}\n\t\tvideo.onerror = () => finish({ width: 0, height: 0, duration: 0, thumbTempFilePath: '' })\n\t\tvideo.src = src\n\t})\n}\n\nasync function buildChooseMediaTempFile(file: File): Promise<ChooseMediaTempFile> {\n\tconst tempFilePath = createTempFilePath(file)\n\tconst fileType: MediaFileType = file.type.startsWith('video') ? 'video' : 'image'\n\n\tif (fileType === 'video') {\n\t\tconst metadata = await readVideoMetadata(tempFilePath)\n\t\treturn {\n\t\t\ttempFilePath,\n\t\t\tsize: file.size,\n\t\t\tduration: metadata.duration,\n\t\t\theight: metadata.height,\n\t\t\twidth: metadata.width,\n\t\t\tthumbTempFilePath: metadata.thumbTempFilePath,\n\t\t\tfileType,\n\t\t\toriginalFileObj: file,\n\t\t}\n\t}\n\n\tconst metadata = await readImageMetadata(tempFilePath)\n\treturn {\n\t\ttempFilePath,\n\t\tsize: file.size,\n\t\tduration: 0,\n\t\theight: metadata.height,\n\t\twidth: metadata.width,\n\t\tthumbTempFilePath: '',\n\t\tfileType,\n\t\toriginalFileObj: file,\n\t}\n}\n\nexport function chooseMedia(\n\tthis: MiniAppContext,\n\t{ count = 9, mediaType = ['image', 'video'], sourceType = ['album', 'camera'], camera = 'back', success, fail, complete }: {\n\t\tcount?: number\n\t\tmediaType?: unknown\n\t\tsourceType?: unknown\n\t\tmaxDuration?: unknown\n\t\tsizeType?: unknown\n\t\tcamera?: ChooseMediaCamera\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onFail, onComplete } = cbs\n\tconst normalizedCount = normalizeChooseMediaCount(count)\n\tconst normalizedMediaType = normalizeStringArray(mediaType, ['image', 'video'])\n\tconst normalizedSourceType = normalizeStringArray(sourceType, ['album', 'camera'])\n\n\tcreateFilePicker(\n\t\t'chooseMedia',\n\t\t{\n\t\t\taccept: getChooseMediaAccept(normalizedMediaType),\n\t\t\tmultiple: normalizedCount > 1,\n\t\t\tcapture: captureAttrFor(normalizedSourceType, camera),\n\t\t},\n\t\tcbs,\n\t\tasync (rawFiles, removeInput) => {\n\t\t\tconst files = rawFiles.slice(0, normalizedCount)\n\t\t\ttry {\n\t\t\t\tconst tempFiles = await Promise.all(files.map(buildChooseMediaTempFile))\n\t\t\t\tonSuccess?.({\n\t\t\t\t\ttempFiles,\n\t\t\t\t\ttype: getChooseMediaResultType(tempFiles),\n\t\t\t\t\tfailedCount: 0,\n\t\t\t\t\terrMsg: 'chooseMedia:ok',\n\t\t\t\t})\n\t\t\t} catch (error) {\n\t\t\t\tonFail?.({ errMsg: `chooseMedia:fail ${(error as Error).message}` })\n\t\t\t} finally {\n\t\t\t\tonComplete?.()\n\t\t\t\tremoveInput()\n\t\t\t}\n\t\t},\n\t)\n}\n\nexport function chooseVideo(\n\tthis: MiniAppContext,\n\t{ sourceType, camera, success, fail, complete }: {\n\t\tsourceType?: unknown\n\t\tcompressed?: unknown\n\t\tmaxDuration?: unknown\n\t\tcamera?: unknown\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onComplete } = cbs\n\tconst normalizedSourceType = normalizeStringArray(sourceType, ['album', 'camera'])\n\n\tcreateFilePicker(\n\t\t'chooseVideo',\n\t\t{ accept: 'video/*', multiple: false, capture: captureAttrFor(normalizedSourceType, camera) },\n\t\tcbs,\n\t\tasync (files, removeInput) => {\n\t\t\tconst file = files[0]!\n\t\t\tconst tempFilePath = createTempFilePath(file)\n\t\t\tconst metadata = await readVideoMetadata(tempFilePath)\n\t\t\tonSuccess?.({\n\t\t\t\ttempFilePath,\n\t\t\t\tduration: metadata.duration,\n\t\t\t\tsize: file.size,\n\t\t\t\twidth: metadata.width,\n\t\t\t\theight: metadata.height,\n\t\t\t\terrMsg: 'chooseVideo:ok',\n\t\t\t})\n\t\t\tonComplete?.()\n\t\t\tremoveInput()\n\t\t},\n\t)\n}\n\n// \u2500\u2500\u2500 Media: Audio (container-side handlers for service-apis/audio) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// service-apis/audio/index.js (injected into the service Worker by the dimina\n// container bundle) calls invokeAPI('audioCreate', { audioId }), etc. DOM media\n// events on the container's HTMLAudioElement are bridged back to the service\n// layer via the `audioListen` handler.\n\n/** Payload delivered to the service-side dispatcher on every audio event. */\ninterface AudioEventPayload {\n\tevent: string\n\tcurrentTime: number\n\tduration: number\n\tbuffered: number\n\tpaused: boolean\n}\n\n/** DOM media event name \u2192 mini-program audio event name. */\nconst AUDIO_EVENT_MAP: Record<string, string> = {\n\tplay: 'play',\n\tpause: 'pause',\n\tended: 'ended',\n\terror: 'error',\n\ttimeupdate: 'timeUpdate',\n\twaiting: 'waiting',\n\tseeking: 'seeking',\n\tseeked: 'seeked',\n\tcanplay: 'canplay',\n}\n\nconst _newAudioInstances = new Map<number, HTMLAudioElement>()\n/** Disposers that unbind the DOM event bridge for a given audio instance. */\nconst _audioEventDisposers = new Map<number, EventBridgeDisposer>()\n/** The service-side dispatcher callback for a given audio instance. */\nconst _audioFire = new Map<number, (payload: AudioEventPayload) => void>()\n\n/** Snapshot the current playback state of an audio element. */\nfunction audioSnapshot(audio: HTMLAudioElement, event: string): AudioEventPayload {\n\treturn {\n\t\tevent,\n\t\tcurrentTime: audio.currentTime || 0,\n\t\tduration: Number.isFinite(audio.duration) ? audio.duration : 0,\n\t\tbuffered: audio.buffered.length ? audio.buffered.end(audio.buffered.length - 1) : 0,\n\t\tpaused: audio.paused,\n\t}\n}\n\nexport function audioCreate(this: MiniAppContext, { audioId }: { audioId: number }) {\n\t_newAudioInstances.set(audioId, new Audio())\n}\n\n/**\n * Persistent event-bridge registration. The service-side InnerAudioContext\n * calls this once at construction with a `keep: true` callback; the container\n * resolves a `fire` callback and binds the DOM media events of the matching\n * audio element to it.\n *\n * The dimina service `invokeAPI` runs every callback through\n * `callback.store(success, keep, evtId)` and delivers the resulting callback\n * id under the `success` field of `params` \u2014 `evtId` itself is consumed by\n * `callback.store` and never reaches the container payload. So the handler\n * resolves `fire` from `success`, exactly like every other media API.\n */\nexport function audioListen(this: MiniAppContext, { audioId, success }: { audioId: number; success: unknown }) {\n\tconst audio = _newAudioInstances.get(audioId)\n\tconst fire = this.createCallbackFunction(success) as ((payload: AudioEventPayload) => void) | undefined\n\tif (!audio || !fire) return\n\n\t_audioFire.set(audioId, fire)\n\n\t// Rebind cleanly if audioListen is somehow called twice for one instance.\n\t_audioEventDisposers.get(audioId)?.()\n\tconst dispose = bindDomEvents<AudioEventPayload>(\n\t\taudio,\n\t\tAUDIO_EVENT_MAP,\n\t\tfire,\n\t\tevent => audioSnapshot(audio, event),\n\t)\n\t_audioEventDisposers.set(audioId, dispose)\n}\n\nexport function audioSetProp(\n\tthis: MiniAppContext,\n\t{ audioId, prop, value, startTime, loop, volume, playbackRate, autoplay }: {\n\t\taudioId: number\n\t\tprop: string\n\t\tvalue: unknown\n\t\tstartTime?: number\n\t\tloop?: boolean\n\t\tvolume?: number\n\t\tplaybackRate?: number\n\t\tautoplay?: boolean\n\t},\n) {\n\tconst audio = _newAudioInstances.get(audioId)\n\tif (!audio) return\n\tswitch (prop) {\n\t\tcase 'src':\n\t\t\taudio.src = value as string\n\t\t\tif (startTime != null) audio.currentTime = startTime\n\t\t\tif (loop != null) audio.loop = loop\n\t\t\tif (volume != null) audio.volume = Math.max(0, Math.min(1, volume))\n\t\t\tif (playbackRate != null) audio.playbackRate = playbackRate\n\t\t\tif (autoplay) audio.play().catch(() => {})\n\t\t\tbreak\n\t\tcase 'startTime': audio.currentTime = Number(value) || 0; break\n\t\tcase 'autoplay': audio.autoplay = !!value; break\n\t\tcase 'loop': audio.loop = !!value; break\n\t\tcase 'volume': audio.volume = Math.max(0, Math.min(1, Number(value) || 0)); break\n\t\tcase 'playbackRate': audio.playbackRate = Number(value) || 1; break\n\t}\n}\n\nexport function audioPlay(this: MiniAppContext, { audioId, src }: { audioId: number; src?: string }) {\n\tconst audio = _newAudioInstances.get(audioId)\n\tif (!audio) return\n\tif (src && audio.src !== src) audio.src = src\n\taudio.play().catch(() => {})\n}\n\nexport function audioPause(this: MiniAppContext, { audioId }: { audioId: number }) {\n\t_newAudioInstances.get(audioId)?.pause()\n}\n\nexport function audioStop(this: MiniAppContext, { audioId }: { audioId: number }) {\n\tconst audio = _newAudioInstances.get(audioId)\n\tif (!audio) return\n\taudio.pause()\n\taudio.currentTime = 0\n\t// `stop` has no DOM equivalent \u2014 synthesise it through the bridge.\n\t_audioFire.get(audioId)?.(audioSnapshot(audio, 'stop'))\n}\n\nexport function audioSeek(this: MiniAppContext, { audioId, position }: { audioId: number; position: number }) {\n\tconst audio = _newAudioInstances.get(audioId)\n\tif (!audio) return\n\taudio.currentTime = position\n}\n\nexport function audioDestroy(this: MiniAppContext, { audioId }: { audioId: number }) {\n\t_audioEventDisposers.get(audioId)?.()\n\t_audioEventDisposers.delete(audioId)\n\t_audioFire.delete(audioId)\n\n\tconst audio = _newAudioInstances.get(audioId)\n\tif (!audio) return\n\taudio.pause()\n\taudio.removeAttribute('src')\n\taudio.load()\n\t_newAudioInstances.delete(audioId)\n}\n", "/**\n * vpath resolver.\n *\n * Spec: `docs/file-system.md` \u2014 single resolver shared by every FSM\n * entry, the renderer-side temp store, and the main-process\n * protocol.handle / disk reader.\n *\n * resolveVPath(url): { kind, writable, realPath? } | null\n *\n * - `difile://_tmp/<id>` \u2192 kind=tmp, writable=false, no realPath\n * - `difile://_store/<id>` \u2192 kind=store, writable=false, realPath in base\n * - `difile://<rel>` \u2192 kind=usr, writable=true, realPath in base\n *\n * Security invariants (the resolver returns `null` instead of throwing):\n * - any non-`difile://` scheme is rejected outright;\n * - any `..` segment (raw or URL-encoded) is rejected before canonicalize;\n * - after `path.normalize` the resulting `realPath` MUST stay inside the\n * sandbox base; otherwise `null`;\n * - reserved prefixes `_tmp/` and `_store/` are case-sensitive \u2014 `_TMP/`,\n * `_Tmp/`, etc. fall through to the user-data namespace.\n */\n\n// Main-process (Node ESM) resolves these natively. Simulator (vite browser\n// bundle) externalizes node builtins as no-op stubs \u2014 that's safe here only\n// because the simulator never enters the `store`/`usr` branches below\n// (renderer-side FSM forwards disk-backed reads/writes to main via IPC).\nimport os from 'node:os'\nimport path from 'node:path'\n\nexport type VPathKind = 'tmp' | 'store' | 'usr'\n\nexport interface ResolvedVPath {\n\tkind: VPathKind\n\t/** `tmp` and `store` are runtime-owned and read-only; `usr` is writable. */\n\twritable: boolean\n\t/**\n\t * Canonical local path on disk for `store` / `usr` kinds (always within the\n\t * USER_DATA_PATH base directory after canonicalize). `tmp` kind has no\n\t * realPath \u2014 the bytes live in the renderer-side Blob Map.\n\t */\n\trealPath?: string\n}\n\nconst DIFILE_PREFIX = 'difile://'\n\n/**\n * USER_DATA_PATH sandbox base. Looked up dynamically on every call so test\n * doubles (e.g. monkey-patching `os.homedir`) and the `DIMINA_HOME` env\n * override apply per invocation. The env override exists for headless test\n * affordance and to keep the renderer/main store sharing one base.\n */\nexport function sandboxBase(): string {\n\tconst env = (typeof process !== 'undefined' && process.env && process.env.DIMINA_HOME) || ''\n\tif (env) {\n\t\treturn path.join(env, 'files')\n\t}\n\treturn path.join(os.homedir(), '.dimina', 'files')\n}\n\nexport function resolveVPath(url: unknown): ResolvedVPath | null {\n\tif (typeof url !== 'string') return null\n\tif (!url.startsWith(DIFILE_PREFIX)) return null\n\n\tconst raw = url.slice(DIFILE_PREFIX.length)\n\tif (raw.length === 0) return null\n\n\t// Decode URL-encoded sequences first so `%2e%2e` style escapes are caught\n\t// by the segment check below.\n\tlet decoded: string\n\ttry {\n\t\tdecoded = decodeURIComponent(raw)\n\t} catch {\n\t\treturn null\n\t}\n\n\t// Reject NUL bytes (raw or %00) \u2014 Node `fs.*` throws TypeError on a NUL\n\t// in the path, bypassing the API's `fail` callback and crashing the\n\t// renderer. Pull the rejection up to the validator.\n\tif (decoded.includes('\\0')) return null\n\n\t// Developer convention: `wx.env.USER_DATA_PATH = 'difile://'` so a write\n\t// like `${USER_DATA_PATH}/foo.txt` produces `difile:///foo.txt`. Strip\n\t// leading slashes so that path-joins cleanly under the sandbox base.\n\tdecoded = decoded.replace(/^[/\\\\]+/, '')\n\tif (decoded.length === 0) return null\n\n\t// Reject any '..' or '.' segment after decoding. We split on both '/' and\n\t// '\\\\' so Windows-style separators cannot smuggle a traversal segment past\n\t// the validator on POSIX hosts.\n\tconst segments = decoded.split(/[/\\\\]+/)\n\tif (segments.some(s => s === '..' || s === '.')) return null\n\n\t// Classify by reserved prefix (case-sensitive \u2014 `_TMP/` is a user file).\n\tlet kind: VPathKind\n\tlet writable: boolean\n\tif (decoded.startsWith('_tmp/')) {\n\t\tkind = 'tmp'\n\t\twritable = false\n\t} else if (decoded.startsWith('_store/')) {\n\t\tkind = 'store'\n\t\twritable = false\n\t} else {\n\t\tkind = 'usr'\n\t\twritable = true\n\t}\n\n\t// `tmp` lives in the renderer Blob Map \u2014 no realPath.\n\tif (kind === 'tmp') {\n\t\treturn { kind, writable }\n\t}\n\n\t// `store` / `usr`: anchor under the sandbox base and assert containment\n\t// after normalization. Defense in depth against any traversal that the\n\t// segment check above missed (e.g. backslash trickery on POSIX).\n\tconst base = sandboxBase()\n\tconst joined = path.join(base, decoded)\n\tconst normalized = path.normalize(joined)\n\tif (normalized !== base && !normalized.startsWith(base + path.sep)) return null\n\n\treturn { kind, writable, realPath: normalized }\n}\n", "/**\n * DevTools API stubs for filesystem wx.xxx APIs\n * (container-side handlers for service-apis/file).\n *\n * Each exported function is bound with `this` = MiniApp instance\n * (via AppManager.registerApi \u2192 MiniApp.invokeApi).\n *\n * Contract (see `docs/file-system.md`):\n * - Every entry point routes its caller-supplied path through `resolveVPath`\n * (single resolver), which rejects non-`difile://` schemes, absolute\n * filesystem paths, and any `..` traversal.\n * - Write-class APIs (writeFile / appendFile / unlink / mkdir / rmdir /\n * truncate / rename(dest) / copyFile(dest)) refuse `difile://_tmp/*` and\n * `difile://_store/*` with `permission denied` \u2014 those namespaces are\n * runtime-owned and read-only, matching wx \u771F\u673A semantics.\n * - `fsSaveFile` returns a `difile://_store/{uuid}.{ext}` vpath, never a\n * real disk path.\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindCallbacks, notSupportedApi } from './simulator-api-helpers'\nimport { resolveVPath, type ResolvedVPath } from '../shared/vpath.js'\nimport { resolveTempFilePath } from './temp-files'\n\n/**\n * Dispatch helper: pull a `_tmp/*` Blob out of the renderer Map and hand back\n * its bytes. Throws an ENOENT-shaped Error if the URL is unknown, so callers\n * can surface a `fail` with a real not-found message.\n */\nasync function _tmpBytes(url: string): Promise<Buffer> {\n\ttry {\n\t\tconst blob = await resolveTempFilePath(url)\n\t\treturn await blobToBuffer(blob)\n\t}\n\tcatch (err) {\n\t\tconst msg = err instanceof Error ? err.message : String(err)\n\t\tthrow new Error(`ENOENT: no such file (${url}): ${msg}`, { cause: err })\n\t}\n}\n\nfunction isArrayBuffer(value: unknown): value is ArrayBuffer {\n\treturn Object.prototype.toString.call(value) === '[object ArrayBuffer]'\n}\n\nasync function blobToBuffer(blob: Blob): Promise<Buffer> {\n\tconst readable = blob as Blob & { arrayBuffer?: () => Promise<ArrayBuffer> }\n\tif (typeof readable.arrayBuffer === 'function') {\n\t\tconst ab = await readable.arrayBuffer()\n\t\treturn Buffer.from(new Uint8Array(ab))\n\t}\n\n\tif (typeof FileReader !== 'function') {\n\t\tthrow new Error('Blob cannot be read as ArrayBuffer')\n\t}\n\n\treturn new Promise((resolve, reject) => {\n\t\tconst reader = new FileReader()\n\t\treader.onerror = () => reject(reader.error ?? new Error('Blob read failed'))\n\t\treader.onload = () => {\n\t\t\tconst result = reader.result\n\t\t\tif (!isArrayBuffer(result)) {\n\t\t\t\treject(new Error('Blob read did not return an ArrayBuffer'))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresolve(Buffer.from(new Uint8Array(result)))\n\t\t}\n\t\treader.readAsArrayBuffer(blob)\n\t})\n}\n\n// In Electron renderer, Node.js built-ins are available via require.\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst _fs: typeof import('fs') = (typeof require !== 'undefined') ? require('fs') : null as unknown as typeof import('fs')\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst _path: typeof import('path') = (typeof require !== 'undefined') ? require('path') : null as unknown as typeof import('path')\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst _crypto: typeof import('crypto') = (typeof require !== 'undefined') ? require('crypto') : null as unknown as typeof import('crypto')\n\ntype FsCallbacks = ReturnType<typeof bindCallbacks>\ntype NodeStats = import('fs').Stats\ntype NodeErr = NodeJS.ErrnoException | Error | null\n\n// \u2500\u2500\u2500 shared fs-API scaffolding \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Every handler below shares the same skeleton: bail if Node `fs` isn't\n// available, resolve the caller path through the single vpath resolver\n// (bailing on invalid/unsafe paths), optionally reject read-only\n// namespaces for write-class APIs, then run a Node fs call and translate its\n// `(err, ...)` callback into the wx success/fail/complete triad. The helpers\n// below are the single authority for each of those steps so per-API bodies\n// only contain what differs between APIs (the real fs call and any extra\n// success payload fields).\n\n/** Bails with `${apiName}:fail not available in browser context` when the Node `fs` binding is absent (e.g. a browser-only build). */\nfunction guardFsAvailable(apiName: string, cbs: FsCallbacks): boolean {\n\tif (_fs) return false\n\tcbs.onFail?.({ errMsg: `${apiName}:fail not available in browser context` })\n\tcbs.onComplete?.()\n\treturn true\n}\n\n/** Resolves `p` via `resolveVPath`, or reports `${apiName}:fail invalid or unsafe path` and runs `complete`. */\nfunction resolveOrBail(p: unknown, apiName: string, cbs: FsCallbacks): ResolvedVPath | undefined {\n\tconst v = resolveVPath(p)\n\tif (v) return v\n\tcbs.onFail?.({ errMsg: `${apiName}:fail invalid or unsafe path` })\n\tcbs.onComplete?.()\n\treturn undefined\n}\n\n/**\n * Type-guards `v` as writable (narrows `realPath` to `string`), or reports\n * `${apiName}:fail permission denied` and runs `complete`. `_tmp` / `_store`\n * are runtime-owned and read-only, matching wx \u771F\u673A semantics.\n */\nfunction ensureWritable(v: ResolvedVPath, apiName: string, cbs: FsCallbacks): v is ResolvedVPath & { realPath: string } {\n\tif (v.writable && v.realPath) return true\n\tcbs.onFail?.({ errMsg: `${apiName}:fail permission denied` })\n\tcbs.onComplete?.()\n\treturn false\n}\n\n/** Fail handler for a `_tmpBytes(...).then(success, ...)` chain: reports `${apiName}:fail <message>` and runs `complete`. */\nfunction tmpFailHandler(apiName: string, cbs: FsCallbacks) {\n\treturn (err: Error) => {\n\t\tcbs.onFail?.({ errMsg: `${apiName}:fail ${err.message}` })\n\t\tcbs.onComplete?.()\n\t}\n}\n\n/**\n * Builds a Node-style `(err, ...args)` callback that translates into the wx\n * success/fail/complete triad: `err` truthy \u2192 fail with `${apiName}:fail\n * <message>`; otherwise success with `${apiName}:ok` plus whatever\n * `buildOk(...args)` contributes.\n */\nfunction nodeComplete<TArgs extends unknown[] = []>(\n\tapiName: string,\n\tcbs: FsCallbacks,\n\tbuildOk?: (...args: TArgs) => Record<string, unknown> | undefined,\n) {\n\treturn (err: NodeErr, ...args: TArgs) => {\n\t\tif (err) {\n\t\t\tcbs.onFail?.({ errMsg: `${apiName}:fail ${err.message}` })\n\t\t} else {\n\t\t\tcbs.onSuccess?.({ ...(buildOk ? buildOk(...args) : undefined), errMsg: `${apiName}:ok` })\n\t\t}\n\t\tcbs.onComplete?.()\n\t}\n}\n\n/**\n * `mkdir -p` the parent of `destReal`, then run `writeFn` (a `writeFile` /\n * `copyFile` call) through `nodeComplete`. Shared by every API that\n * materializes bytes onto disk under a possibly-not-yet-existing directory\n * (fsWriteFile, fsCopyFile's `_tmp` branch, fsSaveFile).\n */\nfunction mkdirpThenWrite(\n\tdestReal: string,\n\tapiName: string,\n\tcbs: FsCallbacks,\n\twriteFn: (done: (err: NodeErr) => void) => void,\n\tokExtra?: Record<string, unknown>,\n): void {\n\t_fs.mkdir(_path.dirname(destReal), { recursive: true }, (mkdirErr) => {\n\t\tif (mkdirErr) {\n\t\t\tcbs.onFail?.({ errMsg: `${apiName}:fail ${mkdirErr.message}` })\n\t\t\tcbs.onComplete?.()\n\t\t\treturn\n\t\t}\n\t\twriteFn(nodeComplete(apiName, cbs, () => okExtra))\n\t})\n}\n\nexport function fsAccess(\n\tthis: MiniAppContext,\n\t{ path, success, fail, complete }: { path: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsAccess', cbs)) return\n\tconst v = resolveOrBail(path, 'fsAccess', cbs)\n\tif (!v) return\n\tif (v.kind === 'tmp') {\n\t\t// _tmp existence check: renderer Map hit \u21D2 ok; miss \u21D2 ENOENT-shaped fail.\n\t\t_tmpBytes(path).then(\n\t\t\t() => { onSuccess?.({ errMsg: 'fsAccess:ok' }); onComplete?.() },\n\t\t\ttmpFailHandler('fsAccess', cbs),\n\t\t)\n\t\treturn\n\t}\n\tif (!v.realPath) {\n\t\tonFail?.({ errMsg: 'fsAccess:fail invalid path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\t_fs.access(v.realPath, _fs.constants.F_OK, nodeComplete('fsAccess', cbs))\n}\n\nexport function fsStat(\n\tthis: MiniAppContext,\n\t{ path, recursive = false, success, fail, complete }: {\n\t\tpath: string\n\t\trecursive?: boolean\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsStat', cbs)) return\n\tconst v = resolveOrBail(path, 'fsStat', cbs)\n\tif (!v) return\n\tif (v.kind === 'tmp') {\n\t\t// _tmp size is known from the Blob; mtime is 0 (no on-disk timestamp).\n\t\t_tmpBytes(path).then(\n\t\t\t(buf) => {\n\t\t\t\tonSuccess?.({\n\t\t\t\t\tstats: {\n\t\t\t\t\t\tsize: buf.length,\n\t\t\t\t\t\tmode: 0,\n\t\t\t\t\t\tlastAccessedTime: 0,\n\t\t\t\t\t\tlastModifiedTime: 0,\n\t\t\t\t\t\tisFile: true,\n\t\t\t\t\t\tisDirectory: false,\n\t\t\t\t\t},\n\t\t\t\t\terrMsg: 'fsStat:ok',\n\t\t\t\t})\n\t\t\t\tonComplete?.()\n\t\t\t},\n\t\t\ttmpFailHandler('fsStat', cbs),\n\t\t)\n\t\treturn\n\t}\n\tif (!v.realPath) {\n\t\tonFail?.({ errMsg: 'fsStat:fail invalid path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\tconst resolved = v.realPath\n\tif (recursive) {\n\t\t// Build a map of path \u2192 stat info for the directory tree\n\t\tconst statsMap: Record<string, unknown> = {}\n\t\tconst walkDir = (dir: string, cb: (err: Error | null) => void) => {\n\t\t\t_fs.readdir(dir, { withFileTypes: true }, (err, entries) => {\n\t\t\t\tif (err) { cb(err); return }\n\t\t\t\tlet pending = entries.length\n\t\t\t\tif (pending === 0) { cb(null); return }\n\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\tconst full = _path.join(dir, entry.name)\n\t\t\t\t\t_fs.stat(full, (statErr, s) => {\n\t\t\t\t\t\tif (!statErr) {\n\t\t\t\t\t\t\tstatsMap[full] = {\n\t\t\t\t\t\t\t\tsize: s.size,\n\t\t\t\t\t\t\t\tmode: s.mode,\n\t\t\t\t\t\t\t\tlastAccessedTime: s.atimeMs,\n\t\t\t\t\t\t\t\tlastModifiedTime: s.mtimeMs,\n\t\t\t\t\t\t\t\tisFile: s.isFile(),\n\t\t\t\t\t\t\t\tisDirectory: s.isDirectory(),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (entry.isDirectory()) {\n\t\t\t\t\t\t\twalkDir(full, () => { if (--pending === 0) cb(null) })\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (--pending === 0) cb(null)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t\twalkDir(resolved, (err) => {\n\t\t\tif (err) {\n\t\t\t\tonFail?.({ errMsg: `fsStat:fail ${err.message}` })\n\t\t\t} else {\n\t\t\t\tonSuccess?.({ stats: statsMap, errMsg: 'fsStat:ok' })\n\t\t\t}\n\t\t\tonComplete?.()\n\t\t})\n\t} else {\n\t\t_fs.stat(resolved, nodeComplete('fsStat', cbs, (s: NodeStats) => ({\n\t\t\tstats: {\n\t\t\t\tsize: s.size,\n\t\t\t\tmode: s.mode,\n\t\t\t\tlastAccessedTime: s.atimeMs,\n\t\t\t\tlastModifiedTime: s.mtimeMs,\n\t\t\t\tisFile: s.isFile(),\n\t\t\t\tisDirectory: s.isDirectory(),\n\t\t\t},\n\t\t})))\n\t}\n}\n\nexport function fsReadFile(\n\tthis: MiniAppContext,\n\t{ filePath, encoding, success, fail, complete }: {\n\t\tfilePath: string\n\t\tencoding?: BufferEncoding\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsReadFile', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsReadFile', cbs)\n\tif (!v) return\n\tif (v.kind === 'tmp') {\n\t\t_tmpBytes(filePath).then(\n\t\t\t(buf) => {\n\t\t\t\tconst data: Buffer | string = encoding ? buf.toString(encoding) : buf\n\t\t\t\tonSuccess?.({ data, errMsg: 'fsReadFile:ok' })\n\t\t\t\tonComplete?.()\n\t\t\t},\n\t\t\ttmpFailHandler('fsReadFile', cbs),\n\t\t)\n\t\treturn\n\t}\n\tif (!v.realPath) {\n\t\tonFail?.({ errMsg: 'fsReadFile:fail invalid path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\t_fs.readFile(v.realPath, encoding || null, nodeComplete('fsReadFile', cbs, (data: Buffer | string) => ({ data })))\n}\n\nexport function fsWriteFile(\n\tthis: MiniAppContext,\n\t{ filePath, data, encoding = 'utf8', success, fail, complete }: {\n\t\tfilePath: string\n\t\tdata: string | Uint8Array\n\t\tencoding?: BufferEncoding\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsWriteFile', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsWriteFile', cbs)\n\tif (!v) return\n\tif (!ensureWritable(v, 'fsWriteFile', cbs)) return\n\tmkdirpThenWrite(v.realPath, 'fsWriteFile', cbs, done => _fs.writeFile(v.realPath, data as string, { encoding }, done))\n}\n\nexport function fsAppendFile(\n\tthis: MiniAppContext,\n\t{ filePath, data, encoding = 'utf8', success, fail, complete }: {\n\t\tfilePath: string\n\t\tdata: string | Uint8Array\n\t\tencoding?: BufferEncoding\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsAppendFile', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsAppendFile', cbs)\n\tif (!v) return\n\tif (!ensureWritable(v, 'fsAppendFile', cbs)) return\n\t_fs.appendFile(v.realPath, data as string, { encoding }, nodeComplete('fsAppendFile', cbs))\n}\n\nexport function fsCopyFile(\n\tthis: MiniAppContext,\n\t{ srcPath, destPath, success, fail, complete }: {\n\t\tsrcPath: string\n\t\tdestPath: string\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsCopyFile', cbs)) return\n\tconst vSrc = resolveOrBail(srcPath, 'fsCopyFile', cbs)\n\tif (!vSrc) return\n\tconst vDest = resolveOrBail(destPath, 'fsCopyFile', cbs)\n\tif (!vDest) return\n\tif (!ensureWritable(vDest, 'fsCopyFile', cbs)) return\n\tif (vSrc.kind === 'tmp') {\n\t\t// Materialize the renderer Blob into the user-data\n\t\t// area. The dest writable check above already rejected _tmp / _store\n\t\t// destinations \u2014 saveFile is the documented route for tmp\u2192store.\n\t\t_tmpBytes(srcPath).then(\n\t\t\tbuf => mkdirpThenWrite(vDest.realPath, 'fsCopyFile', cbs, done => _fs.writeFile(vDest.realPath, buf, done)),\n\t\t\ttmpFailHandler('fsCopyFile', cbs),\n\t\t)\n\t\treturn\n\t}\n\tif (!vSrc.realPath) {\n\t\tonFail?.({ errMsg: 'fsCopyFile:fail invalid src path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\t_fs.copyFile(vSrc.realPath, vDest.realPath, nodeComplete('fsCopyFile', cbs))\n}\n\nexport function fsRename(\n\tthis: MiniAppContext,\n\t{ oldPath, newPath, success, fail, complete }: {\n\t\toldPath: string\n\t\tnewPath: string\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsRename', cbs)) return\n\tconst vOld = resolveOrBail(oldPath, 'fsRename', cbs)\n\tif (!vOld) return\n\tconst vNew = resolveOrBail(newPath, 'fsRename', cbs)\n\tif (!vNew) return\n\t// Rename deletes the source \u2014 both sides must be writable. _tmp / _store\n\t// reject under either role.\n\tif (!ensureWritable(vOld, 'fsRename', cbs)) return\n\tif (!ensureWritable(vNew, 'fsRename', cbs)) return\n\t_fs.rename(vOld.realPath, vNew.realPath, nodeComplete('fsRename', cbs))\n}\n\nexport function fsUnlink(\n\tthis: MiniAppContext,\n\t{ filePath, success, fail, complete }: { filePath: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsUnlink', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsUnlink', cbs)\n\tif (!v) return\n\tif (!ensureWritable(v, 'fsUnlink', cbs)) return\n\t_fs.unlink(v.realPath, nodeComplete('fsUnlink', cbs))\n}\n\nexport function fsMkdir(\n\tthis: MiniAppContext,\n\t{ dirPath, recursive = false, success, fail, complete }: {\n\t\tdirPath: string\n\t\trecursive?: boolean\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsMkdir', cbs)) return\n\tconst v = resolveOrBail(dirPath, 'fsMkdir', cbs)\n\tif (!v) return\n\tif (!ensureWritable(v, 'fsMkdir', cbs)) return\n\t_fs.mkdir(v.realPath, { recursive }, nodeComplete('fsMkdir', cbs))\n}\n\nexport function fsRmdir(\n\tthis: MiniAppContext,\n\t{ dirPath, recursive = false, success, fail, complete }: {\n\t\tdirPath: string\n\t\trecursive?: boolean\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsRmdir', cbs)) return\n\tconst v = resolveOrBail(dirPath, 'fsRmdir', cbs)\n\tif (!v) return\n\tif (!ensureWritable(v, 'fsRmdir', cbs)) return\n\t// Node 14+: rm with recursive; older Node: rmdir with recursive flag\n\tconst rmFn = (_fs as typeof _fs & { rm?: typeof _fs.rmdir }).rm ?? _fs.rmdir\n\trmFn(v.realPath, { recursive } as Parameters<typeof _fs.rmdir>[1], nodeComplete('fsRmdir', cbs))\n}\n\nexport function fsReaddir(\n\tthis: MiniAppContext,\n\t{ dirPath, success, fail, complete }: { dirPath: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsReaddir', cbs)) return\n\tconst v = resolveOrBail(dirPath, 'fsReaddir', cbs)\n\tif (!v) return\n\t// _tmp and _store are flat (id-addressed) namespaces \u2014 readdir is meaningless.\n\tif (v.kind === 'tmp' || v.kind === 'store') {\n\t\tonFail?.({ errMsg: `fsReaddir:fail ${v.kind} is a flat namespace (no dir tree)` })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\tif (!v.realPath) {\n\t\tonFail?.({ errMsg: 'fsReaddir:fail invalid path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\t_fs.readdir(v.realPath, nodeComplete('fsReaddir', cbs, (files: string[]) => ({ files })))\n}\n\nexport function fsGetFileInfo(\n\tthis: MiniAppContext,\n\t{ filePath, digestAlgorithm, success, fail, complete }: {\n\t\tfilePath: string\n\t\tdigestAlgorithm?: string\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsGetFileInfo', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsGetFileInfo', cbs)\n\tif (!v) return\n\tif (v.kind === 'tmp') {\n\t\t_tmpBytes(filePath).then(\n\t\t\t(buf) => {\n\t\t\t\tconst result: Record<string, unknown> = { size: buf.length, errMsg: 'fsGetFileInfo:ok' }\n\t\t\t\tif (digestAlgorithm) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst hash = _crypto.createHash(digestAlgorithm === 'md5' ? 'md5' : 'sha1')\n\t\t\t\t\t\thash.update(buf)\n\t\t\t\t\t\tresult.digest = hash.digest('hex')\n\t\t\t\t\t}\n\t\t\t\t\tcatch {\n\t\t\t\t\t\t// crypto unavailable \u2014 fall through without digest.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tonSuccess?.(result)\n\t\t\t\tonComplete?.()\n\t\t\t},\n\t\t\ttmpFailHandler('fsGetFileInfo', cbs),\n\t\t)\n\t\treturn\n\t}\n\tif (!v.realPath) {\n\t\tonFail?.({ errMsg: 'fsGetFileInfo:fail invalid path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\tconst resolved = v.realPath\n\t_fs.stat(resolved, (err, s) => {\n\t\tif (err) {\n\t\t\tonFail?.({ errMsg: `fsGetFileInfo:fail ${err.message}` })\n\t\t\tonComplete?.()\n\t\t\treturn\n\t\t}\n\t\tconst result: Record<string, unknown> = { size: s.size, errMsg: 'fsGetFileInfo:ok' }\n\t\tif (digestAlgorithm) {\n\t\t\t// Compute digest if crypto is available\n\t\t\ttry {\n\t\t\t\tconst hash = _crypto.createHash(digestAlgorithm === 'md5' ? 'md5' : 'sha1')\n\t\t\t\tconst stream = _fs.createReadStream(resolved)\n\t\t\t\tstream.on('data', (chunk) => hash.update(chunk as Buffer))\n\t\t\t\tstream.on('end', () => {\n\t\t\t\t\tresult.digest = hash.digest('hex')\n\t\t\t\t\tonSuccess?.(result)\n\t\t\t\t\tonComplete?.()\n\t\t\t\t})\n\t\t\t\tstream.on('error', (hashErr) => {\n\t\t\t\t\tonFail?.({ errMsg: `fsGetFileInfo:fail ${hashErr.message}` })\n\t\t\t\t\tonComplete?.()\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t} catch {\n\t\t\t\t// crypto not available, skip digest\n\t\t\t}\n\t\t}\n\t\tonSuccess?.(result)\n\t\tonComplete?.()\n\t})\n}\n\n/**\n * Save a temp file into the read-only `_store/` namespace. Returns a vpath\n * (`difile://_store/{uuid}.{ext}`) rather than a real disk path so callers\n * cannot leak the host filesystem layout.\n *\n * Scope:\n * - source must be a `difile://`-anchored vpath (validator rejects abs paths);\n * - source from `_tmp/` materializes the renderer Blob into `_store/` via the\n * renderer-Blob \u2192 main-fs copy bridge;\n * - source from `_store/` or the user-data area is copied byte-for-byte to a\n * freshly minted `_store/{uuid}.{ext}` entry under the sandbox base.\n */\nexport function fsSaveFile(\n\tthis: MiniAppContext,\n\t{ tempFilePath, filePath: _filePath, success, fail, complete }: {\n\t\ttempFilePath: string\n\t\tfilePath?: string\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tvoid _filePath\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsSaveFile', cbs)) return\n\tconst src = resolveOrBail(tempFilePath, 'fsSaveFile', cbs)\n\tif (!src) return\n\tconst ext = _path.extname(tempFilePath) || ''\n\tconst id = _crypto.randomUUID() + ext\n\tconst savedFilePath = `difile://_store/${id}`\n\tconst destResolved = resolveVPath(savedFilePath)\n\tif (!destResolved || !destResolved.realPath) {\n\t\t// Defensive: a freshly minted vpath must always resolve.\n\t\tonFail?.({ errMsg: 'fsSaveFile:fail unable to allocate destination' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\tconst destReal = destResolved.realPath\n\n\tif (src.kind === 'tmp') {\n\t\t// Materialize the renderer Blob into _store on disk.\n\t\t_tmpBytes(tempFilePath).then(\n\t\t\tbytes => mkdirpThenWrite(destReal, 'fsSaveFile', cbs, done => _fs.writeFile(destReal, bytes, done), { savedFilePath }),\n\t\t\ttmpFailHandler('fsSaveFile', cbs),\n\t\t)\n\t\treturn\n\t}\n\tif (!src.realPath) {\n\t\tonFail?.({ errMsg: 'fsSaveFile:fail invalid src path' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\tmkdirpThenWrite(destReal, 'fsSaveFile', cbs, done => _fs.copyFile(src.realPath!, destReal, done), { savedFilePath })\n}\n\n/**\n * List files previously persisted by `fsSaveFile` \u2014 i.e. anything under the\n * `_store/` namespace. Returned `filePath` entries are vpaths so callers can\n * round-trip through `fsReadFile` / `fsRemoveSavedFile` without ever seeing\n * the host filesystem.\n */\nexport function fsGetSavedFileList(\n\tthis: MiniAppContext,\n\t{ success, fail, complete }: { success?: unknown; fail?: unknown; complete?: unknown } = {},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onSuccess, onComplete } = cbs\n\tif (guardFsAvailable('fsGetSavedFileList', cbs)) return\n\tconst storeVpath = resolveVPath('difile://_store/')\n\tconst storeDir = storeVpath?.realPath\n\tif (!storeDir) {\n\t\tonSuccess?.({ fileList: [], errMsg: 'fsGetSavedFileList:ok' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\t_fs.readdir(storeDir, (err, files) => {\n\t\tif (err) {\n\t\t\t// Directory may not exist yet \u2014 return empty list\n\t\t\tonSuccess?.({ fileList: [], errMsg: 'fsGetSavedFileList:ok' })\n\t\t\tonComplete?.()\n\t\t\treturn\n\t\t}\n\t\tlet pending = files.length\n\t\tif (pending === 0) {\n\t\t\tonSuccess?.({ fileList: [], errMsg: 'fsGetSavedFileList:ok' })\n\t\t\tonComplete?.()\n\t\t\treturn\n\t\t}\n\t\tconst fileList: Array<{ filePath: string; size: number; createTime: number }> = []\n\t\tfor (const name of files) {\n\t\t\tconst full = _path.join(storeDir, name)\n\t\t\t_fs.stat(full, (statErr, s) => {\n\t\t\t\tif (!statErr) {\n\t\t\t\t\tfileList.push({\n\t\t\t\t\t\tfilePath: `difile://_store/${name}`,\n\t\t\t\t\t\tsize: s.size,\n\t\t\t\t\t\tcreateTime: s.birthtimeMs,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tif (--pending === 0) {\n\t\t\t\t\tonSuccess?.({ fileList, errMsg: 'fsGetSavedFileList:ok' })\n\t\t\t\t\tonComplete?.()\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nexport function fsRemoveSavedFile(\n\tthis: MiniAppContext,\n\t{ filePath, success, fail, complete }: { filePath: string; success?: unknown; fail?: unknown; complete?: unknown },\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tconst { onFail, onComplete } = cbs\n\tif (guardFsAvailable('fsRemoveSavedFile', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsRemoveSavedFile', cbs)\n\tif (!v) return\n\t// removeSavedFile is the documented exception to the `_store/` read-only\n\t// rule. Only `_store/*` entries may be removed through this API.\n\tif (v.kind !== 'store' || !v.realPath) {\n\t\tonFail?.({ errMsg: 'fsRemoveSavedFile:fail only _store/ entries may be removed' })\n\t\tonComplete?.()\n\t\treturn\n\t}\n\t_fs.unlink(v.realPath, nodeComplete('fsRemoveSavedFile', cbs))\n}\n\nexport function fsTruncate(\n\tthis: MiniAppContext,\n\t{ filePath, length = 0, success, fail, complete }: {\n\t\tfilePath: string\n\t\tlength?: number\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst cbs = bindCallbacks(this, { success, fail, complete })\n\tif (guardFsAvailable('fsTruncate', cbs)) return\n\tconst v = resolveOrBail(filePath, 'fsTruncate', cbs)\n\tif (!v) return\n\tif (!ensureWritable(v, 'fsTruncate', cbs)) return\n\t_fs.truncate(v.realPath, length, nodeComplete('fsTruncate', cbs))\n}\n\nexport const fsUnzip = notSupportedApi('fsUnzip')\n", "/**\n * DevTools API stubs for network-related wx.xxx APIs.\n *\n * Each exported function is bound with `this` = MiniApp instance\n * (via AppManager.registerApi -> MiniApp.invokeApi).\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindCallbacks } from './simulator-api-helpers'\nimport { createTempFilePath, getTempFileName, resolveTempFilePath } from './temp-files'\n\nexport function downloadFile(\n\tthis: MiniAppContext,\n\t{ url, header = {}, filePath, success, fail, complete }: {\n\t\turl: string\n\t\theader?: Record<string, string>\n\t\tfilePath?: string\n\t\tsuccess?: unknown\n\t\tfail?: unknown\n\t\tcomplete?: unknown\n\t},\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\n\tfetch(url, { headers: header })\n\t\t.then(async (response) => {\n\t\t\tif (!response.ok) throw new Error(response.statusText)\n\t\t\tconst blob = await response.blob()\n\t\t\tconst tempFilePath = createTempFilePath(blob)\n\t\t\tonSuccess?.({\n\t\t\t\ttempFilePath,\n\t\t\t\tfilePath: filePath || tempFilePath,\n\t\t\t\tstatusCode: response.status,\n\t\t\t\terrMsg: 'downloadFile:ok',\n\t\t\t})\n\t\t})\n\t\t.catch((error: Error) => {\n\t\t\tonFail?.({ errMsg: `downloadFile:fail ${error.message}` })\n\t\t})\n\t\t.finally(() => {\n\t\t\tonComplete?.()\n\t\t})\n}\n\ntype UploadFileOptions = {\n\turl: string\n\tfilePath: string\n\tname: string\n\theader?: Record<string, string>\n\tformData?: Record<string, unknown>\n\ttimeout?: number\n\tuploadId?: string\n\tprogress?: unknown\n\theadersReceived?: unknown\n\tsuccess?: unknown\n\tfail?: unknown\n\tcomplete?: unknown\n}\n\nconst uploadRequests = new Map<string, XMLHttpRequest>()\nconst uploadPendingResolves = new Set<string>()\nconst uploadAbortedBeforeStart = new Set<string>()\nlet nextUploadId = 1\n\nfunction createUploadId(): string {\n\treturn `upload_${Date.now()}_${nextUploadId++}`\n}\n\nfunction parseResponseHeaders(raw: string): Record<string, string> {\n\tconst headers: Record<string, string> = {}\n\tfor (const line of raw.trim().split(/[\\r\\n]+/)) {\n\t\tif (!line) continue\n\t\tconst index = line.indexOf(':')\n\t\tif (index <= 0) continue\n\t\tconst key = line.slice(0, index).trim()\n\t\tconst value = line.slice(index + 1).trim()\n\t\tif (key) headers[key] = value\n\t}\n\treturn headers\n}\n\nfunction appendFormData(form: FormData, formData: Record<string, unknown>): void {\n\tfor (const [key, value] of Object.entries(formData)) {\n\t\tif (value == null) continue\n\t\tif (value instanceof Blob) {\n\t\t\tform.append(key, value)\n\t\t} else if (typeof value === 'object') {\n\t\t\t// JSON.stringify for plain objects/arrays; Map/Set/RegExp/TypedArray\n\t\t\t// fall through here too and become '{}' or similar \u2014 caller should\n\t\t\t// pre-stringify if they need a specific form.\n\t\t\tform.append(key, JSON.stringify(value))\n\t\t} else {\n\t\t\tform.append(key, String(value))\n\t\t}\n\t}\n}\n\nexport function uploadFile(\n\tthis: MiniAppContext,\n\t{\n\t\turl,\n\t\tfilePath,\n\t\tname,\n\t\theader = {},\n\t\tformData = {},\n\t\ttimeout,\n\t\tuploadId = createUploadId(),\n\t\tprogress,\n\t\theadersReceived,\n\t\tsuccess,\n\t\tfail,\n\t\tcomplete,\n\t}: UploadFileOptions,\n) {\n\tconst { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete })\n\tconst onProgress = this.createCallbackFunction(progress)\n\tconst onHeadersReceived = this.createCallbackFunction(headersReceived)\n\n\tconst cleanup = () => {\n\t\tuploadRequests.delete(uploadId)\n\t\tuploadPendingResolves.delete(uploadId)\n\t\tuploadAbortedBeforeStart.delete(uploadId)\n\t}\n\tconst finishFail = (message: string) => {\n\t\tcleanup()\n\t\tconst result = { errMsg: `uploadFile:fail ${message}` }\n\t\tonFail?.(result)\n\t\tonComplete?.(result)\n\t}\n\n\tuploadPendingResolves.add(uploadId)\n\tvoid resolveTempFilePath(filePath)\n\t\t.then((blob) => {\n\t\t\tuploadPendingResolves.delete(uploadId)\n\t\t\tif (uploadAbortedBeforeStart.has(uploadId)) {\n\t\t\t\tfinishFail('abort')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst xhr = new XMLHttpRequest()\n\t\t\tlet settled = false\n\t\t\tlet deliveredHeaders = false\n\t\t\tuploadRequests.set(uploadId, xhr)\n\n\t\t\tconst finishSuccess = () => {\n\t\t\t\tif (settled) return\n\t\t\t\tsettled = true\n\t\t\t\tconst headers = parseResponseHeaders(xhr.getAllResponseHeaders())\n\t\t\t\tcleanup()\n\t\t\t\tconst result = {\n\t\t\t\t\tdata: typeof xhr.response === 'string' ? xhr.response : xhr.responseText,\n\t\t\t\t\tstatusCode: xhr.status,\n\t\t\t\t\theader: headers,\n\t\t\t\t\terrMsg: 'uploadFile:ok',\n\t\t\t\t}\n\t\t\t\tonSuccess?.(result)\n\t\t\t\tonComplete?.(result)\n\t\t\t}\n\t\t\tconst finishXhrFail = (message: string) => {\n\t\t\t\tif (settled) return\n\t\t\t\tsettled = true\n\t\t\t\tfinishFail(message)\n\t\t\t}\n\t\t\tconst emitHeaders = () => {\n\t\t\t\tif (deliveredHeaders) return\n\t\t\t\tdeliveredHeaders = true\n\t\t\t\tonHeadersReceived?.({ header: parseResponseHeaders(xhr.getAllResponseHeaders()) })\n\t\t\t}\n\n\t\t\txhr.open('POST', url, true)\n\t\t\tif (timeout === undefined) {\n\t\t\t\txhr.timeout = 60_000\n\t\t\t} else if (Number(timeout) > 0) {\n\t\t\t\txhr.timeout = Number(timeout)\n\t\t\t}\n\t\t\tfor (const [key, value] of Object.entries(header)) {\n\t\t\t\tif (/^(referer|content-type)$/i.test(key)) continue\n\t\t\t\txhr.setRequestHeader(key, String(value))\n\t\t\t}\n\t\t\txhr.upload.onprogress = (event) => {\n\t\t\t\tconst totalBytesExpectedToSend = event.lengthComputable ? event.total : blob.size\n\t\t\t\tconst progressValue = totalBytesExpectedToSend > 0\n\t\t\t\t\t? Math.round((event.loaded / totalBytesExpectedToSend) * 100)\n\t\t\t\t\t: 0\n\t\t\t\tonProgress?.({\n\t\t\t\t\tprogress: Math.max(0, Math.min(100, progressValue)),\n\t\t\t\t\ttotalBytesSent: event.loaded,\n\t\t\t\t\ttotalBytesExpectedToSend,\n\t\t\t\t})\n\t\t\t}\n\t\t\txhr.onreadystatechange = () => {\n\t\t\t\tif (xhr.readyState >= XMLHttpRequest.HEADERS_RECEIVED) emitHeaders()\n\t\t\t}\n\t\t\txhr.onload = () => {\n\t\t\t\temitHeaders()\n\t\t\t\tfinishSuccess()\n\t\t\t}\n\t\t\txhr.onerror = () => finishXhrFail('network error')\n\t\t\txhr.ontimeout = () => finishXhrFail('timeout')\n\t\t\txhr.onabort = () => finishXhrFail('abort')\n\n\t\t\tconst body = new FormData()\n\t\t\tappendFormData(body, formData)\n\t\t\tbody.append(name, blob, getTempFileName(filePath, blob, name || 'file'))\n\t\t\txhr.send(body)\n\t\t})\n\t\t.catch((error: Error) => {\n\t\t\tfinishFail(error.message)\n\t\t})\n}\n\nexport function uploadFileAbort(this: MiniAppContext, { uploadId }: { uploadId?: string } = {}) {\n\tif (!uploadId) return\n\tconst xhr = uploadRequests.get(uploadId)\n\tif (xhr) {\n\t\txhr.abort()\n\t\treturn\n\t}\n\tif (uploadPendingResolves.has(uploadId)) {\n\t\tuploadAbortedBeforeStart.add(uploadId)\n\t}\n}\n", "/**\n * In-renderer pub/sub store for the simulator's native UI overlays (toast,\n * loading, modal, action sheet).\n *\n * The wx.* UI handlers in `simulator-api-ui.ts` run inside the simulator\n * renderer (via runApiAsync) but cannot touch React state directly. They push\n * the desired overlay into this singleton; the `DeviceShell`-resident\n * `<UiOverlay>` subscribes and renders it inside the device frame (above the\n * page <webview>, clipped to the bezel \u2014 the same layering the status/nav bar\n * already use). User interaction (modal confirm/cancel, action-sheet tap) is\n * routed back to the handler through the `onResult` / `onSelect` callbacks the\n * handler attaches to the dialog state.\n */\n\nexport interface ToastState {\n title: string\n icon: 'success' | 'error' | 'loading' | 'none'\n image?: string\n /** ms before auto-dismiss; Infinity for showLoading (dismissed explicitly). */\n duration: number\n mask: boolean\n}\n\nexport interface ModalDialogState {\n kind: 'modal'\n title: string\n content: string\n showCancel: boolean\n cancelText: string\n cancelColor: string\n confirmText: string\n confirmColor: string\n editable: boolean\n placeholderText: string\n /** The renderer calls this when the user taps confirm/cancel. */\n onResult: (confirmed: boolean, content?: string) => void\n}\n\nexport interface ActionSheetDialogState {\n kind: 'actionSheet'\n itemList: string[]\n itemColor: string\n /** The renderer calls this with the tapped index, or -1 to cancel. */\n onSelect: (index: number) => void\n}\n\nexport interface HalfSheetDialogState {\n kind: 'halfSheet'\n islandName: string\n islandAvatar: string\n memberCount: string\n /** The renderer calls this with `true` (join) or `false` (cancel / mask tap). */\n onResult: (confirmed: boolean) => void\n}\n\nexport interface CapsuleMenuItem {\n label: string\n icon: string\n}\n\nexport interface CapsuleMenuDialogState {\n kind: 'capsuleMenu'\n appName: string\n appAvatar: string\n appVersion: string\n items: CapsuleMenuItem[]\n /** The renderer calls this with the tapped item index, or -1 to cancel. */\n onSelect: (index: number) => void\n}\n\nexport interface ShareDialogState {\n kind: 'share'\n type: 'link' | 'image'\n title: string\n desc: string\n url: string\n cover: string\n image: string\n /** The renderer calls this with the tapped platform index, or -1 to cancel. */\n onSelect: (index: number) => void\n}\n\nexport interface OpenPostDialogState {\n kind: 'openPost'\n islandName: string\n islandImage: string\n /** The renderer calls this with `true` (confirm) or `false` (cancel). */\n onResult: (confirmed: boolean) => void\n}\n\nexport type DialogState =\n | ModalDialogState\n | ActionSheetDialogState\n | HalfSheetDialogState\n | CapsuleMenuDialogState\n | ShareDialogState\n | OpenPostDialogState\n\nexport interface UiOverlayState {\n toast: ToastState | null\n dialog: DialogState | null\n}\n\ntype Listener = (state: UiOverlayState) => void\n\nclass UiOverlayBus {\n private state: UiOverlayState = { toast: null, dialog: null }\n private readonly listeners = new Set<Listener>()\n\n getState(): UiOverlayState {\n return this.state\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n showToast(toast: ToastState): void {\n this.set({ ...this.state, toast })\n }\n\n hideToast(): void {\n this.set({ ...this.state, toast: null })\n }\n\n /**\n * Clear `toast` only if it is still the active one. The toast auto-dismiss\n * timer holds a reference to the toast it scheduled; a newer showToast may\n * have replaced it in the meantime, and a blind `hideToast()` from the stale\n * timer would wrongly clear the new toast.\n */\n dismissToast(toast: ToastState): void {\n if (this.state.toast === toast) this.hideToast()\n }\n\n showDialog(dialog: DialogState): void {\n this.set({ ...this.state, dialog })\n }\n\n hideDialog(): void {\n this.set({ ...this.state, dialog: null })\n }\n\n private set(next: UiOverlayState): void {\n this.state = next\n for (const listener of this.listeners) listener(next)\n }\n}\n\nexport const uiOverlayBus = new UiOverlayBus()\n", "/**\n * DevTools implementations for the WeChat-style interaction APIs that native\n * platforms (iOS / Android / Harmony) provide but the web container lacks:\n * showToast / hideToast / showLoading / hideLoading / showModal /\n * showActionSheet.\n *\n * Each handler is bound with `this` = MiniApp instance (via\n * AppManager.registerApi \u2192 MiniApp.invokeApi). The overlay is rendered by the\n * DeviceShell-resident `<UiOverlay>`; these handlers only push state into\n * `uiOverlayBus` and fire the success/fail/complete callbacks \u2014 immediately for\n * toast/loading, and on user interaction for modal/actionSheet (defaults and\n * verdict strings mirror the Android `InteractionApi`).\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindCallbacks } from './simulator-api-helpers'\nimport { uiOverlayBus } from './ui-overlay-bus'\n\ninterface ToastOpts {\n title?: string\n icon?: 'success' | 'error' | 'loading' | 'none'\n image?: string\n duration?: number\n mask?: boolean\n success?: unknown\n fail?: unknown\n complete?: unknown\n}\n\nexport function showToast(this: MiniAppContext, opts: ToastOpts = {}) {\n const { onSuccess, onComplete } = bindCallbacks(this, opts)\n uiOverlayBus.showToast({\n title: opts.title ?? '',\n icon: opts.icon ?? 'success',\n image: opts.image,\n duration: opts.duration ?? 1500,\n mask: opts.mask ?? false,\n })\n onSuccess?.({ errMsg: 'showToast:ok' })\n onComplete?.()\n}\n\nexport function hideToast(this: MiniAppContext, opts: { success?: unknown; complete?: unknown } = {}) {\n const { onSuccess, onComplete } = bindCallbacks(this, opts)\n uiOverlayBus.hideToast()\n onSuccess?.({ errMsg: 'hideToast:ok' })\n onComplete?.()\n}\n\nexport function showLoading(\n this: MiniAppContext,\n opts: { title?: string; mask?: boolean; success?: unknown; complete?: unknown } = {},\n) {\n const { onSuccess, onComplete } = bindCallbacks(this, opts)\n uiOverlayBus.showToast({\n title: opts.title ?? '',\n icon: 'loading',\n duration: Infinity,\n mask: opts.mask ?? false,\n })\n onSuccess?.({ errMsg: 'showLoading:ok' })\n onComplete?.()\n}\n\nexport function hideLoading(this: MiniAppContext, opts: { success?: unknown; complete?: unknown } = {}) {\n const { onSuccess, onComplete } = bindCallbacks(this, opts)\n uiOverlayBus.hideToast()\n onSuccess?.({ errMsg: 'hideLoading:ok' })\n onComplete?.()\n}\n\ninterface ModalOpts {\n title?: string\n content?: string\n showCancel?: boolean\n cancelText?: string\n cancelColor?: string\n confirmText?: string\n confirmColor?: string\n editable?: boolean\n placeholderText?: string\n success?: unknown\n fail?: unknown\n complete?: unknown\n}\n\nexport function showModal(this: MiniAppContext, opts: ModalOpts = {}) {\n const { onSuccess, onComplete } = bindCallbacks(this, opts)\n const editable = opts.editable ?? false\n // Guard against a double resolution (rapid double-tap / a re-render firing the\n // handler twice) settling the callbacks more than once.\n let settled = false\n uiOverlayBus.showDialog({\n kind: 'modal',\n title: opts.title ?? '',\n content: opts.content ?? '',\n showCancel: opts.showCancel ?? true,\n cancelText: opts.cancelText ?? '\u53D6\u6D88',\n cancelColor: opts.cancelColor ?? '#000000',\n confirmText: opts.confirmText ?? '\u786E\u5B9A',\n confirmColor: opts.confirmColor ?? '#576B95',\n editable,\n placeholderText: opts.placeholderText ?? '',\n onResult: (confirmed, content) => {\n if (settled) return\n settled = true\n uiOverlayBus.hideDialog()\n const result: Record<string, unknown> = {\n confirm: confirmed,\n cancel: !confirmed,\n errMsg: 'showModal:ok',\n }\n if (editable) result.content = content ?? ''\n onSuccess?.(result)\n onComplete?.()\n },\n })\n}\n\ninterface ActionSheetOpts {\n itemList?: string[]\n itemColor?: string\n success?: unknown\n fail?: unknown\n complete?: unknown\n}\n\nexport function showActionSheet(this: MiniAppContext, opts: ActionSheetOpts = {}) {\n const { onSuccess, onFail, onComplete } = bindCallbacks(this, opts)\n let settled = false\n uiOverlayBus.showDialog({\n kind: 'actionSheet',\n itemList: opts.itemList ?? [],\n itemColor: opts.itemColor ?? '#000000',\n onSelect: (index) => {\n if (settled) return\n settled = true\n uiOverlayBus.hideDialog()\n if (index === -1) {\n onFail?.({ errMsg: 'showActionSheet:fail cancel' })\n } else {\n onSuccess?.({ tapIndex: index, errMsg: 'showActionSheet:ok' })\n }\n onComplete?.()\n },\n })\n}\n\ninterface ShareOpts {\n type?: string\n title?: string\n desc?: string\n url?: string\n cover?: string\n image?: string\n success?: unknown\n fail?: unknown\n complete?: unknown\n}\n\nexport function share(this: MiniAppContext, opts: ShareOpts = {}) {\n const { onSuccess, onFail, onComplete } = bindCallbacks(this, opts)\n let settled = false\n const shareType = (opts.type === 'image' ? 'image' : 'link') as 'link' | 'image'\n uiOverlayBus.showDialog({\n kind: 'share',\n type: shareType,\n title: opts.title ?? '',\n desc: opts.desc ?? '',\n url: opts.url ?? '',\n cover: opts.cover ?? '',\n image: opts.image ?? '',\n onSelect: (index) => {\n if (settled) return\n settled = true\n uiOverlayBus.hideDialog()\n if (index === -1) {\n onFail?.({ errMsg: 'share:fail cancel' })\n } else {\n onSuccess?.({ errMsg: 'share:ok' })\n }\n onComplete?.()\n },\n })\n}\n\ninterface OpenPostOpts {\n islandId?: string\n appId?: string\n islandName?: string\n islandImage?: string\n joined?: boolean\n bizData?: string\n spuId?: string\n files?: string\n success?: unknown\n fail?: unknown\n complete?: unknown\n}\n\nexport function openPost(this: MiniAppContext, opts: OpenPostOpts = {}) {\n const { onSuccess, onFail, onComplete } = bindCallbacks(this, opts)\n let settled = false\n uiOverlayBus.showDialog({\n kind: 'openPost',\n islandName: opts.islandName ?? '',\n islandImage: opts.islandImage ?? '',\n onResult: (confirmed) => {\n if (settled) return\n settled = true\n uiOverlayBus.hideDialog()\n if (confirmed) {\n onSuccess?.({ islandId: opts.islandId ?? '', errMsg: 'openPost:ok' })\n } else {\n onFail?.({ errMsg: 'openPost:fail cancel' })\n }\n onComplete?.()\n },\n })\n}\n\ninterface JoinIslandOpts {\n islandName?: string\n islandAvatar?: string\n memberCount?: string\n success?: unknown\n fail?: unknown\n complete?: unknown\n}\n\nexport function joinIsland(this: MiniAppContext, opts: JoinIslandOpts = {}) {\n const { onSuccess, onFail, onComplete } = bindCallbacks(this, opts)\n let settled = false\n uiOverlayBus.showDialog({\n kind: 'halfSheet',\n islandName: opts.islandName ?? 'Mock Island',\n islandAvatar: opts.islandAvatar ?? '',\n memberCount: opts.memberCount ?? '128',\n onResult: (confirmed) => {\n if (settled) return\n settled = true\n uiOverlayBus.hideDialog()\n if (confirmed) {\n onSuccess?.({ errMsg: 'joinIsland:ok' })\n } else {\n onFail?.({ errMsg: 'joinIsland:fail cancel' })\n }\n onComplete?.()\n },\n })\n}\n", "/**\n * DevTools API stubs for wx.xxx APIs that exist on native platforms\n * (iOS / Android / Harmony) but are missing in the web container.\n *\n * Each exported function is bound with `this` = MiniApp instance\n * (via AppManager.registerApi \u2192 MiniApp.invokeApi).\n */\n\nimport type { MiniAppContext } from './types'\nimport { bindCallbacks } from './simulator-api-helpers'\nimport {\n\tsetStorageSync,\n\tgetStorageSync,\n\tremoveStorageSync,\n\tclearStorageSync,\n\tgetStorageInfoSync,\n\tsetStorage,\n\tgetStorage,\n\tremoveStorage,\n\tclearStorage,\n\tgetStorageInfo,\n} from './simulator-api-storage'\nimport {\n\thideKeyboard,\n\tadjustPosition,\n\tmakePhoneCall,\n\tchooseContact,\n\taddPhoneContact,\n\tvibrateShort,\n\tvibrateLong,\n\tscanCode,\n\tgetClipboardData,\n\tsetClipboardData,\n\tgetNetworkType,\n} from './simulator-api-device'\nimport {\n\tchooseImage,\n\tpreviewImage,\n\tcompressImage,\n\tsaveCanvasTempFile,\n\tsaveImageToPhotosAlbum,\n\tgetImageInfo,\n\tchooseMedia,\n\tchooseVideo,\n\taudioCreate,\n\taudioListen,\n\taudioSetProp,\n\taudioPlay,\n\taudioPause,\n\taudioStop,\n\taudioSeek,\n\taudioDestroy,\n} from './simulator-api-media'\nimport {\n\tfsAccess,\n\tfsStat,\n\tfsReadFile,\n\tfsWriteFile,\n\tfsAppendFile,\n\tfsCopyFile,\n\tfsRename,\n\tfsUnlink,\n\tfsMkdir,\n\tfsRmdir,\n\tfsReaddir,\n\tfsGetFileInfo,\n\tfsSaveFile,\n\tfsGetSavedFileList,\n\tfsRemoveSavedFile,\n\tfsTruncate,\n\tfsUnzip,\n} from './simulator-api-fs'\nimport {\n\tdownloadFile,\n\tuploadFile,\n\tuploadFileAbort,\n} from './simulator-api-network'\nexport {\n\tdownloadFile,\n\tuploadFile,\n\tuploadFileAbort,\n} from './simulator-api-network'\nimport {\n\tshowToast,\n\thideToast,\n\tshowLoading,\n\thideLoading,\n\tshowModal,\n\tshowActionSheet,\n\tshare,\n\topenPost,\n\tjoinIsland,\n} from './simulator-api-ui'\n\n// \u2500\u2500\u2500 Base \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function canIUse(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown }) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\t// In devtools all standard APIs are considered available.\n\tonSuccess?.(true)\n\tonComplete?.()\n\t// Also return synchronously for callers that use the return value.\n\treturn true\n}\n\nexport function getWindowInfo(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\n\tconst { wb, di, dev, pixelRatio, screenWidth, screenHeight, windowWidth, windowHeight } = readWindowMetrics(this)\n\tconst bar = this.parent?.getStatusBarRect?.() ?? { height: dev?.statusBarHeight ?? 0 }\n\tconst statusBarHeight = (di['statusBarHeight'] as number | undefined) ?? bar.height\n\n\tconst info = {\n\t\tpixelRatio, screenWidth, screenHeight, windowWidth, windowHeight,\n\t\tstatusBarHeight,\n\t\tsafeArea: {\n\t\t\twidth: wb.width,\n\t\t\theight: wb.height - statusBarHeight,\n\t\t\ttop: statusBarHeight,\n\t\t\tbottom: wb.height,\n\t\t\tleft: 0,\n\t\t\tright: wb.width,\n\t\t},\n\t}\n\tonSuccess?.(info)\n\tonComplete?.()\n\treturn info\n}\n\nexport function getSystemSetting(this: MiniAppContext, { success, complete }: { success?: unknown; complete?: unknown } = {}) {\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\n\tconst info = {\n\t\tbluetoothEnabled: false,\n\t\tlocationEnabled: true,\n\t\twifiEnabled: true,\n\t\tdeviceOrientation: 'portrait',\n\t}\n\tonSuccess?.(info)\n\tonComplete?.()\n\treturn info\n}\n\n// \u2500\u2500\u2500 System Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction readWindowMetrics(miniApp: MiniAppContext) {\n\t// Priority unchanged: __deviceInfo \u2192 host DOM rect. Only the last-resort\n\t// fallback follows the CURRENTLY emulated device (SimulatorMiniApp tracks\n\t// boot config device + live DEVICE_CHANGE) instead of a hardcoded 375x812.\n\tconst dev = miniApp.getDeviceMetrics?.()\n\tconst wb = miniApp.parent?.el?.querySelector('.dimina-native-webview__root')?.getBoundingClientRect()\n\t\t?? { width: dev?.screenWidth ?? 375, height: dev?.screenHeight ?? 812 }\n\tconst di = (window as Window & { __deviceInfo?: Record<string, unknown> }).__deviceInfo || {}\n\treturn {\n\t\twb,\n\t\tdi,\n\t\tdev,\n\t\tpixelRatio: (di['pixelRatio'] as number | undefined) || dev?.pixelRatio || window.devicePixelRatio || 2,\n\t\tscreenWidth: (di['screenWidth'] as number | undefined) || wb.width,\n\t\tscreenHeight: (di['screenHeight'] as number | undefined) || wb.height,\n\t\twindowWidth: wb.width,\n\t\twindowHeight: wb.height,\n\t}\n}\n\nfunction buildSystemInfo(miniApp: MiniAppContext) {\n\tconst { wb, di, dev, pixelRatio, screenWidth, screenHeight, windowWidth, windowHeight } = readWindowMetrics(miniApp)\n\tconst statusBarHeight = (di['statusBarHeight'] as number | undefined) ?? dev?.statusBarHeight ?? 0\n\t// Bottom inset sourced from safeAreaInsets.bottom (the single source \u2014 the\n\t// legacy flat `safeAreaBottom` field is decommissioned).\n\tconst bottomInset = (di['safeAreaInsets'] as { bottom?: number } | undefined)?.bottom\n\t\t?? dev?.safeAreaInsets?.bottom ?? 0\n\n\treturn {\n\t\tbrand: di['brand'] || 'devtools',\n\t\tmodel: di['model'] || 'devtools',\n\t\tpixelRatio, screenWidth, screenHeight, windowWidth, windowHeight,\n\t\tstatusBarHeight,\n\t\tlanguage: 'zh_CN',\n\t\tversion: '8.0.5',\n\t\tsystem: di['system'] || 'iOS 16.0',\n\t\tplatform: di['platform'] || 'ios',\n\t\tfontSizeSetting: 16,\n\t\tSDKVersion: '3.0.0',\n\t\tdeviceOrientation: 'portrait',\n\t\tsafeArea: {\n\t\t\twidth: wb.width,\n\t\t\theight: wb.height - statusBarHeight - bottomInset,\n\t\t\ttop: statusBarHeight,\n\t\t\tbottom: wb.height - bottomInset,\n\t\t\tleft: 0,\n\t\t\tright: wb.width,\n\t\t},\n\t}\n}\n\nexport function getSystemInfoAsync(this: MiniAppContext, opts: { success?: unknown; complete?: unknown }) {\n\tconst { success, complete } = opts\n\tconst { onSuccess, onComplete } = bindCallbacks(this, { success, complete })\n\tonSuccess?.(buildSystemInfo(this))\n\tonComplete?.()\n}\n\nexport function getSystemInfo(this: MiniAppContext, opts: { success?: unknown; complete?: unknown }) {\n\tgetSystemInfoAsync.call(this, opts)\n}\n\nexport function getSystemInfoSync(this: MiniAppContext) {\n\treturn buildSystemInfo(this)\n}\n\n// \u2500\u2500\u2500 Open API: Account Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function getAccountInfoSync(this: MiniAppContext) {\n\treturn {\n\t\tminiProgram: {\n\t\t\tappId: this.appId || '',\n\t\t\tenvVersion: 'develop',\n\t\t\tversion: '',\n\t\t},\n\t}\n}\n\n// \u2500\u2500\u2500 Collect all APIs into a map \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// `opts: never` (not the wider `unknown`) so every handler below \u2014 each typed\n// with its OWN specific opts shape (`getSystemInfoAsync`'s `{ success?, complete? }`,\n// `canIUse`'s `string`, \u2026) \u2014 remains assignable into this map: a function\n// parameter is checked contravariantly, and `never` is assignable into any\n// concrete opts type, whereas `unknown` (the caller-side \"any value\" type)\n// would reject every narrower handler signature here. Callers of this map\n// always cast to a caller-appropriate handler type before invoking (see\n// simulator-app.tsx / main-api-runner.ts) \u2014 this declaration only has to\n// typecheck the object literal itself.\nexport const simulatorApis: Record<string, (this: MiniAppContext, opts: never) => unknown> = {\n\t// Base\n\tcanIUse,\n\tgetSystemInfo,\n\tgetSystemInfoAsync,\n\tgetSystemInfoSync,\n\tgetWindowInfo,\n\tgetSystemSetting,\n\t// UI: interaction\n\tshowToast,\n\thideToast,\n\tshowLoading,\n\thideLoading,\n\tshowModal,\n\tshowActionSheet,\n\tshare,\n\topenPost,\n\tjoinIsland,\n\t// Network\n\tdownloadFile,\n\tuploadFile,\n\tuploadFileAbort,\n\t// Storage (sync)\n\tsetStorageSync,\n\tgetStorageSync,\n\tremoveStorageSync,\n\tclearStorageSync,\n\tgetStorageInfoSync,\n\t// Storage (async)\n\tsetStorage,\n\tgetStorage,\n\tremoveStorage,\n\tclearStorage,\n\tgetStorageInfo,\n\t// Open API\n\tgetAccountInfoSync,\n\t// Device\n\thideKeyboard,\n\tadjustPosition,\n\tmakePhoneCall,\n\tchooseContact,\n\taddPhoneContact,\n\tvibrateShort,\n\tvibrateLong,\n\tscanCode,\n\tgetClipboardData,\n\tsetClipboardData,\n\tgetNetworkType,\n\t// Media: Image\n\tchooseImage,\n\tpreviewImage,\n\tcompressImage,\n\tsaveCanvasTempFile,\n\tsaveImageToPhotosAlbum,\n\tgetImageInfo,\n\t// Media: Video\n\tchooseMedia,\n\tchooseVideo,\n\t// Media: Audio (service-apis/audio)\n\taudioCreate,\n\taudioListen,\n\taudioSetProp,\n\taudioPlay,\n\taudioPause,\n\taudioStop,\n\taudioSeek,\n\taudioDestroy,\n\t// Filesystem (service-apis/file)\n\tfsAccess,\n\tfsStat,\n\tfsReadFile,\n\tfsWriteFile,\n\tfsAppendFile,\n\tfsCopyFile,\n\tfsRename,\n\tfsUnlink,\n\tfsMkdir,\n\tfsRmdir,\n\tfsReaddir,\n\tfsGetFileInfo,\n\tfsSaveFile,\n\tfsGetSavedFileList,\n\tfsRemoveSavedFile,\n\tfsTruncate,\n\tfsUnzip,\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAgBA,sBAAkE;;;ACd3D,IAAM,kBAAkB;AAAA,EAC7B,OAAO;AAAA,EACP,SAAS;AAAA,EACT,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMd,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOb,YAAY;AACd;AAEO,IAAM,mBAAmB;AAAA,EAC9B,WAAW;AAAA,EACX,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,YAAY;AAAA;AAAA,EAEZ,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOV,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASf,UAAU;AACZ;;;AC9BA,SAAS,kBAAoC;AAI3C,QAAM,WAAW,EAAE,OAAO,KAAK,QAAQ,KAAK,GAAG,GAAG,GAAG,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,KAAK,QAAQ,IAAI;AACjG,QAAM,WAAW;AAAA,IACf,uBAAuB,MAAM;AAAA,EAC/B;AACA,SAAO;AAAA,IACL,OAAO;AAAA,IACP,wBAAwB,MAAM;AAAA,IAC9B,QAAQ;AAAA,MACN,IAAI;AAAA,QACF,eAAe,MAAM;AAAA,MACvB;AAAA,MACA,kBAAkB,OAAO,EAAE,QAAQ,EAAE;AAAA,IACvC;AAAA,EACF;AACF;AAEO,SAAS,YACd,UACA,MACA,QACwB;AACxB,QAAM,UAAU,SAAS,IAAI;AAC7B,MAAI,CAAC,SAAS;AACZ,WAAO,QAAQ,QAAQ,EAAE,IAAI,OAAO,QAAQ,GAAG,IAAI,mBAAmB,CAAC;AAAA,EACzE;AAEA,SAAO,IAAI,QAAuB,CAAC,YAAY;AAC7C,QAAI,WAAW;AACf,UAAM,SAAS,CAAC,YAAiC;AAC/C,UAAI,SAAU;AACd,iBAAW;AACX,cAAQ,OAAO;AAAA,IACjB;AAEA,UAAM,UAAU,uBAAO,iBAAiB;AACxC,UAAM,OAAO,uBAAO,cAAc;AAClC,UAAM,WAAW,uBAAO,kBAAkB;AAE1C,UAAM,OAAO,gBAAgB;AAC7B,UAAM,MAAwB;AAAA,MAC5B,GAAG;AAAA,MACH,uBAAuB,IAAa;AAClC,YAAI,OAAO,UAAa,OAAO,KAAM,QAAO;AAC5C,eAAO,IAAI,SAAoB;AAC7B,gBAAM,MAAM,KAAK,CAAC;AAClB,cAAI,OAAO,SAAS;AAClB,mBAAO,EAAE,IAAI,MAAM,QAAQ,IAAI,CAAC;AAAA,UAClC,WAAW,OAAO,MAAM;AACtB,kBAAM,SACJ,OAAO,OAAO,QAAQ,YAAY,YAAa,MAC3C,OAAQ,IAA6B,MAAM,IAC3C,GAAG,IAAI;AACb,mBAAO,EAAE,IAAI,OAAO,QAAQ,QAAQ,IAAI,CAAC;AAAA,UAC3C;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,aACJ,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,IACzD,EAAE,GAAI,OAAmC,IACzC,CAAC;AACP,UAAM,aAAa,WAAW,YAAY,UAAa,WAAW,YAAY;AAC9E,UAAM,UAAU,WAAW,SAAS,UAAa,WAAW,SAAS;AACrE,UAAM,cAAc,WAAW,aAAa,UAAa,WAAW,aAAa;AAEjF,eAAW,UAAU;AACrB,eAAW,OAAO;AAClB,QAAI,YAAa,YAAW,WAAW;AAEvC,QAAI;AACF,YAAM,MAAO,QAA4B,KAAK,KAAK,UAAU;AAC7D,UAAI,OAAO,OAAQ,IAA6B,SAAS,YAAY;AACnE,gBAAQ,QAAQ,GAA2B,EAAE;AAAA,UAC3C,CAAC,MAAM,OAAO,EAAE,IAAI,MAAM,QAAQ,EAAE,CAAC;AAAA,UACrC,CAAC,QAAiB;AAChB,kBAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,mBAAO,EAAE,IAAI,OAAO,QAAQ,GAAG,IAAI,SAAS,GAAG,GAAG,CAAC;AAAA,UACrD;AAAA,QACF;AACA;AAAA,MACF;AACA,UAAI,CAAC,cAAc,CAAC,SAAS;AAC3B,eAAO,EAAE,IAAI,MAAM,QAAQ,IAAI,CAAC;AAAA,MAClC;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,aAAO,EAAE,IAAI,OAAO,QAAQ,GAAG,IAAI,SAAS,GAAG,GAAG,CAAC;AAAA,IACrD;AAAA,EACF,CAAC;AACH;;;ACxHO,SAAS,cACd,KACA,MACA;AACA,SAAO;AAAA,IACL,WAAW,IAAI,uBAAuB,KAAK,OAAO;AAAA,IAClD,QAAQ,IAAI,uBAAuB,KAAK,IAAI;AAAA,IAC5C,YAAY,IAAI,uBAAuB,KAAK,QAAQ;AAAA,EACtD;AACF;AAOO,SAAS,gBACd,SAC+E;AAC/E,SAAO,SAAgC,OAA+C,CAAC,GAAG;AACxF,UAAM,EAAE,QAAQ,WAAW,IAAI,cAAc,MAAM,IAAI;AACvD,aAAS,EAAE,QAAQ,GAAG,OAAO,mCAAmC,CAAC;AACjE,iBAAa;AAAA,EACf;AACF;;;ACbA,SAAS,aAAa,OAAe,KAAqB;AACzD,SAAO,GAAG,KAAK,IAAI,GAAG;AACvB;AAGA,SAAS,eAAe,OAAyB;AAChD,QAAM,SAAS,GAAG,KAAK;AACvB,QAAM,OAAiB,CAAC;AACxB,WAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC7C,UAAM,IAAI,aAAa,IAAI,CAAC;AAC5B,QAAI,KAAK,EAAE,WAAW,MAAM,EAAG,MAAK,KAAK,CAAC;AAAA,EAC3C;AACA,SAAO;AACR;AAEA,SAAS,WAAW,OAAe,KAAa,MAAqB;AACpE,QAAM,aAAa,OAAO,SAAS,WAAW,KAAK,UAAU,IAAI,IAAI,OAAO,IAAI;AAChF,eAAa,QAAQ,aAAa,OAAO,GAAG,GAAG,UAAU;AAC1D;AAGA,SAAS,UAAU,OAAe,KAA4C;AAC7E,QAAM,MAAM,aAAa,QAAQ,aAAa,OAAO,GAAG,CAAC;AACzD,MAAI,QAAQ,KAAM,QAAO;AACzB,MAAI;AAAE,WAAO,EAAE,MAAM,KAAK,MAAM,GAAG,EAAa;AAAA,EAAE,QAAQ;AAAE,WAAO,EAAE,MAAM,IAAI;AAAA,EAAE;AAClF;AAEA,SAAS,aAAa,OAAqB;AAC1C,iBAAe,KAAK,EAAE,QAAQ,OAAK,aAAa,WAAW,CAAC,CAAC;AAC9D;AAEA,SAAS,cAAc,OAA2E;AACjG,QAAM,SAAS,GAAG,KAAK;AACvB,QAAM,OAAiB,CAAC;AACxB,MAAI,cAAc;AAClB,aAAW,WAAW,eAAe,KAAK,GAAG;AAC5C,SAAK,KAAK,QAAQ,MAAM,OAAO,MAAM,CAAC;AACtC,UAAM,OAAO,aAAa,QAAQ,OAAO;AACzC,mBAAe,OAAO,KAAK,SAAS,IAAI;AAAA,EACzC;AACA,SAAO,EAAE,MAAM,aAAa,WAAW,KAAK,OAAO,KAAK;AACzD;AAIO,SAAS,eAAqC,EAAE,KAAK,KAAK,GAAmC;AACnG,aAAW,KAAK,OAAO,KAAK,IAAI;AACjC;AAEO,SAAS,eAAqC,EAAE,IAAI,GAAoB;AAE9E,SAAO,UAAU,KAAK,OAAO,GAAG,KAAK,EAAE,MAAM,GAAG;AACjD;AAEO,SAAS,kBAAwC,EAAE,IAAI,GAAoB;AACjF,eAAa,WAAW,aAAa,KAAK,OAAO,GAAG,CAAC;AACtD;AAEO,SAAS,mBAAuC;AACtD,eAAa,KAAK,KAAK;AACxB;AAEO,SAAS,qBAAyC;AACxD,SAAO,cAAc,KAAK,KAAK;AAChC;AAIO,SAAS,WAEf,EAAE,KAAK,MAAM,SAAS,MAAM,SAAS,GACpC;AACD,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AACzF,MAAI;AACH,eAAW,KAAK,OAAO,KAAK,IAAI;AAChC,gBAAY,EAAE,QAAQ,gBAAgB,CAAC;AAAA,EACxC,SAAS,GAAG;AACX,aAAS,EAAE,QAAQ,mBAAoB,EAAY,OAAO,GAAG,CAAC;AAAA,EAC/D;AACA,eAAa;AACd;AAEO,SAAS,WAEf,EAAE,KAAK,SAAS,MAAM,SAAS,GAC9B;AACD,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAGzF,QAAM,QAAQ,UAAU,KAAK,OAAO,GAAG;AACvC,MAAI,CAAC,OAAO;AACX,aAAS,EAAE,QAAQ,iCAAiC,CAAC;AAAA,EACtD,OAAO;AACN,gBAAY,EAAE,MAAM,MAAM,MAAM,QAAQ,gBAAgB,CAAC;AAAA,EAC1D;AACA,eAAa;AACd;AAEO,SAAS,cAEf,EAAE,KAAK,SAAS,MAAM,SAAS,GAC9B;AACD,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AACzF,MAAI;AACH,iBAAa,WAAW,aAAa,KAAK,OAAO,GAAG,CAAC;AACrD,gBAAY,EAAE,QAAQ,mBAAmB,CAAC;AAAA,EAC3C,SAAS,GAAG;AACX,aAAS,EAAE,QAAQ,sBAAuB,EAAY,OAAO,GAAG,CAAC;AAAA,EAClE;AACA,eAAa;AACd;AAEO,SAAS,aAAmC,EAAE,SAAS,SAAS,IAA+C,CAAC,GAAG;AACzH,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,SAAS,CAAC;AAC3E,eAAa,KAAK,KAAK;AACvB,cAAY,EAAE,QAAQ,kBAAkB,CAAC;AACzC,eAAa;AACd;AAEO,SAAS,eAAqC,EAAE,SAAS,SAAS,IAA+C,CAAC,GAAG;AAC3H,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,SAAS,CAAC;AAC3E,QAAM,OAAO,cAAc,KAAK,KAAK;AACrC,cAAY,EAAE,GAAG,MAAM,QAAQ,oBAAoB,CAAC;AACpD,eAAa;AACd;;;ACjIO,SAAS,aAAmC,EAAE,SAAS,SAAS,IAA+C,CAAC,GAAG;AACzH,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,SAAS,CAAC;AAE3E,MAAI,SAAS,iBAAiB,OAAQ,SAAS,cAA8B,SAAS,YAAY;AACjG;AAAC,IAAC,SAAS,cAA8B,KAAK;AAAA,EAC/C;AACA,cAAY,EAAE,QAAQ,kBAAkB,CAAC;AACzC,eAAa;AACd;AAEO,SAAS,eAAqC,EAAE,SAAS,SAAS,IAA+C,CAAC,GAAG;AAC3H,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,SAAS,CAAC;AAC3E,cAAY,EAAE,QAAQ,oBAAoB,CAAC;AAC3C,eAAa;AACd;AAIO,SAAS,cAEf,EAAE,aAAa,SAAS,MAAM,SAAS,GACtC;AACD,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAEzF,MAAI;AACH,WAAO,KAAK,OAAO,WAAW,EAAE;AAChC,gBAAY,EAAE,QAAQ,mBAAmB,CAAC;AAAA,EAC3C,SAAS,OAAO;AACf,aAAS,EAAE,QAAQ,sBAAuB,MAAgB,OAAO,GAAG,CAAC;AAAA,EACtE;AACA,eAAa;AACd;AAIO,IAAM,gBAAgB,gBAAgB,eAAe;AAErD,IAAM,kBAAkB,gBAAgB,iBAAiB;AAIzD,SAAS,aAAmC,EAAE,SAAS,SAAS,IAA8D,CAAC,GAAG;AACxI,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,SAAS,CAAC;AAE3E,MAAI,UAAU,QAAS,WAAU,QAAQ,EAAE;AAC3C,cAAY,EAAE,QAAQ,kBAAkB,CAAC;AACzC,eAAa;AACd;AAEO,SAAS,YAAkC,EAAE,SAAS,SAAS,IAA+C,CAAC,GAAG;AACxH,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,SAAS,CAAC;AAC3E,MAAI,UAAU,QAAS,WAAU,QAAQ,GAAG;AAC5C,cAAY,EAAE,QAAQ,iBAAiB,CAAC;AACxC,eAAa;AACd;AAIO,IAAM,WAAW,gBAAgB,UAAU;AAI3C,SAAS,iBAEf,EAAE,SAAS,MAAM,SAAS,IAA+D,CAAC,GACzF;AACD,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AACzF,SAAO,UAAU,UAAU,SAAS,EAAE;AAAA,IACrC,CAAC,SAAS;AACT,kBAAY,EAAE,MAAM,QAAQ,sBAAsB,CAAC;AACnD,mBAAa;AAAA,IACd;AAAA,IACA,CAAC,UAAmB;AACnB,eAAS,EAAE,QAAQ,yBAA0B,OAAiB,WAAW,KAAK,GAAG,CAAC;AAClF,mBAAa;AAAA,IACd;AAAA,EACD;AACD;AAEO,SAAS,iBAEf,EAAE,MAAM,SAAS,MAAM,SAAS,IAA8E,CAAC,GAC9G;AACD,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAEzF,MAAI,OAAO,SAAS,UAAU;AAC7B,aAAS,EAAE,QAAQ,yCAAyC,CAAC;AAC7D,iBAAa;AACb,WAAO,QAAQ,QAAQ;AAAA,EACxB;AACA,SAAO,UAAU,UAAU,UAAU,IAAI,EAAE;AAAA,IAC1C,MAAM;AACL,kBAAY,EAAE,QAAQ,sBAAsB,CAAC;AAC7C,mBAAa;AAAA,IACd;AAAA,IACA,CAAC,UAAmB;AACnB,eAAS,EAAE,QAAQ,yBAA0B,OAAiB,WAAW,KAAK,GAAG,CAAC;AAClF,mBAAa;AAAA,IACd;AAAA,EACD;AACD;AAcA,SAAS,iBAAiB,eAAqC;AAC9D,UAAQ,eAAe;AAAA,IACtB,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AACJ,aAAO;AAAA,IACR,KAAK;AAAA,IACL,KAAK;AACJ,aAAO;AAAA,IACR;AACC,aAAO;AAAA,EACT;AACD;AAOO,SAAS,mBAAmB,MAAgC;AAClE,MAAI,CAAC,KAAK,OAAQ,QAAO;AACzB,MAAI,KAAK,SAAS,UAAU,KAAK,SAAS,WAAY,QAAO;AAC7D,MAAI,KAAK,SAAS,WAAY,QAAO,iBAAiB,KAAK,aAAa;AACxE,MAAI,KAAK,cAAe,QAAO,iBAAiB,KAAK,aAAa;AAClE,SAAO;AACR;AAEO,SAAS,eAEf,EAAE,SAAS,SAAS,IAA+C,CAAC,GACnE;AACD,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,SAAS,CAAC;AAC3E,QAAM,aAAc,UAEjB;AACH,QAAM,cAAc,mBAAmB;AAAA,IACtC,QAAQ,UAAU;AAAA,IAClB,MAAM,YAAY;AAAA,IAClB,eAAe,YAAY;AAAA,EAC5B,CAAC;AACD,cAAY,EAAE,aAAa,QAAQ,oBAAoB,CAAC;AACxD,eAAa;AACd;;;AC3IO,SAAS,cACf,QACA,UACA,MACA,cACsB;AACtB,QAAM,aAA6C,CAAC;AAEpD,aAAW,CAAC,SAAS,QAAQ,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC3D,UAAM,WAA0B,CAAC,aAAa;AAC7C,WAAK,aAAa,UAAU,QAAQ,CAAC;AAAA,IACtC;AACA,WAAO,iBAAiB,SAAS,QAAQ;AACzC,eAAW,KAAK,CAAC,SAAS,QAAQ,CAAC;AAAA,EACpC;AAEA,SAAO,MAAM;AACZ,eAAW,CAAC,SAAS,QAAQ,KAAK,YAAY;AAC7C,aAAO,oBAAoB,SAAS,QAAQ;AAAA,IAC7C;AACA,eAAW,SAAS;AAAA,EACrB;AACD;;;AC7BA,IAAM,YAAY,oBAAI,IAAkB;AACxC,IAAI,aAAkC;AAMtC,SAAS,iBAAyB;AACjC,QAAM,IAAK,WAA0D;AACrE,MAAI,KAAK,OAAO,EAAE,eAAe,WAAY,QAAO,EAAE,WAAW;AACjE,SAAO,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AAC5D;AAEO,SAAS,mBAAmB,MAAoB;AACtD,QAAMA,QAAO,iBAAiB,eAAe,CAAC;AAC9C,YAAU,IAAIA,OAAM,IAAI;AACxB,cAAY,MAAMA,OAAM,IAAI;AAC5B,SAAOA;AACR;AAiBA,eAAsB,oBAAoBC,OAA6B;AACtE,QAAM,SAAS,UAAU,IAAIA,KAAI;AACjC,MAAI,OAAQ,QAAO;AAEnB,QAAM,WAAW,MAAM,MAAMA,KAAI;AACjC,MAAI,CAAC,SAAS,IAAI;AACjB,UAAM,IAAI,MAAM,wCAAUA,KAAI,EAAE;AAAA,EACjC;AACA,QAAM,OAAO,MAAM,SAAS,KAAK;AAMjC,YAAU,IAAIA,OAAM,IAAI;AACxB,SAAO;AACR;AAEO,SAAS,gBAAgBA,OAAc,MAAY,WAAW,QAAgB;AACpF,QAAM,QAAQ;AACd,MAAI,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,KAAK,GAAG;AACxD,WAAO,MAAM;AAAA,EACd;AAEA,MAAI;AACH,UAAM,MAAM,IAAI,IAAIA,OAAM,OAAO,SAAS,IAAI;AAC9C,UAAM,UAAU,IAAI,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,IAAI;AAC5D,QAAI,QAAS,QAAO,mBAAmB,OAAO;AAAA,EAC/C,QAAQ;AAAA,EAER;AAEA,SAAO;AACR;;;AC1DA,SAAS,eAAe,YAAsB,QAAqD;AAClG,MAAI,WAAW,WAAW,KAAK,WAAW,CAAC,MAAM,SAAU,QAAO;AAClE,SAAO,WAAW,UAAU,SAAS;AACtC;AASA,SAAS,iBACR,SACA,QACA,KACA,QACO;AACP,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,OAAO;AACb,QAAM,SAAS,OAAO;AACtB,QAAM,WAAW,OAAO;AACxB,MAAI,OAAO,QAAS,OAAM,aAAa,WAAW,OAAO,OAAO;AAChE,QAAM,MAAM,UAAU;AACtB,WAAS,KAAK,YAAY,KAAK;AAE/B,QAAM,cAAc,MAAM,MAAM,OAAO;AAEvC,QAAM,iBAAiB,UAAU,MAAM;AACtC,UAAM,QAAQ,MAAM,KAAK,MAAM,SAAS,CAAC,CAAC;AAC1C,QAAI,MAAM,WAAW,GAAG;AACvB,UAAI,SAAS,EAAE,QAAQ,GAAG,OAAO,eAAe,CAAC;AACjD,UAAI,aAAa;AACjB,kBAAY;AACZ;AAAA,IACD;AACA,SAAK,OAAO,OAAO,WAAW;AAAA,EAC/B,CAAC;AAED,QAAM,MAAM;AACb;AAIO,SAAS,YAEf,EAAE,QAAQ,GAAG,YAAY,QAAQ,SAAS,MAAM,SAAS,GASxD;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,QAAM,EAAE,WAAW,WAAW,IAAI;AAClC,QAAM,kBAAkB,0BAA0B,KAAK;AACvD,QAAM,uBAAuB,qBAAqB,YAAY,CAAC,SAAS,QAAQ,CAAC;AAEjF;AAAA,IACC;AAAA,IACA,EAAE,QAAQ,WAAW,UAAU,kBAAkB,GAAG,SAAS,eAAe,sBAAsB,MAAM,EAAE;AAAA,IAC1G;AAAA,IACA,CAAC,UAAU,gBAAgB;AAC1B,YAAM,QAAQ,SAAS,MAAM,GAAG,eAAe;AAC/C,YAAM,gBAAgB,MAAM,IAAI,OAAK,mBAAmB,CAAC,CAAC;AAC1D,YAAMC,aAAY,MAAM,IAAI,CAAC,GAAG,OAAO,EAAE,MAAM,cAAc,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE;AAChF,kBAAY,EAAE,eAAe,WAAAA,YAAW,QAAQ,iBAAiB,CAAC;AAClE,mBAAa;AACb,kBAAY;AAAA,IACb;AAAA,EACD;AACD;AAEO,SAAS,aAEf,EAAE,MAAM,SAAS,SAAS,SAAS,GAClC;AACD,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,SAAS,CAAC;AAE3E,MAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC/B,iBAAa;AACb;AAAA,EACD;AAGA,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,MAAM,UACb;AACD,QAAM,MAAM,SAAS,cAAc,KAAK;AACxC,MAAI,MAAM,WAAW,KAAK,CAAC,KAAK;AAChC,MAAI,MAAM,UAAU;AACpB,UAAQ,YAAY,GAAG;AACvB,UAAQ,iBAAiB,SAAS,MAAM,QAAQ,OAAO,CAAC;AACxD,WAAS,KAAK,YAAY,OAAO;AAEjC,cAAY,EAAE,QAAQ,kBAAkB,CAAC;AACzC,eAAa;AACd;AAEO,SAAS,cAEf,EAAE,KAAK,UAAU,IAAI,SAAS,MAAM,SAAS,GAO5C;AACD,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAEzF,QAAM,MAAM,IAAI,MAAM;AACtB,MAAI,cAAc;AAClB,MAAI,SAAS,MAAM;AAClB,QAAI;AACH,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,QAAQ,IAAI;AACnB,aAAO,SAAS,IAAI;AACpB,YAAM,MAAM,OAAO,WAAW,IAAI;AAClC,UAAI,UAAU,KAAK,GAAG,CAAC;AACvB,aAAO;AAAA,QACN,CAAC,SAAS;AACT,cAAI,MAAM;AACT,kBAAM,eAAe,mBAAmB,IAAI;AAC5C,wBAAY,EAAE,cAAc,QAAQ,mBAAmB,CAAC;AAAA,UACzD,OAAO;AACN,qBAAS,EAAE,QAAQ,uCAAuC,CAAC;AAAA,UAC5D;AACA,uBAAa;AAAA,QACd;AAAA,QACA;AAAA,QACA,UAAU;AAAA,MACX;AAAA,IACD,SAAS,OAAO;AACf,eAAS,EAAE,QAAQ,sBAAuB,MAAgB,OAAO,GAAG,CAAC;AACrE,mBAAa;AAAA,IACd;AAAA,EACD;AACA,MAAI,UAAU,MAAM;AACnB,aAAS,EAAE,QAAQ,sCAAsC,CAAC;AAC1D,iBAAa;AAAA,EACd;AACA,MAAI,MAAM;AACX;AAEO,SAAS,mBAEf,EAAE,SAAS,SAAS,MAAM,SAAS,GAOlC;AACD,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAEzF,MAAI,CAAC,SAAS;AACb,aAAS,EAAE,QAAQ,4CAA4C,CAAC;AAChE,iBAAa;AACb;AAAA,EACD;AAEA,QAAM,QAAQ,6BAA6B,KAAK,OAAO;AACvD,MAAI,CAAC,OAAO;AACX,aAAS,EAAE,QAAQ,6CAA6C,CAAC;AACjE,iBAAa;AACb;AAAA,EACD;AAEA,MAAI;AACH,UAAM,WAAW,MAAM,CAAC;AACxB,UAAM,MAAM,KAAK,MAAM,CAAC,CAAE;AAC1B,UAAM,QAAQ,IAAI,WAAW,IAAI,MAAM;AACvC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,OAAM,CAAC,IAAI,IAAI,WAAW,CAAC;AAChE,UAAM,OAAO,IAAI,KAAK,CAAC,KAAK,GAAG,EAAE,MAAM,SAAS,CAAC;AACjD,UAAM,eAAe,mBAAmB,IAAI;AAC5C,gBAAY,EAAE,cAAc,QAAQ,0BAA0B,CAAC;AAAA,EAChE,SAAS,OAAO;AACf,aAAS,EAAE,QAAQ,6BAA8B,MAAgB,OAAO,GAAG,CAAC;AAAA,EAC7E;AACA,eAAa;AACd;AAEO,SAAS,uBAEf,EAAE,UAAU,SAAS,MAAM,SAAS,GACnC;AACD,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAEzF,MAAI;AACH,UAAM,IAAI,SAAS,cAAc,GAAG;AACpC,MAAE,OAAO;AACT,MAAE,WAAW;AACb,MAAE,MAAM;AACR,gBAAY,EAAE,QAAQ,4BAA4B,CAAC;AAAA,EACpD,SAAS,OAAO;AACf,aAAS,EAAE,QAAQ,+BAAgC,MAAgB,OAAO,GAAG,CAAC;AAAA,EAC/E;AACA,eAAa;AACd;AAEO,SAAS,aAEf,EAAE,KAAK,SAAS,MAAM,SAAS,GAC9B;AACD,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAEzF,QAAM,MAAM,IAAI,MAAM;AACtB,MAAI,cAAc;AAClB,MAAI,SAAS,MAAM;AAClB,gBAAY;AAAA,MACX,OAAO,IAAI;AAAA,MACX,QAAQ,IAAI;AAAA,MACZ,MAAM;AAAA,MACN,aAAa;AAAA,MACb,MAAM;AAAA,MACN,QAAQ;AAAA,IACT,CAAC;AACD,iBAAa;AAAA,EACd;AACA,MAAI,UAAU,MAAM;AACnB,aAAS,EAAE,QAAQ,qCAAqC,CAAC;AACzD,iBAAa;AAAA,EACd;AACA,MAAI,MAAM;AACX;AAOA,IAAM,4BAA4B;AAClC,IAAM,6BAA6B;AAanC,SAAS,qBAAqB,OAAgB,UAA8B;AAC3E,SAAO,MAAM,QAAQ,KAAK,IACvB,MAAM,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ,IAC/D;AACJ;AAEA,SAAS,0BAA0B,OAAwB;AAC1D,QAAM,IAAI,OAAO,KAAK;AACtB,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC;AAC/C;AAEA,SAAS,qBAAqB,WAA6B;AAC1D,QAAM,WAAW,UAAU,SAAS,KAAK;AACzC,QAAM,aAAa,YAAY,UAAU,SAAS,OAAO;AACzD,QAAM,aAAa,YAAY,UAAU,SAAS,OAAO;AACzD,MAAI,cAAc,WAAY,QAAO;AACrC,SAAO,aAAa,YAAY;AACjC;AAEA,SAAS,yBAAyB,OAAyD;AAC1F,QAAM,QAAQ,IAAI,IAAI,MAAM,IAAI,UAAQ,KAAK,QAAQ,CAAC;AACtD,MAAI,MAAM,OAAO,EAAG,QAAO;AAC3B,SAAO,MAAM,CAAC,GAAG,YAAY;AAC9B;AAEA,SAAS,kBAAkB,KAAyD;AACnF,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC/B,UAAM,MAAM,IAAI,MAAM;AACtB,QAAI,SAAS,MAAM,QAAQ,EAAE,OAAO,IAAI,gBAAgB,GAAG,QAAQ,IAAI,iBAAiB,EAAE,CAAC;AAC3F,QAAI,UAAU,MAAM,QAAQ,EAAE,OAAO,GAAG,QAAQ,EAAE,CAAC;AACnD,QAAI,MAAM;AAAA,EACX,CAAC;AACF;AAEA,SAAS,kBAAkB,KAAsG;AAChI,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC/B,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAI,UAAU;AACd,QAAI;AACJ,UAAM,SAAS,CAAC,aAA6F;AAC5G,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,KAAK;AAClB,UAAI,UAAW,cAAa,SAAS;AAIrC,YAAM,mBAAmB;AACzB,YAAM,WAAW;AACjB,YAAM,UAAU;AAChB,YAAM,gBAAgB,KAAK;AAC3B,YAAM,KAAK;AACX,cAAQ,QAAQ;AAAA,IACjB;AACA,UAAM,gBAAgB,CAAC,OAAe,WAAoC;AACzE,aAAO,IAAI,QAAQ,CAAC,iBAAiB;AACpC,YAAI,WAAW;AACf,cAAM,OAAO,CAAC,UAAkB;AAC/B,cAAI,SAAU;AACd,qBAAW;AACX,uBAAa,KAAK;AAAA,QACnB;AACA,YAAI;AACJ,YAAI;AACH,mBAAS,SAAS,cAAc,QAAQ;AACxC,iBAAO,QAAQ,SAAS;AACxB,iBAAO,SAAS,UAAU;AAC1B,gBAAM,MAAM,OAAO,WAAW,IAAI;AAClC,eAAK,UAAU,OAAO,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;AAAA,QACxD,QAAQ;AACP,eAAK,EAAE;AACP;AAAA,QACD;AACA,cAAM,oBAAoB,MAAM;AAC/B,cAAI;AACH,iBAAK,OAAO,UAAU,cAAc,GAAG,CAAC;AAAA,UACzC,QAAQ;AACP,iBAAK,EAAE;AAAA,UACR;AAAA,QACD;AACA,cAAM,gBAAgB,WAAW,mBAAmB,0BAA0B;AAC9E,YAAI;AACH,iBAAO;AAAA,YACN,CAAC,SAAS;AACT,2BAAa,aAAa;AAQ1B,kBAAI,WAAW,SAAU;AACzB,kBAAI,MAAM;AACT,qBAAK,mBAAmB,IAAI,CAAC;AAAA,cAC9B,OAAO;AACN,kCAAkB;AAAA,cACnB;AAAA,YACD;AAAA,YACA;AAAA,YACA;AAAA,UACD;AAAA,QACD,QAAQ;AACP,uBAAa,aAAa;AAC1B,4BAAkB;AAAA,QACnB;AAAA,MACD,CAAC;AAAA,IACF;AACA,UAAM,QAAQ,WAAW,MAAM,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,mBAAmB,GAAG,CAAC,GAAG,yBAAyB;AAE7H,UAAM,UAAU;AAChB,UAAM,QAAQ;AACd,UAAM,mBAAmB,MAAM;AAC9B,YAAM,QAAQ,MAAM,cAAc;AAClC,YAAM,SAAS,MAAM,eAAe;AACpC,YAAM,WAAW,OAAO,SAAS,MAAM,QAAQ,IAAI,MAAM,WAAW;AACpE,YAAM,WAAW,EAAE,OAAO,QAAQ,UAAU,mBAAmB,GAAG;AAClE,UAAI,CAAC,SAAS,CAAC,UAAU,YAAY,GAAG;AACvC,eAAO,QAAQ;AACf;AAAA,MACD;AACA,YAAM,WAAW,YAAY;AAC5B,YAAI,WAAW;AACd,uBAAa,SAAS;AACtB,sBAAY;AAAA,QACb;AACA,cAAM,oBAAoB,MAAM,cAAc,OAAO,MAAM;AAC3D,eAAO,EAAE,GAAG,UAAU,kBAAkB,CAAC;AAAA,MAC1C;AACA,kBAAY,WAAW,MAAM,OAAO,QAAQ,GAAG,0BAA0B;AACzE,UAAI;AACH,cAAM,cAAc,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,WAAW,IAAI,CAAC;AAAA,MAC/D,QAAQ;AACP,eAAO,QAAQ;AAAA,MAChB;AAAA,IACD;AACA,UAAM,UAAU,MAAM,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,mBAAmB,GAAG,CAAC;AACxF,UAAM,MAAM;AAAA,EACb,CAAC;AACF;AAEA,eAAe,yBAAyB,MAA0C;AACjF,QAAM,eAAe,mBAAmB,IAAI;AAC5C,QAAM,WAA0B,KAAK,KAAK,WAAW,OAAO,IAAI,UAAU;AAE1E,MAAI,aAAa,SAAS;AACzB,UAAMC,YAAW,MAAM,kBAAkB,YAAY;AACrD,WAAO;AAAA,MACN;AAAA,MACA,MAAM,KAAK;AAAA,MACX,UAAUA,UAAS;AAAA,MACnB,QAAQA,UAAS;AAAA,MACjB,OAAOA,UAAS;AAAA,MAChB,mBAAmBA,UAAS;AAAA,MAC5B;AAAA,MACA,iBAAiB;AAAA,IAClB;AAAA,EACD;AAEA,QAAM,WAAW,MAAM,kBAAkB,YAAY;AACrD,SAAO;AAAA,IACN;AAAA,IACA,MAAM,KAAK;AAAA,IACX,UAAU;AAAA,IACV,QAAQ,SAAS;AAAA,IACjB,OAAO,SAAS;AAAA,IAChB,mBAAmB;AAAA,IACnB;AAAA,IACA,iBAAiB;AAAA,EAClB;AACD;AAEO,SAAS,YAEf,EAAE,QAAQ,GAAG,YAAY,CAAC,SAAS,OAAO,GAAG,aAAa,CAAC,SAAS,QAAQ,GAAG,SAAS,QAAQ,SAAS,MAAM,SAAS,GAWvH;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI;AAC1C,QAAM,kBAAkB,0BAA0B,KAAK;AACvD,QAAM,sBAAsB,qBAAqB,WAAW,CAAC,SAAS,OAAO,CAAC;AAC9E,QAAM,uBAAuB,qBAAqB,YAAY,CAAC,SAAS,QAAQ,CAAC;AAEjF;AAAA,IACC;AAAA,IACA;AAAA,MACC,QAAQ,qBAAqB,mBAAmB;AAAA,MAChD,UAAU,kBAAkB;AAAA,MAC5B,SAAS,eAAe,sBAAsB,MAAM;AAAA,IACrD;AAAA,IACA;AAAA,IACA,OAAO,UAAU,gBAAgB;AAChC,YAAM,QAAQ,SAAS,MAAM,GAAG,eAAe;AAC/C,UAAI;AACH,cAAMD,aAAY,MAAM,QAAQ,IAAI,MAAM,IAAI,wBAAwB,CAAC;AACvE,oBAAY;AAAA,UACX,WAAAA;AAAA,UACA,MAAM,yBAAyBA,UAAS;AAAA,UACxC,aAAa;AAAA,UACb,QAAQ;AAAA,QACT,CAAC;AAAA,MACF,SAAS,OAAO;AACf,iBAAS,EAAE,QAAQ,oBAAqB,MAAgB,OAAO,GAAG,CAAC;AAAA,MACpE,UAAE;AACD,qBAAa;AACb,oBAAY;AAAA,MACb;AAAA,IACD;AAAA,EACD;AACD;AAEO,SAAS,YAEf,EAAE,YAAY,QAAQ,SAAS,MAAM,SAAS,GAS7C;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,QAAM,EAAE,WAAW,WAAW,IAAI;AAClC,QAAM,uBAAuB,qBAAqB,YAAY,CAAC,SAAS,QAAQ,CAAC;AAEjF;AAAA,IACC;AAAA,IACA,EAAE,QAAQ,WAAW,UAAU,OAAO,SAAS,eAAe,sBAAsB,MAAM,EAAE;AAAA,IAC5F;AAAA,IACA,OAAO,OAAO,gBAAgB;AAC7B,YAAM,OAAO,MAAM,CAAC;AACpB,YAAM,eAAe,mBAAmB,IAAI;AAC5C,YAAM,WAAW,MAAM,kBAAkB,YAAY;AACrD,kBAAY;AAAA,QACX;AAAA,QACA,UAAU,SAAS;AAAA,QACnB,MAAM,KAAK;AAAA,QACX,OAAO,SAAS;AAAA,QAChB,QAAQ,SAAS;AAAA,QACjB,QAAQ;AAAA,MACT,CAAC;AACD,mBAAa;AACb,kBAAY;AAAA,IACb;AAAA,EACD;AACD;AAkBA,IAAM,kBAA0C;AAAA,EAC/C,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AACV;AAEA,IAAM,qBAAqB,oBAAI,IAA8B;AAE7D,IAAM,uBAAuB,oBAAI,IAAiC;AAElE,IAAM,aAAa,oBAAI,IAAkD;AAGzE,SAAS,cAAc,OAAyB,OAAkC;AACjF,SAAO;AAAA,IACN;AAAA,IACA,aAAa,MAAM,eAAe;AAAA,IAClC,UAAU,OAAO,SAAS,MAAM,QAAQ,IAAI,MAAM,WAAW;AAAA,IAC7D,UAAU,MAAM,SAAS,SAAS,MAAM,SAAS,IAAI,MAAM,SAAS,SAAS,CAAC,IAAI;AAAA,IAClF,QAAQ,MAAM;AAAA,EACf;AACD;AAEO,SAAS,YAAkC,EAAE,QAAQ,GAAwB;AACnF,qBAAmB,IAAI,SAAS,IAAI,MAAM,CAAC;AAC5C;AAcO,SAAS,YAAkC,EAAE,SAAS,QAAQ,GAA0C;AAC9G,QAAM,QAAQ,mBAAmB,IAAI,OAAO;AAC5C,QAAM,OAAO,KAAK,uBAAuB,OAAO;AAChD,MAAI,CAAC,SAAS,CAAC,KAAM;AAErB,aAAW,IAAI,SAAS,IAAI;AAG5B,uBAAqB,IAAI,OAAO,IAAI;AACpC,QAAM,UAAU;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAS,cAAc,OAAO,KAAK;AAAA,EACpC;AACA,uBAAqB,IAAI,SAAS,OAAO;AAC1C;AAEO,SAAS,aAEf,EAAE,SAAS,MAAM,OAAO,WAAW,MAAM,QAAQ,cAAc,SAAS,GAUvE;AACD,QAAM,QAAQ,mBAAmB,IAAI,OAAO;AAC5C,MAAI,CAAC,MAAO;AACZ,UAAQ,MAAM;AAAA,IACb,KAAK;AACJ,YAAM,MAAM;AACZ,UAAI,aAAa,KAAM,OAAM,cAAc;AAC3C,UAAI,QAAQ,KAAM,OAAM,OAAO;AAC/B,UAAI,UAAU,KAAM,OAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,MAAM,CAAC;AAClE,UAAI,gBAAgB,KAAM,OAAM,eAAe;AAC/C,UAAI,SAAU,OAAM,KAAK,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AACzC;AAAA,IACD,KAAK;AAAa,YAAM,cAAc,OAAO,KAAK,KAAK;AAAG;AAAA,IAC1D,KAAK;AAAY,YAAM,WAAW,CAAC,CAAC;AAAO;AAAA,IAC3C,KAAK;AAAQ,YAAM,OAAO,CAAC,CAAC;AAAO;AAAA,IACnC,KAAK;AAAU,YAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,OAAO,KAAK,KAAK,CAAC,CAAC;AAAG;AAAA,IAC5E,KAAK;AAAgB,YAAM,eAAe,OAAO,KAAK,KAAK;AAAG;AAAA,EAC/D;AACD;AAEO,SAAS,UAAgC,EAAE,SAAS,IAAI,GAAsC;AACpG,QAAM,QAAQ,mBAAmB,IAAI,OAAO;AAC5C,MAAI,CAAC,MAAO;AACZ,MAAI,OAAO,MAAM,QAAQ,IAAK,OAAM,MAAM;AAC1C,QAAM,KAAK,EAAE,MAAM,MAAM;AAAA,EAAC,CAAC;AAC5B;AAEO,SAAS,WAAiC,EAAE,QAAQ,GAAwB;AAClF,qBAAmB,IAAI,OAAO,GAAG,MAAM;AACxC;AAEO,SAAS,UAAgC,EAAE,QAAQ,GAAwB;AACjF,QAAM,QAAQ,mBAAmB,IAAI,OAAO;AAC5C,MAAI,CAAC,MAAO;AACZ,QAAM,MAAM;AACZ,QAAM,cAAc;AAEpB,aAAW,IAAI,OAAO,IAAI,cAAc,OAAO,MAAM,CAAC;AACvD;AAEO,SAAS,UAAgC,EAAE,SAAS,SAAS,GAA0C;AAC7G,QAAM,QAAQ,mBAAmB,IAAI,OAAO;AAC5C,MAAI,CAAC,MAAO;AACZ,QAAM,cAAc;AACrB;AAEO,SAAS,aAAmC,EAAE,QAAQ,GAAwB;AACpF,uBAAqB,IAAI,OAAO,IAAI;AACpC,uBAAqB,OAAO,OAAO;AACnC,aAAW,OAAO,OAAO;AAEzB,QAAM,QAAQ,mBAAmB,IAAI,OAAO;AAC5C,MAAI,CAAC,MAAO;AACZ,QAAM,MAAM;AACZ,QAAM,gBAAgB,KAAK;AAC3B,QAAM,KAAK;AACX,qBAAmB,OAAO,OAAO;AAClC;;;ACxpBA,qBAAe;AACf,uBAAiB;AAgBjB,IAAM,gBAAgB;AAQf,SAAS,cAAsB;AACrC,QAAM,MAAO,OAAO,YAAY,eAAe,QAAQ,OAAO,QAAQ,IAAI,eAAgB;AAC1F,MAAI,KAAK;AACR,WAAO,iBAAAE,QAAK,KAAK,KAAK,OAAO;AAAA,EAC9B;AACA,SAAO,iBAAAA,QAAK,KAAK,eAAAC,QAAG,QAAQ,GAAG,WAAW,OAAO;AAClD;AAEO,SAAS,aAAa,KAAoC;AAChE,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,MAAI,CAAC,IAAI,WAAW,aAAa,EAAG,QAAO;AAE3C,QAAM,MAAM,IAAI,MAAM,cAAc,MAAM;AAC1C,MAAI,IAAI,WAAW,EAAG,QAAO;AAI7B,MAAI;AACJ,MAAI;AACH,cAAU,mBAAmB,GAAG;AAAA,EACjC,QAAQ;AACP,WAAO;AAAA,EACR;AAKA,MAAI,QAAQ,SAAS,IAAI,EAAG,QAAO;AAKnC,YAAU,QAAQ,QAAQ,WAAW,EAAE;AACvC,MAAI,QAAQ,WAAW,EAAG,QAAO;AAKjC,QAAM,WAAW,QAAQ,MAAM,QAAQ;AACvC,MAAI,SAAS,KAAK,OAAK,MAAM,QAAQ,MAAM,GAAG,EAAG,QAAO;AAGxD,MAAI;AACJ,MAAI;AACJ,MAAI,QAAQ,WAAW,OAAO,GAAG;AAChC,WAAO;AACP,eAAW;AAAA,EACZ,WAAW,QAAQ,WAAW,SAAS,GAAG;AACzC,WAAO;AACP,eAAW;AAAA,EACZ,OAAO;AACN,WAAO;AACP,eAAW;AAAA,EACZ;AAGA,MAAI,SAAS,OAAO;AACnB,WAAO,EAAE,MAAM,SAAS;AAAA,EACzB;AAKA,QAAM,OAAO,YAAY;AACzB,QAAM,SAAS,iBAAAD,QAAK,KAAK,MAAM,OAAO;AACtC,QAAM,aAAa,iBAAAA,QAAK,UAAU,MAAM;AACxC,MAAI,eAAe,QAAQ,CAAC,WAAW,WAAW,OAAO,iBAAAA,QAAK,GAAG,EAAG,QAAO;AAE3E,SAAO,EAAE,MAAM,UAAU,UAAU,WAAW;AAC/C;;;AC3FA,eAAe,UAAU,KAA8B;AACtD,MAAI;AACH,UAAM,OAAO,MAAM,oBAAoB,GAAG;AAC1C,WAAO,MAAM,aAAa,IAAI;AAAA,EAC/B,SACO,KAAK;AACX,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAM,IAAI,MAAM,yBAAyB,GAAG,MAAM,GAAG,IAAI,EAAE,OAAO,IAAI,CAAC;AAAA,EACxE;AACD;AAEA,SAAS,cAAc,OAAsC;AAC5D,SAAO,OAAO,UAAU,SAAS,KAAK,KAAK,MAAM;AAClD;AAEA,eAAe,aAAa,MAA6B;AACxD,QAAM,WAAW;AACjB,MAAI,OAAO,SAAS,gBAAgB,YAAY;AAC/C,UAAM,KAAK,MAAM,SAAS,YAAY;AACtC,WAAO,OAAO,KAAK,IAAI,WAAW,EAAE,CAAC;AAAA,EACtC;AAEA,MAAI,OAAO,eAAe,YAAY;AACrC,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACrD;AAEA,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACvC,UAAM,SAAS,IAAI,WAAW;AAC9B,WAAO,UAAU,MAAM,OAAO,OAAO,SAAS,IAAI,MAAM,kBAAkB,CAAC;AAC3E,WAAO,SAAS,MAAM;AACrB,YAAM,SAAS,OAAO;AACtB,UAAI,CAAC,cAAc,MAAM,GAAG;AAC3B,eAAO,IAAI,MAAM,yCAAyC,CAAC;AAC3D;AAAA,MACD;AACA,cAAQ,OAAO,KAAK,IAAI,WAAW,MAAM,CAAC,CAAC;AAAA,IAC5C;AACA,WAAO,kBAAkB,IAAI;AAAA,EAC9B,CAAC;AACF;AAIA,IAAM,MAA4B,OAAO,YAAY,cAAe,QAAQ,IAAI,IAAI;AAEpF,IAAM,QAAgC,OAAO,YAAY,cAAe,QAAQ,MAAM,IAAI;AAE1F,IAAM,UAAoC,OAAO,YAAY,cAAe,QAAQ,QAAQ,IAAI;AAkBhG,SAAS,iBAAiB,SAAiB,KAA2B;AACrE,MAAI,IAAK,QAAO;AAChB,MAAI,SAAS,EAAE,QAAQ,GAAG,OAAO,yCAAyC,CAAC;AAC3E,MAAI,aAAa;AACjB,SAAO;AACR;AAGA,SAAS,cAAc,GAAY,SAAiB,KAA6C;AAChG,QAAM,IAAI,aAAa,CAAC;AACxB,MAAI,EAAG,QAAO;AACd,MAAI,SAAS,EAAE,QAAQ,GAAG,OAAO,+BAA+B,CAAC;AACjE,MAAI,aAAa;AACjB,SAAO;AACR;AAOA,SAAS,eAAe,GAAkB,SAAiB,KAA6D;AACvH,MAAI,EAAE,YAAY,EAAE,SAAU,QAAO;AACrC,MAAI,SAAS,EAAE,QAAQ,GAAG,OAAO,0BAA0B,CAAC;AAC5D,MAAI,aAAa;AACjB,SAAO;AACR;AAGA,SAAS,eAAe,SAAiB,KAAkB;AAC1D,SAAO,CAAC,QAAe;AACtB,QAAI,SAAS,EAAE,QAAQ,GAAG,OAAO,SAAS,IAAI,OAAO,GAAG,CAAC;AACzD,QAAI,aAAa;AAAA,EAClB;AACD;AAQA,SAAS,aACR,SACA,KACA,SACC;AACD,SAAO,CAAC,QAAiB,SAAgB;AACxC,QAAI,KAAK;AACR,UAAI,SAAS,EAAE,QAAQ,GAAG,OAAO,SAAS,IAAI,OAAO,GAAG,CAAC;AAAA,IAC1D,OAAO;AACN,UAAI,YAAY,EAAE,GAAI,UAAU,QAAQ,GAAG,IAAI,IAAI,QAAY,QAAQ,GAAG,OAAO,MAAM,CAAC;AAAA,IACzF;AACA,QAAI,aAAa;AAAA,EAClB;AACD;AAQA,SAAS,gBACR,UACA,SACA,KACA,SACA,SACO;AACP,MAAI,MAAM,MAAM,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,GAAG,CAAC,aAAa;AACrE,QAAI,UAAU;AACb,UAAI,SAAS,EAAE,QAAQ,GAAG,OAAO,SAAS,SAAS,OAAO,GAAG,CAAC;AAC9D,UAAI,aAAa;AACjB;AAAA,IACD;AACA,YAAQ,aAAa,SAAS,KAAK,MAAM,OAAO,CAAC;AAAA,EAClD,CAAC;AACF;AAEO,SAAS,SAEf,EAAE,MAAAE,OAAM,SAAS,MAAM,SAAS,GAC/B;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI;AAC1C,MAAI,iBAAiB,YAAY,GAAG,EAAG;AACvC,QAAM,IAAI,cAAcA,OAAM,YAAY,GAAG;AAC7C,MAAI,CAAC,EAAG;AACR,MAAI,EAAE,SAAS,OAAO;AAErB,cAAUA,KAAI,EAAE;AAAA,MACf,MAAM;AAAE,oBAAY,EAAE,QAAQ,cAAc,CAAC;AAAG,qBAAa;AAAA,MAAE;AAAA,MAC/D,eAAe,YAAY,GAAG;AAAA,IAC/B;AACA;AAAA,EACD;AACA,MAAI,CAAC,EAAE,UAAU;AAChB,aAAS,EAAE,QAAQ,6BAA6B,CAAC;AACjD,iBAAa;AACb;AAAA,EACD;AACA,MAAI,OAAO,EAAE,UAAU,IAAI,UAAU,MAAM,aAAa,YAAY,GAAG,CAAC;AACzE;AAEO,SAAS,OAEf,EAAE,MAAAA,OAAM,YAAY,OAAO,SAAS,MAAM,SAAS,GAOlD;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI;AAC1C,MAAI,iBAAiB,UAAU,GAAG,EAAG;AACrC,QAAM,IAAI,cAAcA,OAAM,UAAU,GAAG;AAC3C,MAAI,CAAC,EAAG;AACR,MAAI,EAAE,SAAS,OAAO;AAErB,cAAUA,KAAI,EAAE;AAAA,MACf,CAAC,QAAQ;AACR,oBAAY;AAAA,UACX,OAAO;AAAA,YACN,MAAM,IAAI;AAAA,YACV,MAAM;AAAA,YACN,kBAAkB;AAAA,YAClB,kBAAkB;AAAA,YAClB,QAAQ;AAAA,YACR,aAAa;AAAA,UACd;AAAA,UACA,QAAQ;AAAA,QACT,CAAC;AACD,qBAAa;AAAA,MACd;AAAA,MACA,eAAe,UAAU,GAAG;AAAA,IAC7B;AACA;AAAA,EACD;AACA,MAAI,CAAC,EAAE,UAAU;AAChB,aAAS,EAAE,QAAQ,2BAA2B,CAAC;AAC/C,iBAAa;AACb;AAAA,EACD;AACA,QAAM,WAAW,EAAE;AACnB,MAAI,WAAW;AAEd,UAAM,WAAoC,CAAC;AAC3C,UAAM,UAAU,CAAC,KAAa,OAAoC;AACjE,UAAI,QAAQ,KAAK,EAAE,eAAe,KAAK,GAAG,CAAC,KAAK,YAAY;AAC3D,YAAI,KAAK;AAAE,aAAG,GAAG;AAAG;AAAA,QAAO;AAC3B,YAAI,UAAU,QAAQ;AACtB,YAAI,YAAY,GAAG;AAAE,aAAG,IAAI;AAAG;AAAA,QAAO;AACtC,mBAAW,SAAS,SAAS;AAC5B,gBAAM,OAAO,MAAM,KAAK,KAAK,MAAM,IAAI;AACvC,cAAI,KAAK,MAAM,CAAC,SAAS,MAAM;AAC9B,gBAAI,CAAC,SAAS;AACb,uBAAS,IAAI,IAAI;AAAA,gBAChB,MAAM,EAAE;AAAA,gBACR,MAAM,EAAE;AAAA,gBACR,kBAAkB,EAAE;AAAA,gBACpB,kBAAkB,EAAE;AAAA,gBACpB,QAAQ,EAAE,OAAO;AAAA,gBACjB,aAAa,EAAE,YAAY;AAAA,cAC5B;AAAA,YACD;AACA,gBAAI,MAAM,YAAY,GAAG;AACxB,sBAAQ,MAAM,MAAM;AAAE,oBAAI,EAAE,YAAY,EAAG,IAAG,IAAI;AAAA,cAAE,CAAC;AAAA,YACtD,OAAO;AACN,kBAAI,EAAE,YAAY,EAAG,IAAG,IAAI;AAAA,YAC7B;AAAA,UACD,CAAC;AAAA,QACF;AAAA,MACD,CAAC;AAAA,IACF;AACA,YAAQ,UAAU,CAAC,QAAQ;AAC1B,UAAI,KAAK;AACR,iBAAS,EAAE,QAAQ,eAAe,IAAI,OAAO,GAAG,CAAC;AAAA,MAClD,OAAO;AACN,oBAAY,EAAE,OAAO,UAAU,QAAQ,YAAY,CAAC;AAAA,MACrD;AACA,mBAAa;AAAA,IACd,CAAC;AAAA,EACF,OAAO;AACN,QAAI,KAAK,UAAU,aAAa,UAAU,KAAK,CAAC,OAAkB;AAAA,MACjE,OAAO;AAAA,QACN,MAAM,EAAE;AAAA,QACR,MAAM,EAAE;AAAA,QACR,kBAAkB,EAAE;AAAA,QACpB,kBAAkB,EAAE;AAAA,QACpB,QAAQ,EAAE,OAAO;AAAA,QACjB,aAAa,EAAE,YAAY;AAAA,MAC5B;AAAA,IACD,EAAE,CAAC;AAAA,EACJ;AACD;AAEO,SAAS,WAEf,EAAE,UAAU,UAAU,SAAS,MAAM,SAAS,GAO7C;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI;AAC1C,MAAI,iBAAiB,cAAc,GAAG,EAAG;AACzC,QAAM,IAAI,cAAc,UAAU,cAAc,GAAG;AACnD,MAAI,CAAC,EAAG;AACR,MAAI,EAAE,SAAS,OAAO;AACrB,cAAU,QAAQ,EAAE;AAAA,MACnB,CAAC,QAAQ;AACR,cAAM,OAAwB,WAAW,IAAI,SAAS,QAAQ,IAAI;AAClE,oBAAY,EAAE,MAAM,QAAQ,gBAAgB,CAAC;AAC7C,qBAAa;AAAA,MACd;AAAA,MACA,eAAe,cAAc,GAAG;AAAA,IACjC;AACA;AAAA,EACD;AACA,MAAI,CAAC,EAAE,UAAU;AAChB,aAAS,EAAE,QAAQ,+BAA+B,CAAC;AACnD,iBAAa;AACb;AAAA,EACD;AACA,MAAI,SAAS,EAAE,UAAU,YAAY,MAAM,aAAa,cAAc,KAAK,CAAC,UAA2B,EAAE,KAAK,EAAE,CAAC;AAClH;AAEO,SAAS,YAEf,EAAE,UAAU,MAAM,WAAW,QAAQ,SAAS,MAAM,SAAS,GAQ5D;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,MAAI,iBAAiB,eAAe,GAAG,EAAG;AAC1C,QAAM,IAAI,cAAc,UAAU,eAAe,GAAG;AACpD,MAAI,CAAC,EAAG;AACR,MAAI,CAAC,eAAe,GAAG,eAAe,GAAG,EAAG;AAC5C,kBAAgB,EAAE,UAAU,eAAe,KAAK,UAAQ,IAAI,UAAU,EAAE,UAAU,MAAgB,EAAE,SAAS,GAAG,IAAI,CAAC;AACtH;AAEO,SAAS,aAEf,EAAE,UAAU,MAAM,WAAW,QAAQ,SAAS,MAAM,SAAS,GAQ5D;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,MAAI,iBAAiB,gBAAgB,GAAG,EAAG;AAC3C,QAAM,IAAI,cAAc,UAAU,gBAAgB,GAAG;AACrD,MAAI,CAAC,EAAG;AACR,MAAI,CAAC,eAAe,GAAG,gBAAgB,GAAG,EAAG;AAC7C,MAAI,WAAW,EAAE,UAAU,MAAgB,EAAE,SAAS,GAAG,aAAa,gBAAgB,GAAG,CAAC;AAC3F;AAEO,SAAS,WAEf,EAAE,SAAS,UAAU,SAAS,MAAM,SAAS,GAO5C;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,QAAM,EAAE,QAAQ,WAAW,IAAI;AAC/B,MAAI,iBAAiB,cAAc,GAAG,EAAG;AACzC,QAAM,OAAO,cAAc,SAAS,cAAc,GAAG;AACrD,MAAI,CAAC,KAAM;AACX,QAAM,QAAQ,cAAc,UAAU,cAAc,GAAG;AACvD,MAAI,CAAC,MAAO;AACZ,MAAI,CAAC,eAAe,OAAO,cAAc,GAAG,EAAG;AAC/C,MAAI,KAAK,SAAS,OAAO;AAIxB,cAAU,OAAO,EAAE;AAAA,MAClB,SAAO,gBAAgB,MAAM,UAAU,cAAc,KAAK,UAAQ,IAAI,UAAU,MAAM,UAAU,KAAK,IAAI,CAAC;AAAA,MAC1G,eAAe,cAAc,GAAG;AAAA,IACjC;AACA;AAAA,EACD;AACA,MAAI,CAAC,KAAK,UAAU;AACnB,aAAS,EAAE,QAAQ,mCAAmC,CAAC;AACvD,iBAAa;AACb;AAAA,EACD;AACA,MAAI,SAAS,KAAK,UAAU,MAAM,UAAU,aAAa,cAAc,GAAG,CAAC;AAC5E;AAEO,SAAS,SAEf,EAAE,SAAS,SAAS,SAAS,MAAM,SAAS,GAO3C;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,MAAI,iBAAiB,YAAY,GAAG,EAAG;AACvC,QAAM,OAAO,cAAc,SAAS,YAAY,GAAG;AACnD,MAAI,CAAC,KAAM;AACX,QAAM,OAAO,cAAc,SAAS,YAAY,GAAG;AACnD,MAAI,CAAC,KAAM;AAGX,MAAI,CAAC,eAAe,MAAM,YAAY,GAAG,EAAG;AAC5C,MAAI,CAAC,eAAe,MAAM,YAAY,GAAG,EAAG;AAC5C,MAAI,OAAO,KAAK,UAAU,KAAK,UAAU,aAAa,YAAY,GAAG,CAAC;AACvE;AAEO,SAAS,SAEf,EAAE,UAAU,SAAS,MAAM,SAAS,GACnC;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,MAAI,iBAAiB,YAAY,GAAG,EAAG;AACvC,QAAM,IAAI,cAAc,UAAU,YAAY,GAAG;AACjD,MAAI,CAAC,EAAG;AACR,MAAI,CAAC,eAAe,GAAG,YAAY,GAAG,EAAG;AACzC,MAAI,OAAO,EAAE,UAAU,aAAa,YAAY,GAAG,CAAC;AACrD;AAEO,SAAS,QAEf,EAAE,SAAS,YAAY,OAAO,SAAS,MAAM,SAAS,GAOrD;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,MAAI,iBAAiB,WAAW,GAAG,EAAG;AACtC,QAAM,IAAI,cAAc,SAAS,WAAW,GAAG;AAC/C,MAAI,CAAC,EAAG;AACR,MAAI,CAAC,eAAe,GAAG,WAAW,GAAG,EAAG;AACxC,MAAI,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,aAAa,WAAW,GAAG,CAAC;AAClE;AAEO,SAAS,QAEf,EAAE,SAAS,YAAY,OAAO,SAAS,MAAM,SAAS,GAOrD;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,MAAI,iBAAiB,WAAW,GAAG,EAAG;AACtC,QAAM,IAAI,cAAc,SAAS,WAAW,GAAG;AAC/C,MAAI,CAAC,EAAG;AACR,MAAI,CAAC,eAAe,GAAG,WAAW,GAAG,EAAG;AAExC,QAAM,OAAQ,IAA+C,MAAM,IAAI;AACvE,OAAK,EAAE,UAAU,EAAE,UAAU,GAAsC,aAAa,WAAW,GAAG,CAAC;AAChG;AAEO,SAAS,UAEf,EAAE,SAAS,SAAS,MAAM,SAAS,GAClC;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,QAAM,EAAE,QAAQ,WAAW,IAAI;AAC/B,MAAI,iBAAiB,aAAa,GAAG,EAAG;AACxC,QAAM,IAAI,cAAc,SAAS,aAAa,GAAG;AACjD,MAAI,CAAC,EAAG;AAER,MAAI,EAAE,SAAS,SAAS,EAAE,SAAS,SAAS;AAC3C,aAAS,EAAE,QAAQ,kBAAkB,EAAE,IAAI,qCAAqC,CAAC;AACjF,iBAAa;AACb;AAAA,EACD;AACA,MAAI,CAAC,EAAE,UAAU;AAChB,aAAS,EAAE,QAAQ,8BAA8B,CAAC;AAClD,iBAAa;AACb;AAAA,EACD;AACA,MAAI,QAAQ,EAAE,UAAU,aAAa,aAAa,KAAK,CAAC,WAAqB,EAAE,MAAM,EAAE,CAAC;AACzF;AAEO,SAAS,cAEf,EAAE,UAAU,iBAAiB,SAAS,MAAM,SAAS,GAOpD;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI;AAC1C,MAAI,iBAAiB,iBAAiB,GAAG,EAAG;AAC5C,QAAM,IAAI,cAAc,UAAU,iBAAiB,GAAG;AACtD,MAAI,CAAC,EAAG;AACR,MAAI,EAAE,SAAS,OAAO;AACrB,cAAU,QAAQ,EAAE;AAAA,MACnB,CAAC,QAAQ;AACR,cAAM,SAAkC,EAAE,MAAM,IAAI,QAAQ,QAAQ,mBAAmB;AACvF,YAAI,iBAAiB;AACpB,cAAI;AACH,kBAAM,OAAO,QAAQ,WAAW,oBAAoB,QAAQ,QAAQ,MAAM;AAC1E,iBAAK,OAAO,GAAG;AACf,mBAAO,SAAS,KAAK,OAAO,KAAK;AAAA,UAClC,QACM;AAAA,UAEN;AAAA,QACD;AACA,oBAAY,MAAM;AAClB,qBAAa;AAAA,MACd;AAAA,MACA,eAAe,iBAAiB,GAAG;AAAA,IACpC;AACA;AAAA,EACD;AACA,MAAI,CAAC,EAAE,UAAU;AAChB,aAAS,EAAE,QAAQ,kCAAkC,CAAC;AACtD,iBAAa;AACb;AAAA,EACD;AACA,QAAM,WAAW,EAAE;AACnB,MAAI,KAAK,UAAU,CAAC,KAAK,MAAM;AAC9B,QAAI,KAAK;AACR,eAAS,EAAE,QAAQ,sBAAsB,IAAI,OAAO,GAAG,CAAC;AACxD,mBAAa;AACb;AAAA,IACD;AACA,UAAM,SAAkC,EAAE,MAAM,EAAE,MAAM,QAAQ,mBAAmB;AACnF,QAAI,iBAAiB;AAEpB,UAAI;AACH,cAAM,OAAO,QAAQ,WAAW,oBAAoB,QAAQ,QAAQ,MAAM;AAC1E,cAAM,SAAS,IAAI,iBAAiB,QAAQ;AAC5C,eAAO,GAAG,QAAQ,CAAC,UAAU,KAAK,OAAO,KAAe,CAAC;AACzD,eAAO,GAAG,OAAO,MAAM;AACtB,iBAAO,SAAS,KAAK,OAAO,KAAK;AACjC,sBAAY,MAAM;AAClB,uBAAa;AAAA,QACd,CAAC;AACD,eAAO,GAAG,SAAS,CAAC,YAAY;AAC/B,mBAAS,EAAE,QAAQ,sBAAsB,QAAQ,OAAO,GAAG,CAAC;AAC5D,uBAAa;AAAA,QACd,CAAC;AACD;AAAA,MACD,QAAQ;AAAA,MAER;AAAA,IACD;AACA,gBAAY,MAAM;AAClB,iBAAa;AAAA,EACd,CAAC;AACF;AAcO,SAAS,WAEf,EAAE,cAAc,UAAU,WAAW,SAAS,MAAM,SAAS,GAO5D;AACD,OAAK;AACL,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,QAAM,EAAE,QAAQ,WAAW,IAAI;AAC/B,MAAI,iBAAiB,cAAc,GAAG,EAAG;AACzC,QAAM,MAAM,cAAc,cAAc,cAAc,GAAG;AACzD,MAAI,CAAC,IAAK;AACV,QAAM,MAAM,MAAM,QAAQ,YAAY,KAAK;AAC3C,QAAM,KAAK,QAAQ,WAAW,IAAI;AAClC,QAAM,gBAAgB,mBAAmB,EAAE;AAC3C,QAAM,eAAe,aAAa,aAAa;AAC/C,MAAI,CAAC,gBAAgB,CAAC,aAAa,UAAU;AAE5C,aAAS,EAAE,QAAQ,iDAAiD,CAAC;AACrE,iBAAa;AACb;AAAA,EACD;AACA,QAAM,WAAW,aAAa;AAE9B,MAAI,IAAI,SAAS,OAAO;AAEvB,cAAU,YAAY,EAAE;AAAA,MACvB,WAAS,gBAAgB,UAAU,cAAc,KAAK,UAAQ,IAAI,UAAU,UAAU,OAAO,IAAI,GAAG,EAAE,cAAc,CAAC;AAAA,MACrH,eAAe,cAAc,GAAG;AAAA,IACjC;AACA;AAAA,EACD;AACA,MAAI,CAAC,IAAI,UAAU;AAClB,aAAS,EAAE,QAAQ,mCAAmC,CAAC;AACvD,iBAAa;AACb;AAAA,EACD;AACA,kBAAgB,UAAU,cAAc,KAAK,UAAQ,IAAI,SAAS,IAAI,UAAW,UAAU,IAAI,GAAG,EAAE,cAAc,CAAC;AACpH;AAQO,SAAS,mBAEf,EAAE,SAAS,MAAM,SAAS,IAA+D,CAAC,GACzF;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,QAAM,EAAE,WAAW,WAAW,IAAI;AAClC,MAAI,iBAAiB,sBAAsB,GAAG,EAAG;AACjD,QAAM,aAAa,aAAa,kBAAkB;AAClD,QAAM,WAAW,YAAY;AAC7B,MAAI,CAAC,UAAU;AACd,gBAAY,EAAE,UAAU,CAAC,GAAG,QAAQ,wBAAwB,CAAC;AAC7D,iBAAa;AACb;AAAA,EACD;AACA,MAAI,QAAQ,UAAU,CAAC,KAAK,UAAU;AACrC,QAAI,KAAK;AAER,kBAAY,EAAE,UAAU,CAAC,GAAG,QAAQ,wBAAwB,CAAC;AAC7D,mBAAa;AACb;AAAA,IACD;AACA,QAAI,UAAU,MAAM;AACpB,QAAI,YAAY,GAAG;AAClB,kBAAY,EAAE,UAAU,CAAC,GAAG,QAAQ,wBAAwB,CAAC;AAC7D,mBAAa;AACb;AAAA,IACD;AACA,UAAM,WAA0E,CAAC;AACjF,eAAW,QAAQ,OAAO;AACzB,YAAM,OAAO,MAAM,KAAK,UAAU,IAAI;AACtC,UAAI,KAAK,MAAM,CAAC,SAAS,MAAM;AAC9B,YAAI,CAAC,SAAS;AACb,mBAAS,KAAK;AAAA,YACb,UAAU,mBAAmB,IAAI;AAAA,YACjC,MAAM,EAAE;AAAA,YACR,YAAY,EAAE;AAAA,UACf,CAAC;AAAA,QACF;AACA,YAAI,EAAE,YAAY,GAAG;AACpB,sBAAY,EAAE,UAAU,QAAQ,wBAAwB,CAAC;AACzD,uBAAa;AAAA,QACd;AAAA,MACD,CAAC;AAAA,IACF;AAAA,EACD,CAAC;AACF;AAEO,SAAS,kBAEf,EAAE,UAAU,SAAS,MAAM,SAAS,GACnC;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,QAAM,EAAE,QAAQ,WAAW,IAAI;AAC/B,MAAI,iBAAiB,qBAAqB,GAAG,EAAG;AAChD,QAAM,IAAI,cAAc,UAAU,qBAAqB,GAAG;AAC1D,MAAI,CAAC,EAAG;AAGR,MAAI,EAAE,SAAS,WAAW,CAAC,EAAE,UAAU;AACtC,aAAS,EAAE,QAAQ,6DAA6D,CAAC;AACjF,iBAAa;AACb;AAAA,EACD;AACA,MAAI,OAAO,EAAE,UAAU,aAAa,qBAAqB,GAAG,CAAC;AAC9D;AAEO,SAAS,WAEf,EAAE,UAAU,SAAS,GAAG,SAAS,MAAM,SAAS,GAO/C;AACD,QAAM,MAAM,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAC3D,MAAI,iBAAiB,cAAc,GAAG,EAAG;AACzC,QAAM,IAAI,cAAc,UAAU,cAAc,GAAG;AACnD,MAAI,CAAC,EAAG;AACR,MAAI,CAAC,eAAe,GAAG,cAAc,GAAG,EAAG;AAC3C,MAAI,SAAS,EAAE,UAAU,QAAQ,aAAa,cAAc,GAAG,CAAC;AACjE;AAEO,IAAM,UAAU,gBAAgB,SAAS;;;AClsBzC,SAAS,aAEf,EAAE,KAAK,SAAS,CAAC,GAAG,UAAU,SAAS,MAAM,SAAS,GAQrD;AACD,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AAEzF,QAAM,KAAK,EAAE,SAAS,OAAO,CAAC,EAC5B,KAAK,OAAO,aAAa;AACzB,QAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,SAAS,UAAU;AACrD,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAM,eAAe,mBAAmB,IAAI;AAC5C,gBAAY;AAAA,MACX;AAAA,MACA,UAAU,YAAY;AAAA,MACtB,YAAY,SAAS;AAAA,MACrB,QAAQ;AAAA,IACT,CAAC;AAAA,EACF,CAAC,EACA,MAAM,CAAC,UAAiB;AACxB,aAAS,EAAE,QAAQ,qBAAqB,MAAM,OAAO,GAAG,CAAC;AAAA,EAC1D,CAAC,EACA,QAAQ,MAAM;AACd,iBAAa;AAAA,EACd,CAAC;AACH;AAiBA,IAAM,iBAAiB,oBAAI,IAA4B;AACvD,IAAM,wBAAwB,oBAAI,IAAY;AAC9C,IAAM,2BAA2B,oBAAI,IAAY;AACjD,IAAI,eAAe;AAEnB,SAAS,iBAAyB;AACjC,SAAO,UAAU,KAAK,IAAI,CAAC,IAAI,cAAc;AAC9C;AAEA,SAAS,qBAAqB,KAAqC;AAClE,QAAM,UAAkC,CAAC;AACzC,aAAW,QAAQ,IAAI,KAAK,EAAE,MAAM,SAAS,GAAG;AAC/C,QAAI,CAAC,KAAM;AACX,UAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,QAAI,SAAS,EAAG;AAChB,UAAM,MAAM,KAAK,MAAM,GAAG,KAAK,EAAE,KAAK;AACtC,UAAM,QAAQ,KAAK,MAAM,QAAQ,CAAC,EAAE,KAAK;AACzC,QAAI,IAAK,SAAQ,GAAG,IAAI;AAAA,EACzB;AACA,SAAO;AACR;AAEA,SAAS,eAAe,MAAgB,UAAyC;AAChF,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACpD,QAAI,SAAS,KAAM;AACnB,QAAI,iBAAiB,MAAM;AAC1B,WAAK,OAAO,KAAK,KAAK;AAAA,IACvB,WAAW,OAAO,UAAU,UAAU;AAIrC,WAAK,OAAO,KAAK,KAAK,UAAU,KAAK,CAAC;AAAA,IACvC,OAAO;AACN,WAAK,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,IAC/B;AAAA,EACD;AACD;AAEO,SAAS,WAEf;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,CAAC;AAAA,EACV,WAAW,CAAC;AAAA,EACZ;AAAA,EACA,WAAW,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GACC;AACD,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,MAAM,SAAS,CAAC;AACzF,QAAM,aAAa,KAAK,uBAAuB,QAAQ;AACvD,QAAM,oBAAoB,KAAK,uBAAuB,eAAe;AAErE,QAAM,UAAU,MAAM;AACrB,mBAAe,OAAO,QAAQ;AAC9B,0BAAsB,OAAO,QAAQ;AACrC,6BAAyB,OAAO,QAAQ;AAAA,EACzC;AACA,QAAM,aAAa,CAAC,YAAoB;AACvC,YAAQ;AACR,UAAM,SAAS,EAAE,QAAQ,mBAAmB,OAAO,GAAG;AACtD,aAAS,MAAM;AACf,iBAAa,MAAM;AAAA,EACpB;AAEA,wBAAsB,IAAI,QAAQ;AAClC,OAAK,oBAAoB,QAAQ,EAC/B,KAAK,CAAC,SAAS;AACf,0BAAsB,OAAO,QAAQ;AACrC,QAAI,yBAAyB,IAAI,QAAQ,GAAG;AAC3C,iBAAW,OAAO;AAClB;AAAA,IACD;AAEA,UAAM,MAAM,IAAI,eAAe;AAC/B,QAAI,UAAU;AACd,QAAI,mBAAmB;AACvB,mBAAe,IAAI,UAAU,GAAG;AAEhC,UAAM,gBAAgB,MAAM;AAC3B,UAAI,QAAS;AACb,gBAAU;AACV,YAAM,UAAU,qBAAqB,IAAI,sBAAsB,CAAC;AAChE,cAAQ;AACR,YAAM,SAAS;AAAA,QACd,MAAM,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW,IAAI;AAAA,QAC5D,YAAY,IAAI;AAAA,QAChB,QAAQ;AAAA,QACR,QAAQ;AAAA,MACT;AACA,kBAAY,MAAM;AAClB,mBAAa,MAAM;AAAA,IACpB;AACA,UAAM,gBAAgB,CAAC,YAAoB;AAC1C,UAAI,QAAS;AACb,gBAAU;AACV,iBAAW,OAAO;AAAA,IACnB;AACA,UAAM,cAAc,MAAM;AACzB,UAAI,iBAAkB;AACtB,yBAAmB;AACnB,0BAAoB,EAAE,QAAQ,qBAAqB,IAAI,sBAAsB,CAAC,EAAE,CAAC;AAAA,IAClF;AAEA,QAAI,KAAK,QAAQ,KAAK,IAAI;AAC1B,QAAI,YAAY,QAAW;AAC1B,UAAI,UAAU;AAAA,IACf,WAAW,OAAO,OAAO,IAAI,GAAG;AAC/B,UAAI,UAAU,OAAO,OAAO;AAAA,IAC7B;AACA,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAClD,UAAI,4BAA4B,KAAK,GAAG,EAAG;AAC3C,UAAI,iBAAiB,KAAK,OAAO,KAAK,CAAC;AAAA,IACxC;AACA,QAAI,OAAO,aAAa,CAAC,UAAU;AAClC,YAAM,2BAA2B,MAAM,mBAAmB,MAAM,QAAQ,KAAK;AAC7E,YAAM,gBAAgB,2BAA2B,IAC9C,KAAK,MAAO,MAAM,SAAS,2BAA4B,GAAG,IAC1D;AACH,mBAAa;AAAA,QACZ,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,aAAa,CAAC;AAAA,QAClD,gBAAgB,MAAM;AAAA,QACtB;AAAA,MACD,CAAC;AAAA,IACF;AACA,QAAI,qBAAqB,MAAM;AAC9B,UAAI,IAAI,cAAc,eAAe,iBAAkB,aAAY;AAAA,IACpE;AACA,QAAI,SAAS,MAAM;AAClB,kBAAY;AACZ,oBAAc;AAAA,IACf;AACA,QAAI,UAAU,MAAM,cAAc,eAAe;AACjD,QAAI,YAAY,MAAM,cAAc,SAAS;AAC7C,QAAI,UAAU,MAAM,cAAc,OAAO;AAEzC,UAAM,OAAO,IAAI,SAAS;AAC1B,mBAAe,MAAM,QAAQ;AAC7B,SAAK,OAAO,MAAM,MAAM,gBAAgB,UAAU,MAAM,QAAQ,MAAM,CAAC;AACvE,QAAI,KAAK,IAAI;AAAA,EACd,CAAC,EACA,MAAM,CAAC,UAAiB;AACxB,eAAW,MAAM,OAAO;AAAA,EACzB,CAAC;AACH;AAEO,SAAS,gBAAsC,EAAE,SAAS,IAA2B,CAAC,GAAG;AAC/F,MAAI,CAAC,SAAU;AACf,QAAM,MAAM,eAAe,IAAI,QAAQ;AACvC,MAAI,KAAK;AACR,QAAI,MAAM;AACV;AAAA,EACD;AACA,MAAI,sBAAsB,IAAI,QAAQ,GAAG;AACxC,6BAAyB,IAAI,QAAQ;AAAA,EACtC;AACD;;;ACpHA,IAAM,eAAN,MAAmB;AAAA,EACT,QAAwB,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,EAC3C,YAAY,oBAAI,IAAc;AAAA,EAE/C,WAA2B;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAAU,UAAgC;AACxC,SAAK,UAAU,IAAI,QAAQ;AAC3B,WAAO,MAAM;AACX,WAAK,UAAU,OAAO,QAAQ;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,UAAU,OAAyB;AACjC,SAAK,IAAI,EAAE,GAAG,KAAK,OAAO,MAAM,CAAC;AAAA,EACnC;AAAA,EAEA,YAAkB;AAChB,SAAK,IAAI,EAAE,GAAG,KAAK,OAAO,OAAO,KAAK,CAAC;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,OAAyB;AACpC,QAAI,KAAK,MAAM,UAAU,MAAO,MAAK,UAAU;AAAA,EACjD;AAAA,EAEA,WAAW,QAA2B;AACpC,SAAK,IAAI,EAAE,GAAG,KAAK,OAAO,OAAO,CAAC;AAAA,EACpC;AAAA,EAEA,aAAmB;AACjB,SAAK,IAAI,EAAE,GAAG,KAAK,OAAO,QAAQ,KAAK,CAAC;AAAA,EAC1C;AAAA,EAEQ,IAAI,MAA4B;AACtC,SAAK,QAAQ;AACb,eAAW,YAAY,KAAK,UAAW,UAAS,IAAI;AAAA,EACtD;AACF;AAEO,IAAM,eAAe,IAAI,aAAa;;;AC3HtC,SAAS,UAAgC,OAAkB,CAAC,GAAG;AACpE,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,IAAI;AAC1D,eAAa,UAAU;AAAA,IACrB,OAAO,KAAK,SAAS;AAAA,IACrB,MAAM,KAAK,QAAQ;AAAA,IACnB,OAAO,KAAK;AAAA,IACZ,UAAU,KAAK,YAAY;AAAA,IAC3B,MAAM,KAAK,QAAQ;AAAA,EACrB,CAAC;AACD,cAAY,EAAE,QAAQ,eAAe,CAAC;AACtC,eAAa;AACf;AAEO,SAAS,UAAgC,OAAkD,CAAC,GAAG;AACpG,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,IAAI;AAC1D,eAAa,UAAU;AACvB,cAAY,EAAE,QAAQ,eAAe,CAAC;AACtC,eAAa;AACf;AAEO,SAAS,YAEd,OAAkF,CAAC,GACnF;AACA,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,IAAI;AAC1D,eAAa,UAAU;AAAA,IACrB,OAAO,KAAK,SAAS;AAAA,IACrB,MAAM;AAAA,IACN,UAAU;AAAA,IACV,MAAM,KAAK,QAAQ;AAAA,EACrB,CAAC;AACD,cAAY,EAAE,QAAQ,iBAAiB,CAAC;AACxC,eAAa;AACf;AAEO,SAAS,YAAkC,OAAkD,CAAC,GAAG;AACtG,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,IAAI;AAC1D,eAAa,UAAU;AACvB,cAAY,EAAE,QAAQ,iBAAiB,CAAC;AACxC,eAAa;AACf;AAiBO,SAAS,UAAgC,OAAkB,CAAC,GAAG;AACpE,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,IAAI;AAC1D,QAAM,WAAW,KAAK,YAAY;AAGlC,MAAI,UAAU;AACd,eAAa,WAAW;AAAA,IACtB,MAAM;AAAA,IACN,OAAO,KAAK,SAAS;AAAA,IACrB,SAAS,KAAK,WAAW;AAAA,IACzB,YAAY,KAAK,cAAc;AAAA,IAC/B,YAAY,KAAK,cAAc;AAAA,IAC/B,aAAa,KAAK,eAAe;AAAA,IACjC,aAAa,KAAK,eAAe;AAAA,IACjC,cAAc,KAAK,gBAAgB;AAAA,IACnC;AAAA,IACA,iBAAiB,KAAK,mBAAmB;AAAA,IACzC,UAAU,CAAC,WAAW,YAAY;AAChC,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,WAAW;AACxB,YAAM,SAAkC;AAAA,QACtC,SAAS;AAAA,QACT,QAAQ,CAAC;AAAA,QACT,QAAQ;AAAA,MACV;AACA,UAAI,SAAU,QAAO,UAAU,WAAW;AAC1C,kBAAY,MAAM;AAClB,mBAAa;AAAA,IACf;AAAA,EACF,CAAC;AACH;AAUO,SAAS,gBAAsC,OAAwB,CAAC,GAAG;AAChF,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,IAAI;AAClE,MAAI,UAAU;AACd,eAAa,WAAW;AAAA,IACtB,MAAM;AAAA,IACN,UAAU,KAAK,YAAY,CAAC;AAAA,IAC5B,WAAW,KAAK,aAAa;AAAA,IAC7B,UAAU,CAAC,UAAU;AACnB,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,WAAW;AACxB,UAAI,UAAU,IAAI;AAChB,iBAAS,EAAE,QAAQ,8BAA8B,CAAC;AAAA,MACpD,OAAO;AACL,oBAAY,EAAE,UAAU,OAAO,QAAQ,qBAAqB,CAAC;AAAA,MAC/D;AACA,mBAAa;AAAA,IACf;AAAA,EACF,CAAC;AACH;AAcO,SAAS,MAA4B,OAAkB,CAAC,GAAG;AAChE,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,IAAI;AAClE,MAAI,UAAU;AACd,QAAM,YAAa,KAAK,SAAS,UAAU,UAAU;AACrD,eAAa,WAAW;AAAA,IACtB,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO,KAAK,SAAS;AAAA,IACrB,MAAM,KAAK,QAAQ;AAAA,IACnB,KAAK,KAAK,OAAO;AAAA,IACjB,OAAO,KAAK,SAAS;AAAA,IACrB,OAAO,KAAK,SAAS;AAAA,IACrB,UAAU,CAAC,UAAU;AACnB,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,WAAW;AACxB,UAAI,UAAU,IAAI;AAChB,iBAAS,EAAE,QAAQ,oBAAoB,CAAC;AAAA,MAC1C,OAAO;AACL,oBAAY,EAAE,QAAQ,WAAW,CAAC;AAAA,MACpC;AACA,mBAAa;AAAA,IACf;AAAA,EACF,CAAC;AACH;AAgBO,SAAS,SAA+B,OAAqB,CAAC,GAAG;AACtE,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,IAAI;AAClE,MAAI,UAAU;AACd,eAAa,WAAW;AAAA,IACtB,MAAM;AAAA,IACN,YAAY,KAAK,cAAc;AAAA,IAC/B,aAAa,KAAK,eAAe;AAAA,IACjC,UAAU,CAAC,cAAc;AACvB,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,WAAW;AACxB,UAAI,WAAW;AACb,oBAAY,EAAE,UAAU,KAAK,YAAY,IAAI,QAAQ,cAAc,CAAC;AAAA,MACtE,OAAO;AACL,iBAAS,EAAE,QAAQ,uBAAuB,CAAC;AAAA,MAC7C;AACA,mBAAa;AAAA,IACf;AAAA,EACF,CAAC;AACH;AAWO,SAAS,WAAiC,OAAuB,CAAC,GAAG;AAC1E,QAAM,EAAE,WAAW,QAAQ,WAAW,IAAI,cAAc,MAAM,IAAI;AAClE,MAAI,UAAU;AACd,eAAa,WAAW;AAAA,IACtB,MAAM;AAAA,IACN,YAAY,KAAK,cAAc;AAAA,IAC/B,cAAc,KAAK,gBAAgB;AAAA,IACnC,aAAa,KAAK,eAAe;AAAA,IACjC,UAAU,CAAC,cAAc;AACvB,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,WAAW;AACxB,UAAI,WAAW;AACb,oBAAY,EAAE,QAAQ,gBAAgB,CAAC;AAAA,MACzC,OAAO;AACL,iBAAS,EAAE,QAAQ,yBAAyB,CAAC;AAAA,MAC/C;AACA,mBAAa;AAAA,IACf;AAAA,EACF,CAAC;AACH;;;AC1JO,SAAS,QAA8B,EAAE,SAAS,SAAS,GAA8C;AAC/G,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,SAAS,CAAC;AAE3E,cAAY,IAAI;AAChB,eAAa;AAEb,SAAO;AACR;AAEO,SAAS,cAAoC,EAAE,SAAS,SAAS,IAA+C,CAAC,GAAG;AAC1H,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,SAAS,CAAC;AAE3E,QAAM,EAAE,IAAI,IAAI,KAAK,YAAY,aAAa,cAAc,aAAa,aAAa,IAAI,kBAAkB,IAAI;AAChH,QAAM,MAAM,KAAK,QAAQ,mBAAmB,KAAK,EAAE,QAAQ,KAAK,mBAAmB,EAAE;AACrF,QAAM,kBAAmB,GAAG,iBAAiB,KAA4B,IAAI;AAE7E,QAAM,OAAO;AAAA,IACZ;AAAA,IAAY;AAAA,IAAa;AAAA,IAAc;AAAA,IAAa;AAAA,IACpD;AAAA,IACA,UAAU;AAAA,MACT,OAAO,GAAG;AAAA,MACV,QAAQ,GAAG,SAAS;AAAA,MACpB,KAAK;AAAA,MACL,QAAQ,GAAG;AAAA,MACX,MAAM;AAAA,MACN,OAAO,GAAG;AAAA,IACX;AAAA,EACD;AACA,cAAY,IAAI;AAChB,eAAa;AACb,SAAO;AACR;AAEO,SAAS,iBAAuC,EAAE,SAAS,SAAS,IAA+C,CAAC,GAAG;AAC7H,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,SAAS,CAAC;AAE3E,QAAM,OAAO;AAAA,IACZ,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,aAAa;AAAA,IACb,mBAAmB;AAAA,EACpB;AACA,cAAY,IAAI;AAChB,eAAa;AACb,SAAO;AACR;AAIA,SAAS,kBAAkB,SAAyB;AAInD,QAAM,MAAM,QAAQ,mBAAmB;AACvC,QAAM,KAAK,QAAQ,QAAQ,IAAI,cAAc,8BAA8B,GAAG,sBAAsB,KAChG,EAAE,OAAO,KAAK,eAAe,KAAK,QAAQ,KAAK,gBAAgB,IAAI;AACvE,QAAM,KAAM,OAA+D,gBAAgB,CAAC;AAC5F,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAa,GAAG,YAAY,KAA4B,KAAK,cAAc,OAAO,oBAAoB;AAAA,IACtG,aAAc,GAAG,aAAa,KAA4B,GAAG;AAAA,IAC7D,cAAe,GAAG,cAAc,KAA4B,GAAG;AAAA,IAC/D,aAAa,GAAG;AAAA,IAChB,cAAc,GAAG;AAAA,EAClB;AACD;AAEA,SAAS,gBAAgB,SAAyB;AACjD,QAAM,EAAE,IAAI,IAAI,KAAK,YAAY,aAAa,cAAc,aAAa,aAAa,IAAI,kBAAkB,OAAO;AACnH,QAAM,kBAAmB,GAAG,iBAAiB,KAA4B,KAAK,mBAAmB;AAGjG,QAAM,cAAe,GAAG,gBAAgB,GAAuC,UAC3E,KAAK,gBAAgB,UAAU;AAEnC,SAAO;AAAA,IACN,OAAO,GAAG,OAAO,KAAK;AAAA,IACtB,OAAO,GAAG,OAAO,KAAK;AAAA,IACtB;AAAA,IAAY;AAAA,IAAa;AAAA,IAAc;AAAA,IAAa;AAAA,IACpD;AAAA,IACA,UAAU;AAAA,IACV,SAAS;AAAA,IACT,QAAQ,GAAG,QAAQ,KAAK;AAAA,IACxB,UAAU,GAAG,UAAU,KAAK;AAAA,IAC5B,iBAAiB;AAAA,IACjB,YAAY;AAAA,IACZ,mBAAmB;AAAA,IACnB,UAAU;AAAA,MACT,OAAO,GAAG;AAAA,MACV,QAAQ,GAAG,SAAS,kBAAkB;AAAA,MACtC,KAAK;AAAA,MACL,QAAQ,GAAG,SAAS;AAAA,MACpB,MAAM;AAAA,MACN,OAAO,GAAG;AAAA,IACX;AAAA,EACD;AACD;AAEO,SAAS,mBAAyC,MAAiD;AACzG,QAAM,EAAE,SAAS,SAAS,IAAI;AAC9B,QAAM,EAAE,WAAW,WAAW,IAAI,cAAc,MAAM,EAAE,SAAS,SAAS,CAAC;AAC3E,cAAY,gBAAgB,IAAI,CAAC;AACjC,eAAa;AACd;AAEO,SAAS,cAAoC,MAAiD;AACpG,qBAAmB,KAAK,MAAM,IAAI;AACnC;AAEO,SAAS,oBAAwC;AACvD,SAAO,gBAAgB,IAAI;AAC5B;AAIO,SAAS,qBAAyC;AACxD,SAAO;AAAA,IACN,aAAa;AAAA,MACZ,OAAO,KAAK,SAAS;AAAA,MACrB,YAAY;AAAA,MACZ,SAAS;AAAA,IACV;AAAA,EACD;AACD;AAaO,IAAM,gBAAgF;AAAA;AAAA,EAE5F;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;;;AdtSA,IAAM,MAAM;AAAA,EACV,KAAK;AAAA,IACH,QAAQ,CAAC,YAAoB,SAAoB,4BAAY,OAAO,SAAS,GAAG,IAAI;AAAA;AAAA;AAAA;AAAA,IAIpF,UAAU,CAAC,YAAoB,SAAoB,4BAAY,SAAS,SAAS,GAAG,IAAI;AAAA,IACxF,MAAM,CAAC,YAAoB,SAAoB;AAC7C,kCAAY,KAAK,SAAS,GAAG,IAAI;AAAA,IACnC;AAAA,IACA,IAAI,CACF,SACA,aACG;AACH,kCAAY,GAAG,SAAS,QAAQ;AAChC,aAAO,MAAM,4BAAY,eAAe,SAAS,QAAQ;AAAA,IAC3D;AAAA,IACA,MAAM,CACJ,SACA,aACG;AACH,kCAAY,KAAK,SAAS,QAAQ;AAAA,IACpC;AAAA,IACA,gBAAgB,CACd,SACA,aACG;AACH,kCAAY,eAAe,SAAS,QAAQ;AAAA,IAC9C;AAAA,EACF;AACF;AAMA,8BAAc,kBAAkB,YAAY,GAAG;AAQ/C,4BAAY,GAAG,iBAAE,UAAU,CAAC,QAAQ,YAA4B;AAC9D,OAAK;AAAA,IACH;AAAA,IACA,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV,EAAE,KAAK,CAAC,YAAY;AAClB,UAAM,MAA0B;AAAA,MAC9B,cAAc,QAAQ;AAAA,MACtB,WAAW,QAAQ;AAAA,MACnB,IAAI,QAAQ;AAAA,MACZ,QAAQ,QAAQ;AAAA,MAChB,QAAQ,QAAQ;AAAA,IAClB;AACA,gCAAY,KAAK,gBAAE,cAAc,GAAG;AAAA,EACtC,CAAC;AACH,CAAC;AAMD,IAAI,QAAQ,IAAI,aAAa,QAAQ;AACnC,gCAAc,kBAAkB,aAAa;AAAA,IAC3C,MAAM,CAAC,YAAoB,SAAoB;AAC7C;AAAC,MAAC,4BAA0E,KAAK,SAAS,GAAG,IAAI;AAAA,IACnG;AAAA,EACF,CAAC;AACH;",
|
|
6
6
|
"names": ["path", "path", "tempFiles", "metadata", "path", "os", "path"]
|
|
7
7
|
}
|