@weapp-vite/miniprogram-automator 1.1.1 → 1.1.3

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/index.d.mts CHANGED
@@ -42,12 +42,15 @@ declare class Transport extends EventEmitter {
42
42
  }
43
43
  //#endregion
44
44
  //#region src/Connection.d.ts
45
+ interface SendOptions {
46
+ timeout?: number;
47
+ }
45
48
  /** Connection 的实现。 */
46
49
  declare class Connection extends EventEmitter {
47
50
  private transport;
48
51
  private callbacks;
49
52
  constructor(transport: Transport);
50
- send(method: string, params?: Record<string, any>): Promise<any>;
53
+ send(method: string, params?: Record<string, any>, options?: SendOptions): Promise<any>;
51
54
  dispose(): void;
52
55
  private onMessage;
53
56
  private onClose;
@@ -240,6 +243,10 @@ interface IToolClearCacheOptions {
240
243
  clean: 'all' | 'auth' | 'compile' | 'file' | 'network' | 'session' | 'storage';
241
244
  }
242
245
  type AutomatorCallable = (...args: any[]) => any;
246
+ interface CurrentPageOptions {
247
+ retries?: number;
248
+ timeout?: number;
249
+ }
243
250
  /** MiniProgram 的实现。 */
244
251
  declare class MiniProgram extends EventEmitter {
245
252
  private connection;
@@ -253,9 +260,10 @@ declare class MiniProgram extends EventEmitter {
253
260
  navigateBack(): Promise<any>;
254
261
  reLaunch(url: string): Promise<any>;
255
262
  switchTab(url: string): Promise<any>;
256
- currentPage(): Promise<Page>;
263
+ currentPage(options?: CurrentPageOptions): Promise<Page>;
257
264
  systemInfo(): Promise<any>;
258
265
  callWxMethod(method: string, ...args: any[]): Promise<any>;
266
+ private callWxMethodWithTimeout;
259
267
  mockWxMethod(method: string, result: any, ...args: any[]): Promise<void>;
260
268
  restoreWxMethod(method: string): Promise<void>;
261
269
  callPluginWxMethod(pluginId: string, method: string, ...args: any[]): Promise<any>;
@@ -293,6 +301,9 @@ declare class MiniProgram extends EventEmitter {
293
301
  refreshTicket(): Promise<void>;
294
302
  native(): Native;
295
303
  private changeRoute;
304
+ private resolveRouteContextPage;
305
+ private readRoutePollingCurrentPage;
306
+ private readRoutePollingPageStack;
296
307
  private send;
297
308
  private onLogAdded;
298
309
  private onBindingCalled;
package/dist/index.mjs CHANGED
@@ -512,22 +512,23 @@ var Connection = class Connection extends EventEmitter {
512
512
  transport.on("message", this.onMessage);
513
513
  transport.on("close", this.onClose);
514
514
  }
515
- send(method, params = {}) {
515
+ send(method, params = {}, options = {}) {
516
516
  const id = uuid();
517
517
  const payload = stringify({
518
518
  id,
519
519
  method,
520
520
  params
521
521
  });
522
+ const requestTimeout = options.timeout ?? REQUEST_TIMEOUT;
522
523
  debugProtocol(`${dateFormat("yyyy-mm-dd HH:MM:ss:l")} SEND ► ${payload}`);
523
524
  return new Promise((resolve, reject) => {
524
525
  const timeout = setTimeout(() => {
525
526
  this.callbacks.delete(id);
526
- const error = /* @__PURE__ */ new Error(`DevTools did not respond to protocol method ${method} within ${REQUEST_TIMEOUT}ms`);
527
+ const error = /* @__PURE__ */ new Error(`DevTools did not respond to protocol method ${method} within ${requestTimeout}ms`);
527
528
  error.code = "DEVTOOLS_PROTOCOL_TIMEOUT";
528
529
  error.method = method;
529
530
  reject(error);
530
- }, REQUEST_TIMEOUT);
531
+ }, requestTimeout);
531
532
  this.callbacks.set(id, {
532
533
  resolve,
533
534
  reject,
@@ -582,11 +583,11 @@ var Connection = class Connection extends EventEmitter {
582
583
  //#endregion
583
584
  //#region src/headless.ts
584
585
  async function launchHeadlessAutomator(options) {
585
- return await (await import("./launch-CDd9gHVW.mjs")).launch({ projectPath: options.projectPath });
586
+ return await (await import("./launch-F4arniwI.mjs")).launch({ projectPath: options.projectPath });
586
587
  }
587
588
  //#endregion
588
589
  //#region package.json
589
- var version = "1.1.1";
590
+ var version = "1.1.3";
590
591
  //#endregion
591
592
  //#region src/Native.ts
592
593
  /** Native 的实现。 */
@@ -779,6 +780,8 @@ function extractPluginId(p) {
779
780
  const CLOSE_STEP_TIMEOUT = 2e3;
780
781
  const CURRENT_PAGE_RETRIES = 3;
781
782
  const CURRENT_PAGE_RETRY_DELAY = 400;
783
+ const CHANGE_ROUTE_CONTEXT_TIMEOUT = 12e3;
784
+ const CHANGE_ROUTE_CALL_TIMEOUT = 12e3;
782
785
  const CHANGE_ROUTE_READY_TIMEOUT = 15e3;
783
786
  const CHANGE_ROUTE_POLL_DELAY = 500;
784
787
  const CHANGE_ROUTE_DEBUG_ENABLED = process.env.WEAPP_VITE_E2E_CHANGE_ROUTE_DEBUG === "1";
@@ -799,6 +802,9 @@ function withTimeout(task, timeoutMs) {
799
802
  });
800
803
  });
801
804
  }
805
+ function isOperationTimeoutError(error, timeoutMs) {
806
+ return error instanceof Error && error.message.includes(`Operation timed out after ${timeoutMs}ms`);
807
+ }
802
808
  function isFnStr(value) {
803
809
  if (!isStr(value)) return false;
804
810
  const trimmed = trim(value);
@@ -807,6 +813,15 @@ function isFnStr(value) {
807
813
  function isCurrentPageProtocolTimeout(error) {
808
814
  return error instanceof Error && "code" in error && error.code === "DEVTOOLS_PROTOCOL_TIMEOUT" && "method" in error && error.method === "App.getCurrentPage";
809
815
  }
816
+ function isPageStackProtocolTimeout(error) {
817
+ return error instanceof Error && "code" in error && error.code === "DEVTOOLS_PROTOCOL_TIMEOUT" && "method" in error && error.method === "App.getPageStack";
818
+ }
819
+ function isCallWxMethodProtocolTimeout(error) {
820
+ return error instanceof Error && "code" in error && error.code === "DEVTOOLS_PROTOCOL_TIMEOUT" && "method" in error && error.method === "App.callWxMethod";
821
+ }
822
+ function isRouteContextProbeError(error) {
823
+ return isCurrentPageProtocolTimeout(error) || isPageStackProtocolTimeout(error);
824
+ }
810
825
  function normalizeRoutePath(value) {
811
826
  return String(value ?? "").split("?", 1)[0].replace(/^\/+/, "").replace(/\/+$/, "");
812
827
  }
@@ -852,10 +867,12 @@ var MiniProgram = class extends EventEmitter {
852
867
  async switchTab(url) {
853
868
  return await this.changeRoute("switchTab", url);
854
869
  }
855
- async currentPage() {
870
+ async currentPage(options = {}) {
856
871
  let lastError;
857
- for (let attempt = 1; attempt <= CURRENT_PAGE_RETRIES; attempt += 1) try {
858
- const { pageId, path, query } = await this.send("App.getCurrentPage");
872
+ const retries = options.retries ?? CURRENT_PAGE_RETRIES;
873
+ const sendOptions = options.timeout ? { timeout: options.timeout } : void 0;
874
+ for (let attempt = 1; attempt <= retries; attempt += 1) try {
875
+ const { pageId, path, query } = await this.send("App.getCurrentPage", {}, sendOptions);
859
876
  return Page.create(this.connection, {
860
877
  id: pageId,
861
878
  path,
@@ -863,8 +880,22 @@ var MiniProgram = class extends EventEmitter {
863
880
  }, this.pageMap);
864
881
  } catch (error) {
865
882
  lastError = error;
866
- if (!isCurrentPageProtocolTimeout(error) || attempt >= CURRENT_PAGE_RETRIES) throw error;
867
- await sleep(CURRENT_PAGE_RETRY_DELAY);
883
+ if (!isCurrentPageProtocolTimeout(error)) throw error;
884
+ if (attempt < retries) {
885
+ await sleep(CURRENT_PAGE_RETRY_DELAY);
886
+ continue;
887
+ }
888
+ }
889
+ if (isCurrentPageProtocolTimeout(lastError)) try {
890
+ const { pageStack } = await this.send("App.getPageStack", {}, sendOptions);
891
+ const page = pageStack[pageStack.length - 1];
892
+ if (page) return Page.create(this.connection, {
893
+ id: page.pageId,
894
+ path: page.path,
895
+ query: page.query
896
+ }, this.pageMap);
897
+ } catch (error) {
898
+ if (!isPageStackProtocolTimeout(error)) throw error;
868
899
  }
869
900
  throw lastError;
870
901
  }
@@ -877,6 +908,12 @@ var MiniProgram = class extends EventEmitter {
877
908
  args
878
909
  })).result;
879
910
  }
911
+ async callWxMethodWithTimeout(method, timeout, ...args) {
912
+ return (await this.send("App.callWxMethod", {
913
+ method,
914
+ args
915
+ }, { timeout })).result;
916
+ }
880
917
  async mockWxMethod(method, result, ...args) {
881
918
  if (isFn(result) || isFnStr(result)) {
882
919
  await this.send("App.mockWxMethod", {
@@ -1025,19 +1062,22 @@ var MiniProgram = class extends EventEmitter {
1025
1062
  return this.nativeIns;
1026
1063
  }
1027
1064
  async changeRoute(method, url) {
1028
- const currentPage = await this.currentPage();
1065
+ const currentPage = await this.resolveRouteContextPage();
1029
1066
  logChangeRouteDebug(`start method=${method} url=${url ?? "<none>"} current=${currentPage?.path ?? "<none>"}`);
1030
- if (currentPage && isPluginPath(currentPage.path)) {
1031
- const pluginId = extractPluginId(currentPage.path);
1032
- await this.callPluginWxMethod(pluginId, method, { url });
1033
- } else await this.callWxMethod(method, { url });
1067
+ try {
1068
+ if (currentPage && isPluginPath(currentPage.path)) await this.callPluginWxMethod(extractPluginId(currentPage.path), method, { url });
1069
+ else await this.callWxMethodWithTimeout(method, CHANGE_ROUTE_CALL_TIMEOUT, { url });
1070
+ } catch (error) {
1071
+ if (isCallWxMethodProtocolTimeout(error) || isOperationTimeoutError(error, CHANGE_ROUTE_CALL_TIMEOUT)) logChangeRouteDebug(`call-timeout method=${method} url=${url ?? "<none>"}`);
1072
+ else throw error;
1073
+ }
1034
1074
  const expectedRoute = normalizeRoutePath(url);
1035
1075
  const startedAt = Date.now();
1036
1076
  let lastPage;
1037
1077
  let lastError;
1038
1078
  while (Date.now() - startedAt <= CHANGE_ROUTE_READY_TIMEOUT) {
1039
1079
  try {
1040
- const page = await this.currentPage();
1080
+ const page = await this.readRoutePollingCurrentPage();
1041
1081
  lastPage = page;
1042
1082
  logChangeRouteDebug(`poll method=${method} url=${url ?? "<none>"} current=${page?.path ?? "<none>"}`);
1043
1083
  if (!expectedRoute || normalizeRoutePath(page?.path) === expectedRoute) {
@@ -1049,7 +1089,7 @@ var MiniProgram = class extends EventEmitter {
1049
1089
  logChangeRouteDebug(`poll-error method=${method} url=${url ?? "<none>"} error=${error instanceof Error ? error.message : String(error)}`);
1050
1090
  }
1051
1091
  try {
1052
- const stack = await this.pageStack();
1092
+ const stack = await this.readRoutePollingPageStack();
1053
1093
  const stackTop = stack[stack.length - 1];
1054
1094
  if (stackTop) {
1055
1095
  lastPage = stackTop;
@@ -1062,6 +1102,10 @@ var MiniProgram = class extends EventEmitter {
1062
1102
  } catch (error) {
1063
1103
  lastError = error;
1064
1104
  logChangeRouteDebug(`stack-error method=${method} url=${url ?? "<none>"} error=${error instanceof Error ? error.message : String(error)}`);
1105
+ if (isOperationTimeoutError(error, CHANGE_ROUTE_CONTEXT_TIMEOUT)) {
1106
+ await sleep(CHANGE_ROUTE_POLL_DELAY);
1107
+ continue;
1108
+ }
1065
1109
  }
1066
1110
  await sleep(CHANGE_ROUTE_POLL_DELAY);
1067
1111
  }
@@ -1072,8 +1116,38 @@ var MiniProgram = class extends EventEmitter {
1072
1116
  logChangeRouteDebug(`timeout method=${method} url=${url ?? "<none>"} current=<none> error=${lastError instanceof Error ? lastError.message : String(lastError)}`);
1073
1117
  throw lastError instanceof Error ? lastError : /* @__PURE__ */ new Error(`Timed out waiting route ${expectedRoute || "<current>"} after ${method}`);
1074
1118
  }
1075
- async send(method, params = {}) {
1076
- return await this.connection.send(method, params);
1119
+ async resolveRouteContextPage() {
1120
+ const currentPageTask = this.currentPage().catch((error) => {
1121
+ if (isRouteContextProbeError(error)) return;
1122
+ throw error;
1123
+ });
1124
+ const timeoutTask = sleep(CHANGE_ROUTE_CONTEXT_TIMEOUT).then(() => void 0);
1125
+ try {
1126
+ return await Promise.race([currentPageTask, timeoutTask]);
1127
+ } finally {
1128
+ currentPageTask.catch(() => {});
1129
+ }
1130
+ }
1131
+ async readRoutePollingCurrentPage() {
1132
+ const { pageId, path, query } = await this.send("App.getCurrentPage", {}, { timeout: CHANGE_ROUTE_CONTEXT_TIMEOUT });
1133
+ return Page.create(this.connection, {
1134
+ id: pageId,
1135
+ path,
1136
+ query
1137
+ }, this.pageMap);
1138
+ }
1139
+ async readRoutePollingPageStack() {
1140
+ const { pageStack } = await this.send("App.getPageStack", {}, { timeout: CHANGE_ROUTE_CONTEXT_TIMEOUT });
1141
+ return pageStack.map((page) => {
1142
+ return Page.create(this.connection, {
1143
+ id: page.pageId,
1144
+ path: page.path,
1145
+ query: page.query
1146
+ }, this.pageMap);
1147
+ });
1148
+ }
1149
+ async send(method, params = {}, options) {
1150
+ return options ? await this.connection.send(method, params, options) : await this.connection.send(method, params);
1077
1151
  }
1078
1152
  onLogAdded = (payload) => {
1079
1153
  this.emit("console", payload);
@@ -5241,11 +5241,10 @@ function deriveMenuButtonBoundingClientRect(systemInfo) {
5241
5241
  const height = 32;
5242
5242
  const top = 32;
5243
5243
  const right = Math.max(systemInfo.windowWidth - 12, width);
5244
- const left = Math.max(0, right - width);
5245
5244
  return {
5246
- bottom: top + height,
5245
+ bottom: 64,
5247
5246
  height,
5248
- left,
5247
+ left: Math.max(0, right - width),
5249
5248
  right,
5250
5249
  top,
5251
5250
  width
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@weapp-vite/miniprogram-automator",
3
3
  "type": "module",
4
- "version": "1.1.1",
4
+ "version": "1.1.3",
5
5
  "description": "完全兼容微信 miniprogram-automator 的现代化替代实现",
6
6
  "author": "ice breaker <1324318532@qq.com>",
7
7
  "license": "MIT",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "obug": "^2.1.1",
45
- "ws": "^8.20.1",
45
+ "ws": "^8.21.0",
46
46
  "@weapp-vite/qr": "1.1.0"
47
47
  },
48
48
  "publishConfig": {