@weapp-vite/miniprogram-automator 1.2.21 → 1.2.23

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.
@@ -12636,6 +12636,48 @@ function getParserClass(pluginsMap) {
12636
12636
  return cls;
12637
12637
  }
12638
12638
  //#endregion
12639
+ //#region ../../mpcore/packages/simulator/src/view/templateInterpolation.ts
12640
+ /** 按表达式词法寻找插值边界,字符串和对象内部的大括号不结束绑定。 */
12641
+ function templateInterpolations(source) {
12642
+ const ranges = [];
12643
+ let offset = 0;
12644
+ while (offset < source.length) {
12645
+ const start = source.indexOf("{{", offset);
12646
+ if (start < 0) break;
12647
+ let quote;
12648
+ let depth = 0;
12649
+ let closed = false;
12650
+ for (let i = start + 2; i < source.length; i++) {
12651
+ const char = source[i];
12652
+ if (quote) {
12653
+ if (char === "\\") i++;
12654
+ else if (char === quote) quote = void 0;
12655
+ } else if (char === "'" || char === "\"") quote = char;
12656
+ else if (char === "{") depth++;
12657
+ else if (char === "}") {
12658
+ if (depth === 0 && source[i + 1] === "}") {
12659
+ ranges.push({
12660
+ start,
12661
+ end: i + 2,
12662
+ expression: source.slice(start + 2, i)
12663
+ });
12664
+ offset = i + 2;
12665
+ closed = true;
12666
+ break;
12667
+ }
12668
+ depth--;
12669
+ }
12670
+ }
12671
+ if (!closed) break;
12672
+ }
12673
+ return ranges;
12674
+ }
12675
+ function isTemplateExpression(value) {
12676
+ const trimmed = value.trim();
12677
+ const ranges = templateInterpolations(trimmed);
12678
+ return ranges.length === 1 && ranges[0].start === 0 && ranges[0].end === trimmed.length;
12679
+ }
12680
+ //#endregion
12639
12681
  //#region ../../mpcore/packages/simulator/src/view/wxs.ts
12640
12682
  const wxsFunctions = /* @__PURE__ */ new WeakSet();
12641
12683
  function registerWxsExports(value, visited = /* @__PURE__ */ new Set()) {
@@ -12689,13 +12731,9 @@ const BLOCKED_MEMBER_KEYS = /* @__PURE__ */ new Set([
12689
12731
  "prototype"
12690
12732
  ]);
12691
12733
  const NUMERIC_DOT_PATH_RE = /\.(\d+)(?=[.[\s,}\]]|$)/g;
12692
- function isMustacheOnly$2(value) {
12693
- const trimmed = value.trim();
12694
- return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
12695
- }
12696
12734
  function unwrapMustacheExpression(expression) {
12697
12735
  const normalized = expression.trim();
12698
- if (isMustacheOnly$2(normalized)) return normalized.slice(2, -2).trim();
12736
+ if (isTemplateExpression(normalized)) return normalized.slice(2, -2).trim();
12699
12737
  return normalized;
12700
12738
  }
