@weapp-vite/miniprogram-automator 1.1.2 → 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 +13 -2
- package/dist/index.mjs +75 -32
- package/package.json +2 -2
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
|
|
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 ${
|
|
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
|
-
},
|
|
531
|
+
}, requestTimeout);
|
|
531
532
|
this.callbacks.set(id, {
|
|
532
533
|
resolve,
|
|
533
534
|
reject,
|
|
@@ -586,7 +587,7 @@ async function launchHeadlessAutomator(options) {
|
|
|
586
587
|
}
|
|
587
588
|
//#endregion
|
|
588
589
|
//#region package.json
|
|
589
|
-
var version = "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);
|
|
@@ -810,6 +816,12 @@ function isCurrentPageProtocolTimeout(error) {
|
|
|
810
816
|
function isPageStackProtocolTimeout(error) {
|
|
811
817
|
return error instanceof Error && "code" in error && error.code === "DEVTOOLS_PROTOCOL_TIMEOUT" && "method" in error && error.method === "App.getPageStack";
|
|
812
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
|
+
}
|
|
813
825
|
function normalizeRoutePath(value) {
|
|
814
826
|
return String(value ?? "").split("?", 1)[0].replace(/^\/+/, "").replace(/\/+$/, "");
|
|
815
827
|
}
|
|
@@ -855,10 +867,12 @@ var MiniProgram = class extends EventEmitter {
|
|
|
855
867
|
async switchTab(url) {
|
|
856
868
|
return await this.changeRoute("switchTab", url);
|
|
857
869
|
}
|
|
858
|
-
async currentPage() {
|
|
870
|
+
async currentPage(options = {}) {
|
|
859
871
|
let lastError;
|
|
860
|
-
|
|
861
|
-
|
|
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);
|
|
862
876
|
return Page.create(this.connection, {
|
|
863
877
|
id: pageId,
|
|
864
878
|
path,
|
|
@@ -867,13 +881,13 @@ var MiniProgram = class extends EventEmitter {
|
|
|
867
881
|
} catch (error) {
|
|
868
882
|
lastError = error;
|
|
869
883
|
if (!isCurrentPageProtocolTimeout(error)) throw error;
|
|
870
|
-
if (attempt <
|
|
884
|
+
if (attempt < retries) {
|
|
871
885
|
await sleep(CURRENT_PAGE_RETRY_DELAY);
|
|
872
886
|
continue;
|
|
873
887
|
}
|
|
874
888
|
}
|
|
875
889
|
if (isCurrentPageProtocolTimeout(lastError)) try {
|
|
876
|
-
const { pageStack } = await this.send("App.getPageStack");
|
|
890
|
+
const { pageStack } = await this.send("App.getPageStack", {}, sendOptions);
|
|
877
891
|
const page = pageStack[pageStack.length - 1];
|
|
878
892
|
if (page) return Page.create(this.connection, {
|
|
879
893
|
id: page.pageId,
|
|
@@ -894,6 +908,12 @@ var MiniProgram = class extends EventEmitter {
|
|
|
894
908
|
args
|
|
895
909
|
})).result;
|
|
896
910
|
}
|
|
911
|
+
async callWxMethodWithTimeout(method, timeout, ...args) {
|
|
912
|
+
return (await this.send("App.callWxMethod", {
|
|
913
|
+
method,
|
|
914
|
+
args
|
|
915
|
+
}, { timeout })).result;
|
|
916
|
+
}
|
|
897
917
|
async mockWxMethod(method, result, ...args) {
|
|
898
918
|
if (isFn(result) || isFnStr(result)) {
|
|
899
919
|
await this.send("App.mockWxMethod", {
|
|
@@ -1042,33 +1062,22 @@ var MiniProgram = class extends EventEmitter {
|
|
|
1042
1062
|
return this.nativeIns;
|
|
1043
1063
|
}
|
|
1044
1064
|
async changeRoute(method, url) {
|
|
1045
|
-
const currentPage = await this.
|
|
1046
|
-
if (!isCurrentPageProtocolTimeout(error)) throw error;
|
|
1047
|
-
try {
|
|
1048
|
-
const { pageStack } = await this.send("App.getPageStack");
|
|
1049
|
-
const page = pageStack[pageStack.length - 1];
|
|
1050
|
-
return page ? Page.create(this.connection, {
|
|
1051
|
-
id: page.pageId,
|
|
1052
|
-
path: page.path,
|
|
1053
|
-
query: page.query
|
|
1054
|
-
}, this.pageMap) : void 0;
|
|
1055
|
-
} catch (pageStackError) {
|
|
1056
|
-
if (!isPageStackProtocolTimeout(pageStackError)) throw pageStackError;
|
|
1057
|
-
return;
|
|
1058
|
-
}
|
|
1059
|
-
});
|
|
1065
|
+
const currentPage = await this.resolveRouteContextPage();
|
|
1060
1066
|
logChangeRouteDebug(`start method=${method} url=${url ?? "<none>"} current=${currentPage?.path ?? "<none>"}`);
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
await this.
|
|
1064
|
-
}
|
|
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
|
+
}
|
|
1065
1074
|
const expectedRoute = normalizeRoutePath(url);
|
|
1066
1075
|
const startedAt = Date.now();
|
|
1067
1076
|
let lastPage;
|
|
1068
1077
|
let lastError;
|
|
1069
1078
|
while (Date.now() - startedAt <= CHANGE_ROUTE_READY_TIMEOUT) {
|
|
1070
1079
|
try {
|
|
1071
|
-
const page = await this.
|
|
1080
|
+
const page = await this.readRoutePollingCurrentPage();
|
|
1072
1081
|
lastPage = page;
|
|
1073
1082
|
logChangeRouteDebug(`poll method=${method} url=${url ?? "<none>"} current=${page?.path ?? "<none>"}`);
|
|
1074
1083
|
if (!expectedRoute || normalizeRoutePath(page?.path) === expectedRoute) {
|
|
@@ -1080,7 +1089,7 @@ var MiniProgram = class extends EventEmitter {
|
|
|
1080
1089
|
logChangeRouteDebug(`poll-error method=${method} url=${url ?? "<none>"} error=${error instanceof Error ? error.message : String(error)}`);
|
|
1081
1090
|
}
|
|
1082
1091
|
try {
|
|
1083
|
-
const stack = await this.
|
|
1092
|
+
const stack = await this.readRoutePollingPageStack();
|
|
1084
1093
|
const stackTop = stack[stack.length - 1];
|
|
1085
1094
|
if (stackTop) {
|
|
1086
1095
|
lastPage = stackTop;
|
|
@@ -1093,6 +1102,10 @@ var MiniProgram = class extends EventEmitter {
|
|
|
1093
1102
|
} catch (error) {
|
|
1094
1103
|
lastError = error;
|
|
1095
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
|
+
}
|
|
1096
1109
|
}
|
|
1097
1110
|
await sleep(CHANGE_ROUTE_POLL_DELAY);
|
|
1098
1111
|
}
|
|
@@ -1103,8 +1116,38 @@ var MiniProgram = class extends EventEmitter {
|
|
|
1103
1116
|
logChangeRouteDebug(`timeout method=${method} url=${url ?? "<none>"} current=<none> error=${lastError instanceof Error ? lastError.message : String(lastError)}`);
|
|
1104
1117
|
throw lastError instanceof Error ? lastError : /* @__PURE__ */ new Error(`Timed out waiting route ${expectedRoute || "<current>"} after ${method}`);
|
|
1105
1118
|
}
|
|
1106
|
-
async
|
|
1107
|
-
|
|
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);
|
|
1108
1151
|
}
|
|
1109
1152
|
onLogAdded = (payload) => {
|
|
1110
1153
|
this.emit("console", payload);
|
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.
|
|
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.
|
|
45
|
+
"ws": "^8.21.0",
|
|
46
46
|
"@weapp-vite/qr": "1.1.0"
|
|
47
47
|
},
|
|
48
48
|
"publishConfig": {
|