12701
12739
  function parseTemplateExpression(expression) {
@@ -12925,7 +12963,7 @@ const loadingPages = /* @__PURE__ */ new WeakSet();
12925
12963
  function isPageBeforeReady(page) {
12926
12964
  return loadingPages.has(page);
12927
12965
  }
12928
- function runInitialPageLifecycles(page, query, scheduler, isAlive, renderComponents) {
12966
+ function runInitialPageLifecycles(page, query, scheduler, isAlive, renderComponents, onReady) {
12929
12967
  loadingPages.add(page);
12930
12968
  try {
12931
12969
  renderComponents?.();
@@ -12940,7 +12978,10 @@ function runInitialPageLifecycles(page, query, scheduler, isAlive, renderCompone
12940
12978
  scheduler.setTimeout(() => {
12941
12979
  if (!isAlive()) return;
12942
12980
  page.onReady?.();
12943
- if (isAlive()) page.onRouteDone?.({});
12981
+ if (isAlive()) {
12982
+ page.onRouteDone?.({});
12983
+ onReady?.();
12984
+ }
12944
12985
  }, 0);
12945
12986
  }
12946
12987
  //#endregion
@@ -13006,6 +13047,21 @@ function createPluginArtifactSource(fallback, miniprogramRootPath, plugins) {
13006
13047
  };
13007
13048
  }
13008
13049
  //#endregion
13050
+ //#region ../../mpcore/packages/simulator/src/view/nodeDataset.ts
13051
+ const DATASET_NAME_RE = /-([a-z])/g;
13052
+ function isDatasetAttribute(attributeName) {
13053
+ return attributeName.startsWith("data-") && !attributeName.startsWith("data-sim-");
13054
+ }
13055
+ function toDatasetKey(attributeName) {
13056
+ return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
13057
+ }
13058
+ function collectNodeDataset(node) {
13059
+ if (node.dataset) return { ...node.dataset };
13060
+ const dataset = {};
13061
+ for (const [key, value] of Object.entries(node.attribs ?? {})) if (isDatasetAttribute(key)) dataset[toDatasetKey(key)] = value;
13062
+ return dataset;
13063
+ }
13064
+ //#endregion
13009
13065
  //#region ../../mpcore/packages/simulator/src/view/eventBinding.ts
13010
13066
  const LEGACY_EVENT_NAME_RE = /^\w+$/;
13011
13067
  const EVENT_BINDING_PREFIXES = [
@@ -13059,7 +13115,34 @@ function collectMiniProgramEventBindings(attributes = {}) {
13059
13115
  function resolveMiniProgramEventBinding(attributes, eventName) {
13060
13116
  return collectMiniProgramEventBindings(attributes).get(eventName) ?? null;
13061
13117
  }
13118
+ /** 冒泡时保留原始 target,currentTarget 则属于当前执行绑定的节点。 */
13119
+ function createMiniProgramEventPayload(node, eventName, event, origin = node) {
13120
+ const dataset = collectNodeDataset(node);
13121
+ const nodeId = node.attribs?.id ?? "";
13122
+ const isOrigin = node === origin;
13123
+ return {
13124
+ bubbles: false,
13125
+ capturePhase: false,
13126
+ composed: false,
13127
+ currentTarget: {
13128
+ dataset: isOrigin ? event.currentTarget?.dataset ?? event.dataset ?? dataset : dataset,
13129
+ id: isOrigin ? event.currentTarget?.id ?? event.id ?? nodeId : nodeId
13130
+ },
13131
+ detail: event.detail,
13132
+ mark: event.mark,
13133
+ target: {
13134
+ dataset: event.target?.dataset ?? event.dataset ?? (isOrigin ? dataset : collectNodeDataset(origin)),
13135
+ id: event.target?.id ?? event.id ?? origin.attribs?.id ?? ""
13136
+ },
13137
+ timeStamp: Date.now(),
13138
+ type: eventName
13139
+ };
13140
+ }
13062
13141
  const WEVU_NATIVE_INSTANCE_KEY = "__wevuNativeInstance";
13142
+ /** 宿主值为 1 时,实现带 routeEventId 的路由阶段契约;不代表微信 SDK 版本。 */
13143
+ const WEVU_ROUTE_EVENT_CONTRACT_KEY = "__wevuRouteEventContract";
13144
+ /** 宿主负责 pageScrollTo 的实际滚动事件,框架不再合成第二份通知。 */
13145
+ const WEVU_PAGE_SCROLL_EVENT_CONTRACT_KEY = "__wevuPageScrollEventContract";
13063
13146
  //#endregion
13064
13147
  //#region ../../mpcore/packages/simulator/src/view/selectorQueryScope.ts
13065
13148
  const MPCORE_COMPONENT_SCOPE_ID = "__mpcoreComponentScopeId";
@@ -13168,21 +13251,6 @@ function resolveMiniProgramComponent(ownerFilePath, request, miniprogramRootPath
13168
13251
  return npmPath ? relative(miniprogramRootPath, npmPath).slice(0, -3) : localPath;
13169
13252
  }
13170
13253
  //#endregion
13171
- //#region ../../mpcore/packages/simulator/src/view/nodeDataset.ts
13172
- const DATASET_NAME_RE = /-([a-z])/g;
13173
- function isDatasetAttribute(attributeName) {
13174
- return attributeName.startsWith("data-") && !attributeName.startsWith("data-sim-");
13175
- }
13176
- function toDatasetKey(attributeName) {
13177
- return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
13178
- }
13179
- function collectNodeDataset(node) {
13180
- if (node.dataset) return { ...node.dataset };
13181
- const dataset = {};
13182
- for (const [key, value] of Object.entries(node.attribs ?? {})) if (isDatasetAttribute(key)) dataset[toDatasetKey(key)] = value;
13183
- return dataset;
13184
- }
13185
- //#endregion
13186
13254
  //#region ../../mpcore/packages/simulator/src/view/templateImports.ts
13187
13255
  function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
13188
13256
  const documents = /* @__PURE__ */ new Map();
@@ -13234,12 +13302,15 @@ function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
13234
13302
  }
13235
13303
  //#endregion
13236
13304
  //#region ../../mpcore/packages/simulator/src/view/templateText.ts
13237
- const TEMPLATE_INTERPOLATION_RE$2 = /\{\{([^{}]+)\}\}/g;
13238
- function interpolateTemplateText(input, data) {
13239
- return input.replace(TEMPLATE_INTERPOLATION_RE$2, (_match, expression) => {
13240
- const value = resolveTemplateExpression(data, expression);
13241
- return value === void 0 ? "" : String(value);
13242
- });
13305
+ function interpolateTemplateText(input, data, emptyNull = false) {
13306
+ let output = "";
13307
+ let offset = 0;
13308
+ for (const range of templateInterpolations(input)) {
13309
+ const value = resolveTemplateExpression(data, range.expression);
13310
+ output += input.slice(offset, range.start) + (value === void 0 || emptyNull && value === null ? "" : String(value));
13311
+ offset = range.end;
13312
+ }
13313
+ return output + input.slice(offset);
13243
13314
  }
13244
13315
  //#endregion
13245
13316
  //#region ../../node_modules/.pnpm/entities@8.1.0/node_modules/entities/dist/decode-codepoint.js
@@ -17015,20 +17086,76 @@ function parseDocument(data, options) {
17015
17086
  return handler.root;
17016
17087
  }
17017
17088
  //#endregion
17089
+ //#region ../../mpcore/packages/simulator/src/view/templateAttributes.ts
17090
+ function decodeAttribute(raw, quote) {
17091
+ const source = raw.replaceAll(`\\${quote}`, quote);
17092
+ let value = "";
17093
+ let offset = 0;
17094
+ for (const range of templateInterpolations(source)) {
17095
+ value += source.slice(offset, range.start).replace(/\\([\s\S])/g, "$1");
17096
+ value += source.slice(range.start, range.end);
17097
+ offset = range.end;
17098
+ }
17099
+ return value + source.slice(offset).replace(/\\([\s\S])/g, "$1");
17100
+ }
17101
+ /** 先保存 WXML 属性词法值,再交给 HTML parser 构建标签树,避免反斜杠引号提前截断属性。 */
17102
+ function maskTemplateAttributes(source) {
17103
+ const attributes = /* @__PURE__ */ new Map();
17104
+ if (!source.includes("\\")) return {
17105
+ source,
17106
+ attributes
17107
+ };
17108
+ let prefix = "__mpcore_quoted_attribute_";
17109
+ while (source.includes(prefix)) prefix += "_";
17110
+ let output = "";
17111
+ let offset = 0;
17112
+ for (let i = 0; i < source.length; i++) {
17113
+ if (source.startsWith("<!--", i)) {
17114
+ const end = source.indexOf("-->", i + 4);
17115
+ i = end < 0 ? source.length : end + 2;
17116
+ continue;
17117
+ }
17118
+ if (source[i] !== "<" || !/[a-z_]/i.test(source[i + 1] ?? "")) continue;
17119
+ for (i++; i < source.length && source[i] !== ">"; i++) {
17120
+ if (source[i] !== "\"" && source[i] !== "'") continue;
17121
+ const quote = source[i];
17122
+ const start = i + 1;
17123
+ for (i++; i < source.length && source[i] !== quote; i++) if (source[i] === "\\") i++;
17124
+ if (i >= source.length) break;
17125
+ const raw = source.slice(start, i);
17126
+ if (!raw.includes("\\")) continue;
17127
+ const token = `${prefix}${attributes.size}`;
17128
+ attributes.set(token, decodeAttribute(raw, quote));
17129
+ output += source.slice(offset, start) + token;
17130
+ offset = i;
17131
+ }
17132
+ }
17133
+ return {
17134
+ source: output + source.slice(offset),
17135
+ attributes
17136
+ };
17137
+ }
17138
+ //#endregion
17018
17139
  //#region ../../mpcore/packages/simulator/src/view/wxsDocument.ts
17019
17140
  const INLINE_WXS_RE = /<wxs\b([^>]*)(?<!\/)>([\s\S]*?)<\/wxs\s*>/g;
17020
17141
  function parseWxsTemplateDocument(templateSource) {
17021
17142
  const scripts = [];
17022
- const document = parseDocument(`<page>${templateSource.replace(INLINE_WXS_RE, (_match, attributes, script) => {
17143
+ const masked = maskTemplateAttributes(templateSource.replace(INLINE_WXS_RE, (_match, attributes, script) => {
17023
17144
  return `<wxs data-sim-wxs="${scripts.push(script) - 1}"${attributes}>wxs-source</wxs>`;
17024
- })}</page>`, {
17145
+ }));
17146
+ const document = parseDocument(`<page>${masked.source}</page>`, {
17025
17147
  xmlMode: false,
17026
17148
  decodeEntities: false,
17027
17149
  lowerCaseAttributeNames: false,
17028
17150
  lowerCaseTags: false,
17029
17151
  recognizeSelfClosing: true
17030
17152
  });
17153
+ if (!scripts.length && !masked.attributes.size) return document;
17154
+ const hasAttributes = masked.attributes.size > 0;
17031
17155
  const restore = (node) => {
17156
+ if (hasAttributes && node.attribs) {
17157
+ for (const [name, token] of Object.entries(node.attribs)) if (masked.attributes.has(token)) node.attribs[name] = masked.attributes.get(token);
17158
+ }
17032
17159
  if (node.name === "wxs" && Object.hasOwn(node.attribs ?? {}, "data-sim-wxs")) {
17033
17160
  node.children[0].data = scripts[Number(node.attribs["data-sim-wxs"])];
17034
17161
  delete node.attribs["data-sim-wxs"];
@@ -17040,7 +17167,6 @@ function parseWxsTemplateDocument(templateSource) {
17040
17167
  }
17041
17168
  //#endregion
17042
17169
  //#region ../../mpcore/packages/simulator/src/runtime/render/shared.ts
17043
- const TEMPLATE_INTERPOLATION_RE$1 = /\{\{([^{}]+)\}\}/g;
17044
17170
  const LEADING_SLASH_RE$4 = /^\/+/;
17045
17171
  const EVENT_BINDING_ATTRS = [
17046
17172
  "bindtap",
@@ -17060,8 +17186,7 @@ const STRUCTURAL_ATTRS = [
17060
17186
  const CLASS_SPLIT_RE = /\s+/;
17061
17187
  const JS_FILE_RE = /\.js$/;
17062
17188
  function isMustacheOnly$1(value) {
17063
- const trimmed = value.trim();
17064
- return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
17189
+ return isTemplateExpression(value);
17065
17190
  }
17066
17191
  function collectDataset(node, source) {
17067
17192
  if (node.dataset) return collectNodeDataset(node);
@@ -17086,17 +17211,11 @@ function cloneNode(node) {
17086
17211
  children: node.children?.map((child) => cloneNode(child))
17087
17212
  };
17088
17213
  }
17089
- function resolveValueByPath$1(source, expression) {
17090
- return resolveTemplateExpression(source, expression) ?? "";
17091
- }
17092
17214
  function resolveRawValueByPath$1(source, expression) {
17093
17215
  return resolveTemplateExpression(source, expression);
17094
17216
  }
17095
17217
  function interpolateTemplate$1(input, data) {
17096
- return input.replace(TEMPLATE_INTERPOLATION_RE$1, (_match, expression) => {
17097
- const value = resolveValueByPath$1(data, expression);
17098
- return typeof value === "string" ? value : String(value);
17099
- });
17218
+ return interpolateTemplateText(input, data, true);
17100
17219
  }
17101
17220
  function readTemplateSource(artifactSource, filePath) {
17102
17221
  const source = artifactSource.readText(filePath);
@@ -17223,7 +17342,7 @@ function resolveUsingComponents(context, ownerJsonPath, ownerFilePath) {
17223
17342
  return /* @__PURE__ */ new Map();
17224
17343
  }
17225
17344
  }
17226
- function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePath, componentFilePath) {
17345
+ function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePath, componentFilePath, ownerGenerics) {
17227
17346
  const componentJsonPath = path.resolve(context.project.miniprogramRootPath, `${componentFilePath.replace(JS_FILE_RE, "")}.json`);
17228
17347
  const componentGenerics = readComponentConfig(context.artifactSource, componentJsonPath).componentGenerics;
17229
17348
  if (!componentGenerics || typeof componentGenerics !== "object" || Array.isArray(componentGenerics)) return;
@@ -17231,7 +17350,7 @@ function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePat
17231
17350
  const resolved = /* @__PURE__ */ new Map();
17232
17351
  for (const [genericName, definition] of Object.entries(componentGenerics)) {
17233
17352
  const selectedAlias = hostNode.attribs?.[`generic:${genericName}`];
17234
- const selectedPath = selectedAlias ? ownerComponents.get(selectedAlias) : void 0;
17353
+ const selectedPath = selectedAlias ? ownerGenerics?.get(selectedAlias) ?? ownerComponents.get(selectedAlias) : void 0;
17235
17354
  if (selectedPath) {
17236
17355
  resolved.set(genericName, selectedPath);
17237
17356
  continue;
@@ -17293,8 +17412,9 @@ function syncComponentProperties(instance, definition, nextProperties, bindingEx
17293
17412
  const previousSnapshot = instance.__propertySnapshots?.[key];
17294
17413
  if (hasComponentPropertyValueChanged(instance.properties[key], previousSnapshot, nextValue, bindingAffected)) {
17295
17414
  previousProperties[key] = instance.properties[key];
17296
- instance.properties[key] = nextValue;
17297
- if (Object.hasOwn(definition.properties ?? {}, key)) instance.data[key] = nextValue;
17415
+ const deliveredValue = cloneValue$1(nextValue);
17416
+ instance.properties[key] = deliveredValue;
17417
+ if (Object.hasOwn(definition.properties ?? {}, key)) instance.data[key] = deliveredValue;
17298
17418
  changedRootKeys.push(key);
17299
17419
  }
17300
17420
  instance.__propertySnapshots ??= {};
@@ -17492,7 +17612,7 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
17492
17612
  const componentScopeId = `${instancePath}/${clonedNode.name}`;
17493
17613
  const ownerScopeId = scope.getScopeId().includes("/") ? scope.getScopeId() : void 0;
17494
17614
  const { nextProperties, bindingExpressions } = resolveComponentProperties(clonedNode, scope, componentEntry.definition);
17495
- const genericComponents = resolveComponentGenerics(context, clonedNode, ownerJsonPath, ownerFilePath, componentEntry.filePath);
17615
+ const genericComponents = resolveComponentGenerics(context, clonedNode, ownerJsonPath, ownerFilePath, componentEntry.filePath, scope.genericComponents);
17496
17616
  const slots = collectComponentSlots(clonedNode, scope, ownerJsonPath, ownerFilePath, componentScopeId, templateRenderState);
17497
17617
  let componentInstance = context.componentCache.get(componentScopeId);
17498
17618
  if (!componentInstance) componentInstance = createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId, (instance) => {
@@ -17775,7 +17895,13 @@ var StartupNavigationQueue = class {
17775
17895
  }
17776
17896
  }
17777
17897
  run(option, operation) {
17778
- if (!this.collecting) return operation(option);
17898
+ if (!this.collecting) {
17899
+ const submitted = option && { ...option };
17900
+ this.scheduler.queueMicrotask(() => {
17901
+ this.scheduler.setTimeout(() => operation(submitted), 0);
17902
+ });
17903
+ return;
17904
+ }
17779
17905
  const defer = (callback) => callback ? (...args) => {
17780
17906
  this.scheduler.setTimeout(() => callback(...args), 0);
17781
17907
  } : void 0;
@@ -18034,6 +18160,14 @@ function createHeadlessWx(driver, runtimeConsole = console) {
18034
18160
  navigateBack: true,
18035
18161
  navigateTo: true,
18036
18162
  nextTick: true,
18163
+ offBeforeAppRoute: true,
18164
+ offBeforePageUnload: true,
18165
+ offAppRoute: true,
18166
+ offAppRouteDone: true,
18167
+ onBeforeAppRoute: true,
18168
+ onBeforePageUnload: true,
18169
+ onAppRoute: true,
18170
+ onAppRouteDone: true,
18037
18171
  offAppHide: true,
18038
18172
  offAppShow: true,
18039
18173
  offNetworkStatusChange: true,
@@ -18081,6 +18215,8 @@ function createHeadlessWx(driver, runtimeConsole = console) {
18081
18215
  };
18082
18216
  return {
18083
18217
  ...eventBus,
18218
+ [WEVU_ROUTE_EVENT_CONTRACT_KEY]: 1,
18219
+ [WEVU_PAGE_SCROLL_EVENT_CONTRACT_KEY]: 1,
18084
18220
  canIUse: (schema) => typeof schema === "string" && schema.trim() !== "" && resolveCapabilityValue(capabilityTree, schema.trim()) != null,
18085
18221
  canvasToTempFilePath: (option) => invokeWxApi(() => driver.canvasToTempFilePath(option), option),
18086
18222
  chooseImage: (option) => invokeWxApi(() => driver.chooseImage(option ?? {}), option),
@@ -18208,6 +18344,14 @@ function createHeadlessWx(driver, runtimeConsole = console) {
18208
18344
  driver.navigateTo(deferred);
18209
18345
  }, deferred)),
18210
18346
  nextTick: (callback) => driver.nextTick(callback),
18347
+ offBeforeAppRoute: (listener) => driver.offBeforeAppRoute(listener),
18348
+ offBeforePageUnload: (listener) => driver.offBeforePageUnload(listener),
18349
+ offAppRoute: (listener) => driver.offAppRoute(listener),
18350
+ offAppRouteDone: (listener) => driver.offAppRouteDone(listener),
18351
+ onBeforeAppRoute: (listener) => driver.onBeforeAppRoute(listener),
18352
+ onBeforePageUnload: (listener) => driver.onBeforePageUnload(listener),
18353
+ onAppRoute: (listener) => driver.onAppRoute(listener),
18354
+ onAppRouteDone: (listener) => driver.onAppRouteDone(listener),
18211
18355
  offAppHide: (callback) => driver.offAppHide(callback),
18212
18356
  offAppShow: (callback) => driver.offAppShow(callback),
18213
18357
  offNetworkStatusChange: (callback) => driver.offNetworkStatusChange(callback),
@@ -18367,6 +18511,7 @@ var RuntimeScheduler = class {
18367
18511
  intervals = /* @__PURE__ */ new Set();
18368
18512
  timeouts = /* @__PURE__ */ new Set();
18369
18513
  closed = false;
18514
+ canceledHandle;
18370
18515
  onError;
18371
18516
  constructor(onError) {
18372
18517
  this.onError = onError;
@@ -18388,7 +18533,7 @@ var RuntimeScheduler = class {
18388
18533
  this.timeouts.clear();
18389
18534
  }
18390
18535
  queueMicrotask(handler) {
18391
- if (this.closed) throw new Error("Cannot schedule work after the runtime session has closed.");
18536
+ if (this.closed) return;
18392
18537
  queueMicrotask(() => {
18393
18538
  if (this.closed) return;
18394
18539
  try {
@@ -18399,7 +18544,7 @@ var RuntimeScheduler = class {
18399
18544
  });
18400
18545
  }
18401
18546
  setInterval(handler, timeout, ...args) {
18402
- if (this.closed) throw new Error("Cannot schedule work after the runtime session has closed.");
18547
+ if (this.closed) return this.getCanceledHandle();
18403
18548
  const handle = setInterval(() => {
18404
18549
  try {
18405
18550
  handler(...args);
@@ -18411,7 +18556,7 @@ var RuntimeScheduler = class {
18411
18556
  return handle;
18412
18557
  }
18413
18558
  setTimeout(handler, timeout, ...args) {
18414
- if (this.closed) throw new Error("Cannot schedule work after the runtime session has closed.");
18559
+ if (this.closed) return this.getCanceledHandle();
18415
18560
  const handle = setTimeout(() => {
18416
18561
  this.timeouts.delete(handle);
18417
18562
  try {
@@ -18423,6 +18568,14 @@ var RuntimeScheduler = class {
18423
18568
  this.timeouts.add(handle);
18424
18569
  return handle;
18425
18570
  }
18571
+ /** 已销毁 realm 的 Promise 回调不能复活任务,也不能向外泄漏异步拒绝。 */
18572
+ getCanceledHandle() {
18573
+ if (!this.canceledHandle) {
18574
+ this.canceledHandle = setTimeout(() => {}, 0);
18575
+ clearTimeout(this.canceledHandle);
18576
+ }
18577
+ return this.canceledHandle;
18578
+ }
18426
18579
  };
18427
18580
  //#endregion
18428
18581
  //#region ../../mpcore/packages/simulator/src/kernel/runtimeKernel.ts
@@ -37922,27 +38075,6 @@ function queryXPathElements(root, expression) {
37922
38075
  function escapeText$1(text) {
37923
38076
  return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
37924
38077
  }
37925
- function createEventPayload$1(node, eventName, event) {
37926
- const dataset = collectNodeDataset(node);
37927
- const nodeId = node.attribs?.id ?? "";
37928
- return {
37929
- bubbles: false,
37930
- capturePhase: false,
37931
- composed: false,
37932
- currentTarget: {
37933
- dataset: event.currentTarget?.dataset ?? event.dataset ?? dataset,
37934
- id: event.currentTarget?.id ?? event.id ?? nodeId
37935
- },
37936
- detail: event.detail,
37937
- mark: event.mark,
37938
- target: {
37939
- dataset: event.target?.dataset ?? event.dataset ?? dataset,
37940
- id: event.target?.id ?? event.id ?? nodeId
37941
- },
37942
- timeStamp: Date.now(),
37943
- type: eventName
37944
- };
37945
- }
37946
38078
  function createValueEventDetail(value, detail) {
37947
38079
  return {
37948
38080
  value,
@@ -38052,9 +38184,18 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
38052
38184
  if (!this.interactions) throw new Error("Node interactions are not available for this headless testing node.");
38053
38185
  const normalizedEventName = eventName.trim();
38054
38186
  if (!normalizedEventName) throw new Error("Event name must be a non-empty string in headless testing runtime.");
38187
+ let handlerResults;
38188
+ const nativeHandled = this.interactions.dispatchNativeEvent?.(this.node, normalizedEventName, event, normalizedEventName === "tap" ? (result) => {
38189
+ handlerResults ??= [];
38190
+ handlerResults.push(result);
38191
+ } : void 0);
38192
+ if (normalizedEventName === "tap" && nativeHandled) return handlerResults ? (await Promise.all(handlerResults))[0] : void 0;
38055
38193
  const binding = resolveMiniProgramEventBinding(this.node.attribs, normalizedEventName);
38056
- if (!binding?.method) throw new Error(`No ${normalizedEventName} binding was found on <${this.node.name ?? "unknown"}> in headless testing runtime.`);
38057
- return await this.interactions.callMethod(this.node.attribs?.["data-sim-scope"] ?? null, binding.method, createEventPayload$1(this.node, normalizedEventName, event));
38194
+ if (!binding?.method) {
38195
+ if (nativeHandled) return;
38196
+ throw new Error(`No ${normalizedEventName} binding was found on <${this.node.name ?? "unknown"}> in headless testing runtime.`);
38197
+ }
38198
+ return await this.interactions.callMethod(this.node.attribs?.["data-sim-scope"] ?? null, binding.method, createMiniProgramEventPayload(this.node, normalizedEventName, event));
38058
38199
  }
38059
38200
  async tap(event = {}) {
38060
38201
  return await this.trigger("tap", event);
@@ -38161,35 +38302,9 @@ function renderPageTree(project, page) {
38161
38302
  };
38162
38303
  }
38163
38304
  //#endregion
38164
- //#region ../../mpcore/packages/simulator/src/view/selectorQuery.ts
38165
- const LEADING_MARK_PREFIX_RE = /^mark[:\-]?/;
38166
- const MARK_NAME_RE = /[:\-]([a-z])/g;
38305
+ //#region ../../mpcore/packages/simulator/src/view/nodeGeometry.ts
38167
38306
  const NUMERIC_LIKE_VALUE_RE$1 = /-?\d+(?:\.\d+)?/;
38168
- function toMarkKey(attributeName) {
38169
- return attributeName.replace(LEADING_MARK_PREFIX_RE, "").replace(MARK_NAME_RE, (_match, char) => char.toUpperCase());
38170
- }
38171
- function collectMark(node) {
38172
- const mark = {};
38173
- for (const [key, value] of Object.entries(node.attribs ?? {})) {
38174
- if (!(key.startsWith("mark:") || key.startsWith("mark-"))) continue;
38175
- mark[toMarkKey(key)] = value;
38176
- }
38177
- return mark;
38178
- }
38179
- function createScopedRoot(node) {
38180
- return {
38181
- children: [node],
38182
- type: "root"
38183
- };
38184
- }
38185
- function findNodeByScopeId(root, scopeId) {
38186
- if (root.attribs?.["data-sim-scope"] === scopeId) return root;
38187
- for (const child of root.children ?? []) {
38188
- const match = findNodeByScopeId(child, scopeId);
38189
- if (match) return match;
38190
- }
38191
- return null;
38192
- }
38307
+ const PIXEL_LENGTH_RE = /^\d+(?:\.\d+)?px$|^0$/;
38193
38308
  function parseStyleDeclarations$1(styleValue) {
38194
38309
  const declarations = {};
38195
38310
  if (!styleValue) return declarations;
@@ -38224,18 +38339,229 @@ function resolveRect$1(node, windowInfo) {
38224
38339
  width
38225
38340
  };
38226
38341
  }
38342
+ /** 仅支持显式 px 视口和单个 view 内容盒;未知样式布局不作为已测量滚动范围。 */
38343
+ function resolveNativeScrollBounds(node) {
38344
+ const viewport = parseStyleDeclarations$1(node.attribs?.style);
38345
+ let content;
38346
+ const visit = (parent) => {
38347
+ for (const child of parent.children ?? []) {
38348
+ if (child.type === "comment" || child.type === "text" && !child.data?.trim()) continue;
38349
+ if (child.name === "block") {
38350
+ if (!visit(child)) return false;
38351
+ } else {
38352
+ if (content || child.name !== "view") return false;
38353
+ content = child;
38354
+ }
38355
+ }
38356
+ return true;
38357
+ };
38358
+ if (!visit(node)) return {
38359
+ maxTop: 0,
38360
+ maxLeft: 0
38361
+ };
38362
+ const box = parseStyleDeclarations$1(content?.attribs?.style);
38363
+ const supported = content && !viewport.display && !box.display && !box.position && !box.transform;
38364
+ const extent = (dimension) => {
38365
+ const viewportValue = viewport[dimension];
38366
+ const contentValue = box[dimension];
38367
+ if (!supported || !viewportValue || !contentValue || !PIXEL_LENGTH_RE.test(viewportValue) || !PIXEL_LENGTH_RE.test(contentValue)) return 0;
38368
+ const size = parseNumericLikeValue$1(viewportValue);
38369
+ const contentSize = parseNumericLikeValue$1(contentValue);
38370
+ return size > 0 && Number.isFinite(size) && Number.isFinite(contentSize) ? Math.max(0, contentSize - size) : 0;
38371
+ };
38372
+ return {
38373
+ maxTop: extent("height"),
38374
+ maxLeft: extent("width")
38375
+ };
38376
+ }
38377
+ //#endregion
38378
+ //#region ../../mpcore/packages/simulator/src/view/nativeNode.ts
38379
+ const pageScrollViews = /* @__PURE__ */ new WeakMap();
38380
+ function coordinate(value) {
38381
+ const number = Number(value);
38382
+ return Number.isFinite(number) ? Math.max(0, number) : 0;
38383
+ }
38384
+ function nodeKey(node) {
38385
+ return node.attribs?.["data-sim-node"] ?? "";
38386
+ }
38387
+ /** 原生滚动位置独立于绑定值;未改变的绑定不能覆盖用户滚动。 */
38388
+ function syncNativeScrollViews(page, root, emit) {
38389
+ let states = pageScrollViews.get(page);
38390
+ let present;
38391
+ const visit = (node) => {
38392
+ if (node.name === "scroll-view") {
38393
+ const key = nodeKey(node);
38394
+ present ??= /* @__PURE__ */ new Set();
38395
+ present.add(key);
38396
+ if (!states) {
38397
+ states = /* @__PURE__ */ new Map();
38398
+ pageScrollViews.set(page, states);
38399
+ }
38400
+ const boundTop = coordinate(node.attribs?.["scroll-top"]);
38401
+ const boundLeft = coordinate(node.attribs?.["scroll-left"]);
38402
+ const bounds = resolveNativeScrollBounds(node);
38403
+ const scrollY = node.attribs?.["scroll-y"];
38404
+ const scrollX = node.attribs?.["scroll-x"];
38405
+ const maxTop = scrollY !== void 0 && scrollY !== "false" && scrollY !== "0" ? bounds.maxTop : 0;
38406
+ const maxLeft = scrollX !== void 0 && scrollX !== "false" && scrollX !== "0" ? bounds.maxLeft : 0;
38407
+ let state = states.get(key);
38408
+ const previousTop = state?.scrollTop ?? 0;
38409
+ const previousLeft = state?.scrollLeft ?? 0;
38410
+ if (!state) {
38411
+ state = {
38412
+ boundTop,
38413
+ boundLeft,
38414
+ scrollTop: boundTop,
38415
+ scrollLeft: boundLeft,
38416
+ maxTop,
38417
+ maxLeft
38418
+ };
38419
+ states.set(key, state);
38420
+ } else {
38421
+ if (state.boundTop !== boundTop) {
38422
+ state.scrollTop = boundTop;
38423
+ state.boundTop = boundTop;
38424
+ }
38425
+ if (state.boundLeft !== boundLeft) {
38426
+ state.scrollLeft = boundLeft;
38427
+ state.boundLeft = boundLeft;
38428
+ }
38429
+ }
38430
+ state.maxTop = maxTop;
38431
+ state.maxLeft = maxLeft;
38432
+ state.scrollTop = Math.min(state.scrollTop, maxTop);
38433
+ state.scrollLeft = Math.min(state.scrollLeft, maxLeft);
38434
+ if (state.scrollTop !== previousTop || state.scrollLeft !== previousLeft) {
38435
+ const binding = resolveMiniProgramEventBinding(node.attribs, "scroll");
38436
+ if (binding?.method) {
38437
+ const target = {
38438
+ id: node.attribs?.id ?? "",
38439
+ dataset: collectNodeDataset(node)
38440
+ };
38441
+ emit(node.attribs?.["data-sim-scope"] ?? null, binding.method, {
38442
+ type: "scroll",
38443
+ timeStamp: Date.now(),
38444
+ detail: {
38445
+ scrollTop: state.scrollTop,
38446
+ scrollLeft: state.scrollLeft
38447
+ },
38448
+ target,
38449
+ currentTarget: target
38450
+ });
38451
+ }
38452
+ }
38453
+ }
38454
+ for (const child of node.children ?? []) visit(child);
38455
+ };
38456
+ visit(root);
38457
+ if (states) {
38458
+ for (const key of states.keys()) if (!present?.has(key)) states.delete(key);
38459
+ }
38460
+ }
38461
+ function getNativeScrollOffset(page, node) {
38462
+ const state = pageScrollViews.get(page)?.get(nodeKey(node));
38463
+ return {
38464
+ scrollTop: state?.scrollTop ?? 0,
38465
+ scrollLeft: state?.scrollLeft ?? 0
38466
+ };
38467
+ }
38468
+ /** 原生交互更新宿主状态,导航仍交给统一 wx 路由入口。 */
38469
+ function dispatchNativeNodeEvent(page, node, eventName, event, wx, resolveMethod, onHandlerResult) {
38470
+ if (eventName === "scroll" && node.name === "scroll-view") {
38471
+ const detail = event.detail;
38472
+ const state = pageScrollViews.get(page)?.get(nodeKey(node));
38473
+ if (state) {
38474
+ if (detail?.scrollTop !== void 0) state.scrollTop = Math.min(coordinate(detail.scrollTop), state.maxTop);
38475
+ if (detail?.scrollLeft !== void 0) state.scrollLeft = Math.min(coordinate(detail.scrollLeft), state.maxLeft);
38476
+ }
38477
+ event.detail = Object.assign(event.detail && typeof event.detail === "object" ? event.detail : {}, getNativeScrollOffset(page, node));
38478
+ return true;
38479
+ }
38480
+ if (eventName !== "tap") return false;
38481
+ let current = node;
38482
+ let navigator;
38483
+ let handlers;
38484
+ let handled = false;
38485
+ while (current) {
38486
+ if (!navigator && current.name === "navigator") navigator = current;
38487
+ const binding = resolveMiniProgramEventBinding(current.attribs, "tap");
38488
+ if (binding?.method) {
38489
+ const call = resolveMethod(current.attribs?.["data-sim-scope"] ?? null, binding.method);
38490
+ handlers ??= [];
38491
+ handlers.push({
38492
+ node: current,
38493
+ call
38494
+ });
38495
+ handled = true;
38496
+ }
38497
+ if (binding?.stopAfter) {
38498
+ handled = true;
38499
+ break;
38500
+ }
38501
+ current = current.parent;
38502
+ }
38503
+ if (handlers) for (const handler of handlers) {
38504
+ const result = handler.call(createMiniProgramEventPayload(handler.node, "tap", event, node));
38505
+ onHandlerResult?.(result);
38506
+ }
38507
+ if (!navigator) return handled;
38508
+ const openType = navigator.attribs?.["open-type"] ?? "navigate";
38509
+ const url = navigator.attribs?.url ?? "";
38510
+ switch (openType) {
38511
+ case "navigate":
38512
+ wx.navigateTo({ url });
38513
+ break;
38514
+ case "redirect":
38515
+ wx.redirectTo({ url });
38516
+ break;
38517
+ case "switchTab":
38518
+ wx.switchTab({ url });
38519
+ break;
38520
+ case "reLaunch":
38521
+ wx.reLaunch({ url });
38522
+ break;
38523
+ case "navigateBack":
38524
+ wx.navigateBack({ delta: Number(navigator.attribs?.delta ?? 1) });
38525
+ break;
38526
+ default: return handled;
38527
+ }
38528
+ return true;
38529
+ }
38530
+ //#endregion
38531
+ //#region ../../mpcore/packages/simulator/src/view/selectorQuery.ts
38532
+ const LEADING_MARK_PREFIX_RE = /^mark[:\-]?/;
38533
+ const MARK_NAME_RE = /[:\-]([a-z])/g;
38534
+ function toMarkKey(attributeName) {
38535
+ return attributeName.replace(LEADING_MARK_PREFIX_RE, "").replace(MARK_NAME_RE, (_match, char) => char.toUpperCase());
38536
+ }
38537
+ function collectMark(node) {
38538
+ const mark = {};
38539
+ for (const [key, value] of Object.entries(node.attribs ?? {})) {
38540
+ if (!(key.startsWith("mark:") || key.startsWith("mark-"))) continue;
38541
+ mark[toMarkKey(key)] = value;
38542
+ }
38543
+ return mark;
38544
+ }
38545
+ function createScopedRoot(node) {
38546
+ return {
38547
+ children: [node],
38548
+ type: "root"
38549
+ };
38550
+ }
38551
+ function findNodeByScopeId(root, scopeId) {
38552
+ if (root.attribs?.["data-sim-scope"] === scopeId) return root;
38553
+ for (const child of root.children ?? []) {
38554
+ const match = findNodeByScopeId(child, scopeId);
38555
+ if (match) return match;
38556
+ }
38557
+ return null;
38558
+ }
38227
38559
  function resolveSelectorScrollTop(root, selector) {
38228
38560
  const normalizedSelector = selector?.trim();
38229
38561
  if (!normalizedSelector) return null;
38230
38562
  const match = querySelectorAll(root, normalizedSelector)[0];
38231
38563
  return match ? resolveRect$1(match).top : null;
38232
38564
  }
38233
- function resolveScrollOffset(node) {
38234
- return {
38235
- scrollLeft: parseNumericLikeValue$1(node.attribs?.["data-sim-scroll-left"]),
38236
- scrollTop: parseNumericLikeValue$1(node.attribs?.["data-sim-scroll-top"])
38237
- };
38238
- }
38239
38565
  function resolvePropertyValue(node, propertyName) {
38240
38566
  const normalizedPropertyName = propertyName.trim();
38241
38567
  if (!normalizedPropertyName) return;
@@ -38268,7 +38594,7 @@ function resolveFieldsResult(node, fields, options) {
38268
38594
  result.height = rect.height;
38269
38595
  }
38270
38596
  }
38271
- if (fields.scrollOffset) Object.assign(result, resolveScrollOffset(node));
38597
+ if (fields.scrollOffset) Object.assign(result, getNativeScrollOffset(options.page, node));
38272
38598
  if (Array.isArray(fields.properties) && fields.properties.length > 0) Object.assign(result, pickProperties(node, fields.properties));
38273
38599
  if (Array.isArray(fields.computedStyle) && fields.computedStyle.length > 0) Object.assign(result, pickComputedStyle(node, fields.computedStyle));
38274
38600
  if (fields.context) result.context = options.resolveContext?.(node) ?? { type: "unsupported-context" };
@@ -39359,6 +39685,99 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
39359
39685
  return loader;
39360
39686
  }
39361
39687
  //#endregion
39688
+ //#region ../../mpcore/packages/simulator/src/runtime/packageAsset.ts
39689
+ const assetExtension = /\.(?:png|jpe?g|gif|svg|cer|mp3|aac|m4a|mp4|wav|ogg|silk|wasm|br|cur|ico|skel|crt|cert|gltf|glb|bin|ktx|hdr|exr|xnet|pem)$/i;
39690
+ /** 包内静态资源只读且每次访问宿主当前产物,不缓存为用户文件,也不允许越过包根目录。 */
39691
+ function createPackageAssetReader(read) {
39692
+ return (filePath) => {
39693
+ if (filePath.includes("\\") || filePath.includes(":") || filePath.includes("\0")) return;
39694
+ const relative = posix.normalize(filePath.replace(/^\/+/, ""));
39695
+ if (relative === ".." || relative.startsWith("../") || !assetExtension.test(relative)) return;
39696
+ return read(relative);
39697
+ };
39698
+ }
39699
+ //#endregion
39700
+ //#region ../../mpcore/packages/simulator/src/runtime/routeEvents.ts
39701
+ /** 两种模拟器共用路由身份与监听状态,完成事件由页面就绪和宿主提交驱动。 */
39702
+ var HeadlessRouteEvents = class {
39703
+ requestRender;
39704
+ isCurrentPage;
39705
+ bus = createHeadlessUniEventBus();
39706
+ pages = /* @__PURE__ */ new WeakMap();
39707
+ nextRouteId = 0;
39708
+ nextWebviewId = 0;
39709
+ activeEvent;
39710
+ constructor(requestRender, isCurrentPage) {
39711
+ this.requestRender = requestRender;
39712
+ this.isCurrentPage = isCurrentPage;
39713
+ }
39714
+ on(name, listener) {
39715
+ this.bus.$on(name, listener);
39716
+ }
39717
+ off(name, listener) {
39718
+ this.bus.$off(name, listener);
39719
+ }
39720
+ begin(path, query, openType, renderer, page) {
39721
+ const previous = page ? this.pages.get(page) : void 0;
39722
+ const event = {
39723
+ path,
39724
+ query: { ...query },
39725
+ openType: this.nextRouteId === 0 ? "appLaunch" : openType,
39726
+ renderer: previous?.event.renderer ?? (renderer === "skyline" || renderer === "xr-frame" ? renderer : "webview"),
39727
+ routeEventId: String(++this.nextRouteId),
39728
+ webviewId: previous?.webviewId ?? ++this.nextWebviewId,
39729
+ timeStamp: Date.now()
39730
+ };
39731
+ this.activeEvent = event;
39732
+ this.bus.$emit("beforeAppRoute", event);
39733
+ return event;
39734
+ }
39735
+ attach(page, event) {
39736
+ const state = this.pages.get(page);
39737
+ this.pages.set(page, {
39738
+ webviewId: event.webviewId,
39739
+ ready: state?.ready ?? false,
39740
+ event,
39741
+ committed: false
39742
+ });
39743
+ }
39744
+ commit(page, event) {
39745
+ const state = this.pages.get(page);
39746
+ if (!state || state.event !== event || this.activeEvent !== event || !this.isCurrentPage(page)) return;
39747
+ state.committed = true;
39748
+ this.bus.$emit("appRoute", event);
39749
+ if (state.ready) this.complete(page, state);
39750
+ }
39751
+ ready(page) {
39752
+ const state = this.pages.get(page);
39753
+ if (!state) return;
39754
+ state.ready = true;
39755
+ if (state.committed) this.complete(page, state);
39756
+ }
39757
+ complete(page, state) {
39758
+ this.requestRender(() => {
39759
+ if (this.pages.get(page) !== state || this.activeEvent !== state.event || !this.isCurrentPage(page)) return;
39760
+ this.bus.$emit("appRouteDone", state.event);
39761
+ });
39762
+ }
39763
+ beforeUnload(page, event) {
39764
+ const state = this.pages.get(page);
39765
+ if (event && state) this.bus.$emit("beforePageUnload", {
39766
+ ...event,
39767
+ path: page.route,
39768
+ query: { ...page.options },
39769
+ renderer: state.event.renderer,
39770
+ webviewId: state.webviewId,
39771
+ page
39772
+ });
39773
+ this.pages.delete(page);
39774
+ }
39775
+ close() {
39776
+ this.activeEvent = void 0;
39777
+ this.bus.$off();
39778
+ }
39779
+ };
39780
+ //#endregion
39362
39781
  //#region ../../mpcore/packages/simulator/src/runtime/systemInfo.ts
39363
39782
  function createDefaultLocationResult() {
39364
39783
  return {
@@ -40153,7 +40572,7 @@ function createHeadlessWxState(scheduler = nativeScheduler, options = {}) {
40153
40572
  const readFile = (filePath, encoding) => {
40154
40573
  normalizeEncoding(encoding);
40155
40574
  const normalizedPath = normalizeFsPath(filePath);
40156
- const fileContent = files.get(normalizedPath);
40575
+ const fileContent = files.get(normalizedPath) ?? options.readPackageFile?.(normalizedPath);
40157
40576
  if (fileContent == null) throw new Error(`readFile:fail no such file or directory, open '${normalizedPath}'`);
40158
40577
  return {
40159
40578
  data: fileContent,
@@ -41201,6 +41620,7 @@ var HeadlessSession = class {
41201
41620
  appInstance = null;
41202
41621
  appLifecycle = new HeadlessAppLifecycle();
41203
41622
  moduleLoader;
41623
+ routeEvents = new HeadlessRouteEvents((callback) => this.requestRender(callback), (page) => this.currentPageInstance === page && !this.kernel.isClosed);
41204
41624
  eventListeners = /* @__PURE__ */ new Map();
41205
41625
  registries;
41206
41626
  currentPageInstance = null;
@@ -41228,7 +41648,10 @@ var HeadlessSession = class {
41228
41648
  constructor(options) {
41229
41649
  if (!options.project && !options.projectPath) throw new Error("Headless runtime requires projectPath or project.");
41230
41650
  this.project = options.project ?? loadProject(options.projectPath);
41231
- this.wxState = createHeadlessWxState(this.kernel.scheduler, { strictMocks: options.strictHostMocks });
41651
+ this.wxState = createHeadlessWxState(this.kernel.scheduler, {
41652
+ strictMocks: options.strictHostMocks,
41653
+ readPackageFile: createPackageAssetReader((relative) => this.project.artifactSource.readText(path.join(this.project.miniprogramRootPath, relative)))
41654
+ });
41232
41655
  this.registries = createHostRegistries();
41233
41656
  const rawTabBarList = Array.isArray(this.project.appConfig.tabBar?.list) ? this.project.appConfig.tabBar.list : [];
41234
41657
  this.tabBarRoutes = /* @__PURE__ */ new Set();
@@ -41295,6 +41718,14 @@ var HeadlessSession = class {
41295
41718
  saveImageToPhotosAlbum: (option) => this.wxState.saveImageToPhotosAlbum(option),
41296
41719
  saveVideoToPhotosAlbum: (option) => this.wxState.saveVideoToPhotosAlbum(option),
41297
41720
  nextTick: (callback) => this.kernel.scheduler.queueMicrotask(() => callback?.()),
41721
+ offBeforeAppRoute: (listener) => this.routeEvents.off("beforeAppRoute", listener),
41722
+ offBeforePageUnload: (listener) => this.routeEvents.off("beforePageUnload", listener),
41723
+ offAppRoute: (listener) => this.routeEvents.off("appRoute", listener),
41724
+ offAppRouteDone: (listener) => this.routeEvents.off("appRouteDone", listener),
41725
+ onBeforeAppRoute: (listener) => this.routeEvents.on("beforeAppRoute", listener),
41726
+ onBeforePageUnload: (listener) => this.routeEvents.on("beforePageUnload", listener),
41727
+ onAppRoute: (listener) => this.routeEvents.on("appRoute", listener),
41728
+ onAppRouteDone: (listener) => this.routeEvents.on("appRouteDone", listener),
41298
41729
  offAppHide: (callback) => this.appLifecycle.offAppHide(callback),
41299
41730
  offAppShow: (callback) => this.appLifecycle.offAppShow(callback),
41300
41731
  offNetworkStatusChange: (callback) => this.wxState.offNetworkStatusChange(callback),
@@ -41347,15 +41778,28 @@ var HeadlessSession = class {
41347
41778
  }
41348
41779
  close() {
41349
41780
  if (this.kernel.isClosed) return;
41350
- this.unloadAllPages();
41781
+ let failure;
41782
+ try {
41783
+ this.unloadAllPages(void 0, true);
41784
+ } catch (error) {
41785
+ failure = { error };
41786
+ }
41351
41787
  this.canvasContexts.clear();
41352
41788
  this.renderRequestCallbacks.length = 0;
41353
41789
  this.renderRequestPending = false;
41354
41790
  this.appLifecycle.close();
41791
+ this.routeEvents.close();
41355
41792
  this.wxState.close();
41356
41793
  this.moduleLoader.close();
41357
- this.eventListeners.clear();
41358
41794
  this.kernel.close();
41795
+ try {
41796
+ this.emit("close");
41797
+ } catch (error) {
41798
+ failure ??= { error };
41799
+ } finally {
41800
+ this.eventListeners.clear();
41801
+ }
41802
+ if (failure) throw failure.error;
41359
41803
  }
41360
41804
  getDiagnostics() {
41361
41805
  return this.kernel.diagnostics.getEntries();
@@ -41372,7 +41816,14 @@ var HeadlessSession = class {
41372
41816
  if (listeners?.size === 0) this.eventListeners.delete(eventName);
41373
41817
  }
41374
41818
  emit(eventName, ...args) {
41375
- for (const handler of [...this.eventListeners.get(eventName) ?? []]) handler(...args);
41819
+ let failure;
41820
+ for (const handler of [...this.eventListeners.get(eventName) ?? []]) try {
41821
+ handler(...args);
41822
+ } catch (error) {
41823
+ if (eventName !== "close") throw error;
41824
+ failure ??= { error };
41825
+ }
41826
+ if (failure) throw failure.error;
41376
41827
  }
41377
41828
  get isClosed() {
41378
41829
  return this.kernel.isClosed;
@@ -41454,6 +41905,11 @@ var HeadlessSession = class {
41454
41905
  selectOwnerComponent: (scopeId) => this.selectOwnerComponent(scopeId)
41455
41906
  }
41456
41907
  }, current);
41908
+ syncNativeScrollViews(current, rendered.root, (scopeId, method, event) => {
41909
+ this.kernel.scheduler.queueMicrotask(() => {
41910
+ if (this.currentPageInstance === current) this.callScopeMethod(scopeId, method, event);
41911
+ });
41912
+ });
41457
41913
  const componentScopePrefix = `page:${stripLeadingSlash(current.route)}`;
41458
41914
  for (const [scopeId, instance] of this.componentCache.entries()) if (scopeId.startsWith(componentScopePrefix)) this.selectorQueryScopeSnapshots.set(instance, {
41459
41915
  page: current,
@@ -41462,6 +41918,11 @@ var HeadlessSession = class {
41462
41918
  });
41463
41919
  return rendered;
41464
41920
  }
41921
+ dispatchNativeNodeEvent(node, eventName, event, onHandlerResult) {
41922
+ const current = this.requireCurrentPage("dispatchNativeNodeEvent()");
41923
+ if (eventName === "tap") this.renderCurrentPage();
41924
+ return dispatchNativeNodeEvent(current, node, eventName, event, this.moduleLoader.wx, (scopeId, method) => this.resolveScopeMethod(current, scopeId, method), onHandlerResult);
41925
+ }
41465
41926
  requestRender(callback) {
41466
41927
  this.assertActive();
41467
41928
  if (callback) this.renderRequestCallbacks.push(callback);
@@ -41509,21 +41970,27 @@ var HeadlessSession = class {
41509
41970
  callScopeMethod(scopeId, methodName, event) {
41510
41971
  const current = this.requireCurrentPage(`scope method "${methodName}"`);
41511
41972
  this.renderCurrentPage();
41512
- if (!scopeId || scopeId === `page:${stripLeadingSlash(current.route)}`) {
41973
+ return this.resolveScopeMethod(current, scopeId, methodName)(event);
41974
+ }
41975
+ /** 解析本次交互的实例,后续回调不随重渲染或导航切换所有者。 */
41976
+ resolveScopeMethod(current, scopeId, methodName) {
41977
+ if (!scopeId || scopeId === `page:${stripLeadingSlash(current.route)}`) return (event) => {
41513
41978
  const method = current[methodName];
41514
41979
  if (typeof method !== "function") throw new TypeError(`Method "${methodName}" does not exist on headless page ${current.route}.`);
41515
41980
  return method.call(current, event);
41516
- }
41981
+ };
41517
41982
  const instance = this.componentCache.get(scopeId);
41518
41983
  if (!instance) throw new Error(`Unknown scope "${scopeId}" in headless runtime.`);
41519
- instance.__lastInteractionEvent__ = {
41520
- currentTarget: event.currentTarget,
41521
- mark: event.mark,
41522
- target: event.target
41984
+ return (event) => {
41985
+ instance.__lastInteractionEvent__ = {
41986
+ currentTarget: event.currentTarget,
41987
+ mark: event.mark,
41988
+ target: event.target
41989
+ };
41990
+ const method = instance[methodName];
41991
+ if (typeof method !== "function") throw new TypeError(`Method "${methodName}" does not exist on headless component scope "${scopeId}".`);
41992
+ return method.call(instance, event);
41523
41993
  };
41524
- const method = instance[methodName];
41525
- if (typeof method !== "function") throw new TypeError(`Method "${methodName}" does not exist on headless component scope "${scopeId}".`);
41526
- return method.call(instance, event);
41527
41994
  }
41528
41995
  callScopeMethodDirect(scopeId, methodName, ...args) {
41529
41996
  const normalizedScopeId = scopeId.trim();
@@ -41804,11 +42271,13 @@ var HeadlessSession = class {
41804
42271
  const target = this.resolveNavigationTarget(url);
41805
42272
  const launchedPage = this.bootstrapNavigation(createAppLaunchOptions(target.normalizedRoute, target.query));
41806
42273
  if (launchedPage) return launchedPage;
41807
- this.unloadAllPages();
41808
- const pageInstance = this.createFreshPage(target);
42274
+ const event = this.beginRoute(target, "reLaunch");
42275
+ this.unloadAllPages(event);
42276
+ const pageInstance = this.createFreshPage(target, event);
41809
42277
  this.pages.push(pageInstance);
41810
42278
  this.currentPageInstance = pageInstance;
41811
42279
  this.runInitialPageLifecycles(pageInstance, target.query);
42280
+ this.routeEvents.commit(pageInstance, event);
41812
42281
  return pageInstance;
41813
42282
  }
41814
42283
  navigateTo(url) {
@@ -41817,12 +42286,14 @@ var HeadlessSession = class {
41817
42286
  if (launchedPage) return launchedPage;
41818
42287
  if (this.pages.length >= PAGE_STACK_LIMIT) throw new Error(`Cannot navigateTo() beyond a ${PAGE_STACK_LIMIT}-page stack in headless runtime.`);
41819
42288
  if (this.isTabBarRoute(target.routeRecord.route)) throw new Error(`wx.navigateTo() cannot open a tabBar page in headless runtime: ${url}`);
42289
+ const event = this.beginRoute(target, "navigateTo");
41820
42290
  this.currentPageInstance?.onHide?.();
41821
42291
  if (this.currentPageInstance) this.runPageComponentLifetime(this.currentPageInstance.route, "hide");
41822
- const pageInstance = this.createFreshPage(target);
42292
+ const pageInstance = this.createFreshPage(target, event);
41823
42293
  this.pages.push(pageInstance);
41824
42294
  this.currentPageInstance = pageInstance;
41825
42295
  this.runInitialPageLifecycles(pageInstance, target.query);
42296
+ this.routeEvents.commit(pageInstance, event);
41826
42297
  return pageInstance;
41827
42298
  }
41828
42299
  redirectTo(url) {
@@ -41830,15 +42301,17 @@ var HeadlessSession = class {
41830
42301
  const launchedPage = this.bootstrapNavigation(createAppLaunchOptions(target.normalizedRoute, target.query));
41831
42302
  if (launchedPage) return launchedPage;
41832
42303
  if (this.isTabBarRoute(target.routeRecord.route)) throw new Error(`wx.redirectTo() cannot open a tabBar page in headless runtime: ${url}`);
42304
+ const event = this.beginRoute(target, "redirectTo");
41833
42305
  const current = this.currentPageInstance;
41834
42306
  if (current) {
42307
+ this.unloadPage(current, event);
41835
42308
  this.pages.pop();
41836
- this.unloadPage(current);
41837
42309
  }
41838
- const pageInstance = this.createFreshPage(target);
42310
+ const pageInstance = this.createFreshPage(target, event);
41839
42311
  this.pages.push(pageInstance);
41840
42312
  this.currentPageInstance = pageInstance;
41841
42313
  this.runInitialPageLifecycles(pageInstance, target.query);
42314
+ this.routeEvents.commit(pageInstance, event);
41842
42315
  return pageInstance;
41843
42316
  }
41844
42317
  navigateBack(delta = 1) {
@@ -41846,12 +42319,17 @@ var HeadlessSession = class {
41846
42319
  if (launchedPage) return launchedPage;
41847
42320
  if (this.pages.length <= 1) return this.currentPageInstance;
41848
42321
  const removableCount = Math.min(Number.isFinite(delta) ? Math.max(1, Math.trunc(delta)) : 1, this.pages.length - 1);
41849
- const removedPages = this.pages.splice(this.pages.length - removableCount, removableCount);
41850
- for (const page of removedPages.reverse()) this.unloadPage(page);
41851
- const nextPage = this.pages.at(-1) ?? null;
42322
+ const nextPage = this.pages[this.pages.length - removableCount - 1];
42323
+ const event = this.routeEvents.begin(nextPage.route, nextPage.options, "navigateBack", void 0, nextPage);
42324
+ this.routeEvents.attach(nextPage, event);
42325
+ for (let index = 0; index < removableCount; index++) {
42326
+ this.unloadPage(this.pages.at(-1), event);
42327
+ this.pages.pop();
42328
+ }
41852
42329
  this.currentPageInstance = nextPage;
41853
42330
  nextPage?.onShow?.();
41854
42331
  if (nextPage) this.runPageComponentLifetime(nextPage.route, "show");
42332
+ this.routeEvents.commit(nextPage, event);
41855
42333
  return nextPage;
41856
42334
  }
41857
42335
  switchTab(url) {
@@ -41869,6 +42347,8 @@ var HeadlessSession = class {
41869
42347
  const current = this.currentPageInstance;
41870
42348
  const cachedTarget = this.tabPages.get(target.routeRecord.route) ?? null;
41871
42349
  if (current === cachedTarget && current) return () => {};
42350
+ const event = this.beginRoute(target, "switchTab", cachedTarget ?? void 0);
42351
+ if (cachedTarget) this.routeEvents.attach(cachedTarget, event);
41872
42352
  if (current && current !== cachedTarget) {
41873
42353
  current.onHide?.();
41874
42354
  this.runPageComponentLifetime(current.route, "hide");
@@ -41878,7 +42358,7 @@ var HeadlessSession = class {
41878
42358
  let nextPage = cachedTarget;
41879
42359
  let shouldRunInitialLifecycles = false;
41880
42360
  if (!nextPage) {
41881
- nextPage = this.createFreshPage(target);
42361
+ nextPage = this.createFreshPage(target, event);
41882
42362
  this.tabPages.set(target.routeRecord.route, nextPage);
41883
42363
  shouldRunInitialLifecycles = true;
41884
42364
  }
@@ -41891,7 +42371,8 @@ var HeadlessSession = class {
41891
42371
  this.runPageComponentLifetime(nextPage.route, "show");
41892
42372
  }
41893
42373
  return () => {
41894
- for (const page of pagesToUnload) this.unloadPage(page);
42374
+ for (const page of pagesToUnload) this.unloadPage(page, event);
42375
+ this.routeEvents.commit(nextPage, event);
41895
42376
  };
41896
42377
  }
41897
42378
  pageScrollTo(option) {
@@ -41986,17 +42467,20 @@ var HeadlessSession = class {
41986
42467
  windowInfo: this.getWindowInfo()
41987
42468
  });
41988
42469
  }
41989
- createFreshPage(target) {
42470
+ beginRoute(target, openType, page) {
42471
+ return this.routeEvents.begin(target.normalizedRoute, target.query, openType, target.pageConfig?.renderer ?? this.project.appConfig.renderer, page);
42472
+ }
42473
+ createFreshPage(target, event) {
41990
42474
  const resourcePath = target.routeRecord.resourcePath ?? target.routeRecord.route;
41991
42475
  const pageModulePath = path.resolve(this.project.miniprogramRootPath, `${resourcePath}.js`);
41992
- const pageConfigPath = path.resolve(this.project.miniprogramRootPath, `${resourcePath}.json`);
41993
42476
  const pageDefinition = this.moduleLoader.executePageModule(pageModulePath, target.routeRecord.route);
41994
- const pageConfig = readJsonObject(this.project, pageConfigPath);
42477
+ const pageConfig = target.pageConfig;
41995
42478
  const pageInstance = createPageInstance(`/${target.routeRecord.route}`, pageDefinition, target.query, {
41996
42479
  background: resolveBackgroundSnapshot(this.project.appConfig, pageConfig),
41997
42480
  navigationBar: resolveNavigationBarSnapshot(this.project.appConfig, pageConfig),
41998
42481
  requestRender: (callback) => this.requestRender(callback)
41999
42482
  });
42483
+ this.routeEvents.attach(pageInstance, event);
42000
42484
  pageInstance.createIntersectionObserver = (options) => this.createIntersectionObserver(pageInstance, options);
42001
42485
  pageInstance.createMediaQueryObserver = () => this.createMediaQueryObserver(pageInstance);
42002
42486
  pageInstance.selectComponent = (selector) => resolveNativeComponentSelection(this.selectComponent(selector));
@@ -42009,7 +42493,7 @@ var HeadlessSession = class {
42009
42493
  return pageInstance;
42010
42494
  }
42011
42495
  runInitialPageLifecycles(pageInstance, query) {
42012
- runInitialPageLifecycles(pageInstance, query, this.kernel.scheduler, () => this.pages.includes(pageInstance) || this.tabPages.get(pageInstance.route) === pageInstance, () => this.renderCurrentPage());
42496
+ runInitialPageLifecycles(pageInstance, query, this.kernel.scheduler, () => this.pages.includes(pageInstance) || this.tabPages.get(pageInstance.route) === pageInstance, () => this.renderCurrentPage(), () => this.routeEvents.ready(pageInstance));
42013
42497
  }
42014
42498
  isTabBarRoute(route) {
42015
42499
  return this.tabBarRoutes.has(stripLeadingSlash(route));
@@ -42038,24 +42522,45 @@ var HeadlessSession = class {
42038
42522
  return {
42039
42523
  normalizedRoute,
42040
42524
  query,
42041
- routeRecord
42525
+ routeRecord,
42526
+ pageConfig: readJsonObject(this.project, path.resolve(this.project.miniprogramRootPath, `${routeRecord.resourcePath ?? routeRecord.route}.json`))
42042
42527
  };
42043
42528
  }
42044
- unloadAllPages() {
42529
+ unloadAllPages(event, closing = false) {
42045
42530
  const pagesToUnload = /* @__PURE__ */ new Set([...this.pages, ...this.tabPages.values()]);
42046
- for (const page of [...pagesToUnload].reverse()) this.unloadPage(page);
42531
+ let failure;
42532
+ for (const page of [...pagesToUnload].reverse()) try {
42533
+ this.unloadPage(page, event, closing);
42534
+ } catch (error) {
42535
+ if (!closing) throw error;
42536
+ failure ??= { error };
42537
+ }
42047
42538
  this.pages.length = 0;
42048
42539
  this.tabPages.clear();
42049
42540
  this.componentCache.clear();
42050
42541
  this.componentScopes.clear();
42051
42542
  this.clearMediaQueryObservers();
42052
42543
  this.currentPageInstance = null;
42544
+ if (failure) throw failure.error;
42053
42545
  }
42054
- unloadPage(page) {
42055
- page.onUnload?.();
42546
+ unloadPage(page, event, closing = false) {
42547
+ let failure;
42548
+ try {
42549
+ this.routeEvents.beforeUnload(page, event);
42550
+ page.onUnload?.();
42551
+ } catch (error) {
42552
+ if (!closing) throw error;
42553
+ failure = { error };
42554
+ }
42056
42555
  this.clearMediaQueryObservers(page);
42057
- this.detachPageComponents(page.route);
42556
+ try {
42557
+ this.detachPageComponents(page.route, closing);
42558
+ } catch (error) {
42559
+ if (!closing) throw error;
42560
+ failure ??= { error };
42561
+ }
42058
42562
  this.tabPages.delete(stripLeadingSlash(page.route));
42563
+ if (failure) throw failure.error;
42059
42564
  }
42060
42565
  requireCurrentPage(action) {
42061
42566
  this.bootstrap();
@@ -42083,15 +42588,27 @@ var HeadlessSession = class {
42083
42588
  const directlyOwned = rootScopeId ? matches.filter(([, scope]) => scope.ownerScopeId === rootScopeId) : [];
42084
42589
  return (directlyOwned.length > 0 ? directlyOwned : matches).map(([candidateScopeId]) => this.componentCache.get(candidateScopeId)).filter(Boolean);
42085
42590
  }
42086
- detachPageComponents(route) {
42591
+ detachPageComponents(route, closing = false) {
42087
42592
  const prefix = `page:${stripLeadingSlash(route)}`;
42088
42593
  const removed = [...this.componentCache].filter(([scopeId]) => scopeId.startsWith(prefix));
42089
- for (const [, instance] of removed) runComponentLifecycle(instance, "detached");
42090
- detachComponentRelations(removed.map(([, instance]) => instance));
42594
+ let failure;
42595
+ for (const [, instance] of removed) try {
42596
+ runComponentLifecycle(instance, "detached");
42597
+ } catch (error) {
42598
+ if (!closing) throw error;
42599
+ failure ??= { error };
42600
+ }
42601
+ try {
42602
+ detachComponentRelations(removed.map(([, instance]) => instance));
42603
+ } catch (error) {
42604
+ if (!closing) throw error;
42605
+ failure ??= { error };
42606
+ }
42091
42607
  for (const [scopeId] of removed) {
42092
42608
  this.componentCache.delete(scopeId);
42093
42609
  this.componentScopes.delete(scopeId);
42094
42610
  }
42611
+ if (failure) throw failure.error;
42095
42612
  }
42096
42613
  notifyMediaQueryObservers() {
42097
42614
  for (const entry of [...this.mediaQueryObservers]) {
@@ -42251,6 +42768,7 @@ function createPageRootNodeHandle(options) {
42251
42768
  const tree = session?.getCurrentPages().includes(page) ? session.renderCurrentPage() : renderPageTree(project, page);
42252
42769
  return new HeadlessTestingNodeHandle(tree.root.type === "root" && !options.includeHostRoots ? tree.root.children?.[0] ?? tree.root : tree.root, {
42253
42770
  assertActive: options.assertActive,
42771
+ dispatchNativeEvent: (node, eventName, event, onHandlerResult) => session?.getCurrentPages().at(-1) === page ? session.dispatchNativeNodeEvent(node, eventName, event, onHandlerResult) : false,
42254
42772
  callMethod: (scopeId, methodName, event) => {
42255
42773
  if (session?.getCurrentPages().includes(page)) return session.callScopeMethod(scopeId, methodName, event);
42256
42774
  const method = page[methodName];
@@ -42690,9 +43208,13 @@ var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
42690
43208
  var HeadlessTestingSessionHandle = class {
42691
43209
  project;
42692
43210
  session;
43211
+ pendingNavigations = /* @__PURE__ */ new Set();
42693
43212
  constructor(project, session) {
42694
43213
  this.project = project;
42695
43214
  this.session = session;
43215
+ this.session.on("close", () => {
43216
+ for (const reject of this.pendingNavigations) reject(/* @__PURE__ */ new Error("The headless testing session closed before navigation completed."));
43217
+ });
42696
43218
  }
42697
43219
  async close() {
42698
43220
  this.session.close();
@@ -42761,23 +43283,23 @@ var HeadlessTestingSessionHandle = class {
42761
43283
  }, `Timed out waiting for ${normalizedCount} component(s) matching "${normalizedSelector}" in headless testing runtime.`, options);
42762
43284
  }
42763
43285
  async reLaunch(route) {
42764
- this.invokeNavigation("reLaunch", { url: route });
43286
+ await this.invokeNavigation("reLaunch", { url: route });
42765
43287
  return await this.requireCurrentPage("reLaunch", route);
42766
43288
  }
42767
43289
  async navigateTo(route) {
42768
- this.invokeNavigation("navigateTo", { url: route });
43290
+ await this.invokeNavigation("navigateTo", { url: route });
42769
43291
  return await this.requireCurrentPage("navigateTo", route);
42770
43292
  }
42771
43293
  async redirectTo(route) {
42772
- this.invokeNavigation("redirectTo", { url: route });
43294
+ await this.invokeNavigation("redirectTo", { url: route });
42773
43295
  return await this.requireCurrentPage("redirectTo", route);
42774
43296
  }
42775
43297
  async navigateBack(delta = 1) {
42776
- this.invokeNavigation("navigateBack", { delta });
43298
+ await this.invokeNavigation("navigateBack", { delta });
42777
43299
  return await this.currentPage();
42778
43300
  }
42779
43301
  async switchTab(route) {
42780
- this.invokeNavigation("switchTab", { url: route });
43302
+ await this.invokeNavigation("switchTab", { url: route });
42781
43303
  return await this.requireCurrentPage("switchTab", route);
42782
43304
  }
42783
43305
  async waitForCurrentPage(route, options = {}) {
@@ -42814,21 +43336,30 @@ var HeadlessTestingSessionHandle = class {
42814
43336
  this.session.triggerRouteDone(options);
42815
43337
  }
42816
43338
  invokeNavigation(methodName, options) {
42817
- const failure = { received: false };
42818
- this.session.callWxMethod(methodName, {
42819
- ...options,
42820
- fail(error) {
42821
- failure.received = true;
42822
- failure.error = error;
43339
+ return new Promise((resolve, reject) => {
43340
+ const rejectNavigation = (error) => {
43341
+ this.pendingNavigations.delete(rejectNavigation);
43342
+ reject(error);
43343
+ };
43344
+ this.pendingNavigations.add(rejectNavigation);
43345
+ try {
43346
+ this.session.callWxMethod(methodName, {
43347
+ ...options,
43348
+ success: () => {
43349
+ this.pendingNavigations.delete(rejectNavigation);
43350
+ resolve();
43351
+ },
43352
+ fail: (error) => {
43353
+ const details = error && typeof error === "object" ? error : void 0;
43354
+ const reason = typeof details?.errMsg === "string" ? details.errMsg : typeof details?.message === "string" ? details.message : String(error);
43355
+ const target = options.url ? ` route "${options.url}"` : "";
43356
+ rejectNavigation(new Error(`Failed to ${methodName}${target} through the headless wx runtime: ${reason}`, { cause: error }));
43357
+ }
43358
+ });
43359
+ } catch (error) {
43360
+ rejectNavigation(error);
42823
43361
  }
42824
43362
  });
42825
- if (failure.received) {
42826
- const error = failure.error;
42827
- const details = error && typeof error === "object" ? error : void 0;
42828
- const reason = typeof details?.errMsg === "string" ? details.errMsg : typeof details?.message === "string" ? details.message : String(error);
42829
- const target = options.url ? ` route "${options.url}"` : "";
42830
- throw new Error(`Failed to ${methodName}${target} through the headless wx runtime: ${reason}`, { cause: error });
42831
- }
42832
43363
  }
42833
43364
  async requireCurrentPage(methodName, route) {
42834
43365
  const page = await this.currentPage();