@weapp-vite/miniprogram-automator 1.2.22 → 1.3.0
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/README.md +22 -0
- package/dist/index.d.mts +40 -3
- package/dist/index.mjs +341 -167
- package/dist/{launch-BNQZTcKo.mjs → launch-anJvfi-6.mjs} +1669 -445
- package/dist/operation.d.mts +64 -0
- package/dist/operation.mjs +233 -0
- package/package.json +17 -6
package/dist/index.mjs
CHANGED
|
@@ -1,31 +1,48 @@
|
|
|
1
|
+
import { OPERATION_TIMEOUT_CODE, OperationLifecycle, classifyOperationError, isRecoverableOperationError, readWechatLoginState } from "./operation.mjs";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import net from "node:net";
|
|
3
4
|
import process from "node:process";
|
|
4
|
-
import { spawn } from "node:child_process";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { EventEmitter } from "node:events";
|
|
7
7
|
import { createDebug } from "obug";
|
|
8
8
|
import WebSocket from "ws";
|
|
9
9
|
import fs from "node:fs/promises";
|
|
10
10
|
import os from "node:os";
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
11
12
|
import { decodeQrCodeFromBase64, renderTerminalQrCode } from "@weapp-vite/qr";
|
|
12
13
|
import { Buffer } from "node:buffer";
|
|
13
14
|
//#region src/internal/compat/async.ts
|
|
14
15
|
/**
|
|
15
16
|
* @file 异步控制工具。
|
|
16
17
|
*/
|
|
17
|
-
/**
|
|
18
|
-
function sleep$1(timeout) {
|
|
19
|
-
|
|
18
|
+
/** 可取消的延迟,取消时释放自身计时器。 */
|
|
19
|
+
function sleep$1(timeout, signal) {
|
|
20
|
+
signal?.throwIfAborted();
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
let timer;
|
|
23
|
+
function abort() {
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
signal?.removeEventListener("abort", abort);
|
|
26
|
+
reject(signal?.reason);
|
|
27
|
+
}
|
|
28
|
+
timer = setTimeout(() => {
|
|
29
|
+
signal?.removeEventListener("abort", abort);
|
|
30
|
+
resolve();
|
|
31
|
+
}, timeout);
|
|
32
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
33
|
+
});
|
|
20
34
|
}
|
|
21
|
-
/**
|
|
22
|
-
async function waitUntil(condition, timeout = 0, interval = 250) {
|
|
23
|
-
const
|
|
35
|
+
/** 条件轮询受同一个单调时钟 deadline 与取消信号约束。 */
|
|
36
|
+
async function waitUntil(condition, timeout = 0, interval = 250, signal) {
|
|
37
|
+
const deadlineAt = timeout ? performance.now() + timeout : Number.POSITIVE_INFINITY;
|
|
24
38
|
while (true) {
|
|
39
|
+
signal?.throwIfAborted();
|
|
25
40
|
const value = await condition();
|
|
41
|
+
signal?.throwIfAborted();
|
|
26
42
|
if (value) return value;
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
const remaining = deadlineAt - performance.now();
|
|
44
|
+
if (remaining <= 0) throw new Error(`Wait timed out after ${timeout} ms`);
|
|
45
|
+
await sleep$1(Math.min(interval, remaining), signal);
|
|
29
46
|
}
|
|
30
47
|
}
|
|
31
48
|
//#endregion
|
|
@@ -825,7 +842,7 @@ var Transport = class extends EventEmitter {
|
|
|
825
842
|
this.ws.send(message);
|
|
826
843
|
}
|
|
827
844
|
close() {
|
|
828
|
-
this.ws.
|
|
845
|
+
this.ws.terminate();
|
|
829
846
|
}
|
|
830
847
|
};
|
|
831
848
|
//#endregion
|
|
@@ -839,11 +856,21 @@ const REQUEST_TIMEOUT = 3e4;
|
|
|
839
856
|
const CONNECT_TIMEOUT = 3e4;
|
|
840
857
|
/** Page frame 定向消息失效、需要切换到 App-service Page 协议的 DevTools 版本。 */
|
|
841
858
|
const APP_SERVICE_PAGE_PROTOCOL_VERSIONS = /* @__PURE__ */ new Set(["2.01.2510290"]);
|
|
859
|
+
/** 仅页面方法调用失效的版本,元素查询仍保留原生组件作用域。 */
|
|
860
|
+
const APP_SERVICE_PAGE_METHOD_VERSIONS = /* @__PURE__ */ new Set([
|
|
861
|
+
"2.02.2608070",
|
|
862
|
+
"2.02.2608080",
|
|
863
|
+
"2.02.2609231",
|
|
864
|
+
"2.02.2610082",
|
|
865
|
+
"2.02.2610092"
|
|
866
|
+
]);
|
|
842
867
|
/** Connection 的实现。 */
|
|
843
868
|
var Connection = class Connection extends EventEmitter {
|
|
844
869
|
transport;
|
|
870
|
+
disposed = false;
|
|
845
871
|
callbacks = /* @__PURE__ */ new Map();
|
|
846
872
|
useAppServicePageProtocol = false;
|
|
873
|
+
useAppServicePageMethod = false;
|
|
847
874
|
constructor(transport) {
|
|
848
875
|
super();
|
|
849
876
|
this.transport = transport;
|
|
@@ -851,6 +878,7 @@ var Connection = class Connection extends EventEmitter {
|
|
|
851
878
|
transport.on("close", this.onClose);
|
|
852
879
|
}
|
|
853
880
|
send(method, params = {}, options = {}) {
|
|
881
|
+
if (this.disposed) return Promise.reject(/* @__PURE__ */ new Error(closeErrTip));
|
|
854
882
|
const id = uuid();
|
|
855
883
|
const payload = stringify({
|
|
856
884
|
id,
|
|
@@ -874,23 +902,34 @@ var Connection = class Connection extends EventEmitter {
|
|
|
874
902
|
});
|
|
875
903
|
try {
|
|
876
904
|
this.transport.send(payload);
|
|
877
|
-
} catch {
|
|
905
|
+
} catch (cause) {
|
|
878
906
|
clearTimeout(timeout);
|
|
879
907
|
this.callbacks.delete(id);
|
|
880
|
-
reject(
|
|
908
|
+
reject(new Error(closeErrTip, { cause }));
|
|
881
909
|
}
|
|
882
910
|
});
|
|
883
911
|
}
|
|
884
912
|
dispose() {
|
|
913
|
+
if (this.disposed) return;
|
|
914
|
+
this.disposed = true;
|
|
915
|
+
this.onClose();
|
|
916
|
+
this.transport.off("message", this.onMessage);
|
|
917
|
+
this.transport.off("close", this.onClose);
|
|
885
918
|
this.transport.close();
|
|
886
919
|
}
|
|
887
920
|
/** 根据 DevTools 版本选择稳定的 Page 协议实现。 */
|
|
888
921
|
configureToolInfo(info) {
|
|
889
|
-
|
|
922
|
+
const version = String(info.version ?? "");
|
|
923
|
+
this.useAppServicePageProtocol = APP_SERVICE_PAGE_PROTOCOL_VERSIONS.has(version);
|
|
924
|
+
this.useAppServicePageMethod = this.useAppServicePageProtocol || APP_SERVICE_PAGE_METHOD_VERSIONS.has(version);
|
|
890
925
|
}
|
|
891
926
|
get prefersAppServicePageProtocol() {
|
|
892
927
|
return this.useAppServicePageProtocol;
|
|
893
928
|
}
|
|
929
|
+
/** 页面方法调用独立选择协议,不影响节点查询及其组件作用域。 */
|
|
930
|
+
get prefersAppServicePageMethod() {
|
|
931
|
+
return this.useAppServicePageMethod;
|
|
932
|
+
}
|
|
894
933
|
onMessage = (message) => {
|
|
895
934
|
debugProtocol(`${dateFormat("yyyy-mm-dd HH:MM:ss:l")} ◀ RECV ${message}`);
|
|
896
935
|
const { id, method, error, result, params } = JSON.parse(message);
|
|
@@ -915,35 +954,53 @@ var Connection = class Connection extends EventEmitter {
|
|
|
915
954
|
}
|
|
916
955
|
this.callbacks.clear();
|
|
917
956
|
};
|
|
918
|
-
static create(url, timeout = CONNECT_TIMEOUT) {
|
|
957
|
+
static create(url, timeout = CONNECT_TIMEOUT, signal) {
|
|
958
|
+
signal?.throwIfAborted();
|
|
919
959
|
return new Promise((resolve, reject) => {
|
|
920
960
|
const ws = new WebSocket(url);
|
|
921
961
|
let settled = false;
|
|
922
|
-
|
|
962
|
+
let timer;
|
|
963
|
+
let onAbort;
|
|
964
|
+
let onOpen;
|
|
965
|
+
let onClose;
|
|
966
|
+
function cleanup() {
|
|
967
|
+
clearTimeout(timer);
|
|
968
|
+
signal?.removeEventListener("abort", onAbort);
|
|
969
|
+
ws.off("open", onOpen);
|
|
970
|
+
ws.off("close", onClose);
|
|
971
|
+
}
|
|
972
|
+
function fail(error) {
|
|
923
973
|
if (settled) return;
|
|
924
974
|
settled = true;
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
975
|
+
cleanup();
|
|
976
|
+
ws.terminate();
|
|
977
|
+
reject(error);
|
|
978
|
+
}
|
|
979
|
+
onAbort = () => {
|
|
980
|
+
fail(signal?.reason);
|
|
981
|
+
};
|
|
982
|
+
onClose = () => {
|
|
983
|
+
fail(/* @__PURE__ */ new Error("DevTools websocket closed before handshake completed"));
|
|
984
|
+
};
|
|
985
|
+
onOpen = () => {
|
|
929
986
|
if (settled) return;
|
|
930
987
|
settled = true;
|
|
931
|
-
|
|
988
|
+
cleanup();
|
|
932
989
|
resolve(new Connection(new Transport(ws)));
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
990
|
+
};
|
|
991
|
+
timer = setTimeout(() => fail(/* @__PURE__ */ new Error(`Timed out connecting to DevTools websocket ${url} after ${timeout}ms`)), timeout);
|
|
992
|
+
ws.on("open", onOpen);
|
|
993
|
+
ws.on("error", fail);
|
|
994
|
+
ws.on("close", onClose);
|
|
995
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
996
|
+
if (signal?.aborted) onAbort();
|
|
940
997
|
});
|
|
941
998
|
}
|
|
942
999
|
};
|
|
943
1000
|
//#endregion
|
|
944
1001
|
//#region src/headless.ts
|
|
945
1002
|
async function launchHeadlessAutomator(options) {
|
|
946
|
-
return await (await import("./launch-
|
|
1003
|
+
return await (await import("./launch-anJvfi-6.mjs")).launch({ projectPath: options.projectPath });
|
|
947
1004
|
}
|
|
948
1005
|
//#endregion
|
|
949
1006
|
//#region src/launcher/portLease.ts
|
|
@@ -1055,7 +1112,10 @@ async function enableAutomatorViaHttp(options) {
|
|
|
1055
1112
|
if (options.account) endpoint.searchParams.set("account", options.account);
|
|
1056
1113
|
if (options.ticket) endpoint.searchParams.set("ticket", options.ticket);
|
|
1057
1114
|
if (options.trustProject) endpoint.searchParams.set("trustProject", "true");
|
|
1058
|
-
const response = await fetch(endpoint, {
|
|
1115
|
+
const response = await fetch(endpoint, {
|
|
1116
|
+
redirect: "follow",
|
|
1117
|
+
signal: options.signal
|
|
1118
|
+
});
|
|
1059
1119
|
const body = await response.text();
|
|
1060
1120
|
if (!response.ok) throw new Error(`WeChat DevTools HTTP automator fallback failed with status ${response.status}`);
|
|
1061
1121
|
let result;
|
|
@@ -1064,6 +1124,7 @@ async function enableAutomatorViaHttp(options) {
|
|
|
1064
1124
|
} catch {
|
|
1065
1125
|
throw new Error("WeChat DevTools HTTP automator fallback returned invalid JSON");
|
|
1066
1126
|
}
|
|
1127
|
+
if (result && typeof result === "object" && "code" in result && Number(result.code) === 10) throw Object.assign(/* @__PURE__ */ new Error("DEVTOOLS_LOGIN_REQUIRED: need re-login"), { code: 10 });
|
|
1067
1128
|
if (result && typeof result === "object" && "autoPort" in result) {
|
|
1068
1129
|
const autoPort = Number(result.autoPort);
|
|
1069
1130
|
if (!Number.isInteger(autoPort) || autoPort <= 0 || autoPort > 65535) throw new Error("WeChat DevTools HTTP automator fallback returned invalid autoPort");
|
|
@@ -1097,8 +1158,122 @@ function resolveWechatDevtoolsBootstrapArgs(args) {
|
|
|
1097
1158
|
return bootstrapArgs;
|
|
1098
1159
|
}
|
|
1099
1160
|
//#endregion
|
|
1161
|
+
//#region src/launcher/process.ts
|
|
1162
|
+
const WINDOWS_BATCH_CLI_RE = /\.(?:bat|cmd)$/i;
|
|
1163
|
+
function shouldUseWindowsCommandShell(cliPath) {
|
|
1164
|
+
return isWindows && WINDOWS_BATCH_CLI_RE.test(cliPath);
|
|
1165
|
+
}
|
|
1166
|
+
function escapeWindowsCmdArg(arg) {
|
|
1167
|
+
const escaped = arg.replace(/"/g, "\"\"").replace(/%/g, "%%");
|
|
1168
|
+
return /[\s"&<>^|()]/.test(arg) ? `"${escaped}"` : escaped;
|
|
1169
|
+
}
|
|
1170
|
+
function resolveWindowsBatchSpawn(cliPath, args) {
|
|
1171
|
+
return {
|
|
1172
|
+
file: process.env.ComSpec || "cmd.exe",
|
|
1173
|
+
args: [
|
|
1174
|
+
"/d",
|
|
1175
|
+
"/s",
|
|
1176
|
+
"/c",
|
|
1177
|
+
`"${[cliPath, ...args].map(escapeWindowsCmdArg).join(" ")}"`
|
|
1178
|
+
]
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
/** 只持有本次 CLI 子进程句柄;退出后不通过旧 PID 查找或终止宿主。 */
|
|
1182
|
+
function spawnWechatCli(cliPath, args, cwd, scope) {
|
|
1183
|
+
scope.throwIfAborted();
|
|
1184
|
+
const state = {
|
|
1185
|
+
error: null,
|
|
1186
|
+
success: false,
|
|
1187
|
+
exited: false,
|
|
1188
|
+
output: ""
|
|
1189
|
+
};
|
|
1190
|
+
const bootstrapArgs = resolveWechatDevtoolsBootstrapArgs(args);
|
|
1191
|
+
const target = shouldUseWindowsCommandShell(cliPath) ? resolveWindowsBatchSpawn(cliPath, bootstrapArgs) : {
|
|
1192
|
+
file: cliPath,
|
|
1193
|
+
args: bootstrapArgs
|
|
1194
|
+
};
|
|
1195
|
+
const child = spawn(target.file, target.args, {
|
|
1196
|
+
stdio: [
|
|
1197
|
+
"ignore",
|
|
1198
|
+
"pipe",
|
|
1199
|
+
"pipe"
|
|
1200
|
+
],
|
|
1201
|
+
cwd: cwd || void 0,
|
|
1202
|
+
...shouldUseWindowsCommandShell(cliPath) ? {
|
|
1203
|
+
windowsHide: true,
|
|
1204
|
+
windowsVerbatimArguments: true
|
|
1205
|
+
} : {}
|
|
1206
|
+
});
|
|
1207
|
+
let released;
|
|
1208
|
+
const release = () => {
|
|
1209
|
+
if (!released) released = new Promise((resolve) => {
|
|
1210
|
+
if (state.exited || !child.pid || child.exitCode !== null || child.signalCode !== null) {
|
|
1211
|
+
resolve();
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
child.once("close", () => resolve());
|
|
1215
|
+
child.kill("SIGKILL");
|
|
1216
|
+
});
|
|
1217
|
+
return released;
|
|
1218
|
+
};
|
|
1219
|
+
const disown = scope.own(release, "cli-process");
|
|
1220
|
+
child.on("error", (error) => {
|
|
1221
|
+
state.error = error;
|
|
1222
|
+
});
|
|
1223
|
+
const output = (chunk) => {
|
|
1224
|
+
state.output = (state.output + String(chunk)).slice(-32768);
|
|
1225
|
+
};
|
|
1226
|
+
child.stdout?.on("data", output);
|
|
1227
|
+
child.stderr?.on("data", output);
|
|
1228
|
+
child.on("exit", (code, signal) => {
|
|
1229
|
+
disown();
|
|
1230
|
+
state.exited = true;
|
|
1231
|
+
if (code !== 0 || signal) state.error = /* @__PURE__ */ new Error(`DevTools cli exited unexpectedly with code ${code ?? "null"}${signal ? ` and signal ${signal}` : ""}`);
|
|
1232
|
+
else state.success = true;
|
|
1233
|
+
});
|
|
1234
|
+
child.unref();
|
|
1235
|
+
return Object.assign(state, { release });
|
|
1236
|
+
}
|
|
1237
|
+
//#endregion
|
|
1100
1238
|
//#region package.json
|
|
1101
|
-
var version = "1.
|
|
1239
|
+
var version = "1.3.0";
|
|
1240
|
+
//#endregion
|
|
1241
|
+
//#region src/appServiceHeap.ts
|
|
1242
|
+
function isRecord$1(value) {
|
|
1243
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1244
|
+
}
|
|
1245
|
+
function unsupported$1(reason) {
|
|
1246
|
+
return {
|
|
1247
|
+
status: "unsupported",
|
|
1248
|
+
source: "appservice-cdp-runtime",
|
|
1249
|
+
reason
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
/** 只读取 AppService 当前 V8 堆,不调用 GC,也不把协议故障或空响应当成零内存。 */
|
|
1253
|
+
async function readAppServiceHeapUsage(send, options = {}) {
|
|
1254
|
+
const timeout = options.timeout ?? 2500;
|
|
1255
|
+
if (!Number.isFinite(timeout) || timeout <= 0) throw new TypeError("AppService heap timeout must be finite and positive");
|
|
1256
|
+
let response;
|
|
1257
|
+
try {
|
|
1258
|
+
response = await send("App.CDPCommand", {
|
|
1259
|
+
domain: "Runtime",
|
|
1260
|
+
method: "getHeapUsage",
|
|
1261
|
+
params: {}
|
|
1262
|
+
}, { timeout });
|
|
1263
|
+
} catch (error) {
|
|
1264
|
+
if (error instanceof Error && (error.message === "appservice App.CDPCommand unimplemented" || error.message === "Method not implemented.")) return unsupported$1("protocol-unimplemented");
|
|
1265
|
+
if (error instanceof Error && error.message === "'Runtime.getHeapUsage' wasn't found") return unsupported$1("method-not-found");
|
|
1266
|
+
throw error;
|
|
1267
|
+
}
|
|
1268
|
+
if (isRecord$1(response) && isRecord$1(response.error) && response.error.code === -32601) return unsupported$1("method-not-found");
|
|
1269
|
+
if (!isRecord$1(response) || response.error || response.exceptionDetails || typeof response.usedSize !== "number" || !Number.isFinite(response.usedSize) || response.usedSize < 0 || typeof response.totalSize !== "number" || !Number.isFinite(response.totalSize) || response.totalSize < response.usedSize) throw new Error("Invalid AppService Runtime.getHeapUsage response: expected finite non-negative usedSize <= totalSize");
|
|
1270
|
+
return {
|
|
1271
|
+
status: "available",
|
|
1272
|
+
source: "appservice-cdp-runtime",
|
|
1273
|
+
usedSize: response.usedSize,
|
|
1274
|
+
totalSize: response.totalSize
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1102
1277
|
//#endregion
|
|
1103
1278
|
//#region src/Native.ts
|
|
1104
1279
|
/** Native 的实现。 */
|
|
@@ -1149,6 +1324,25 @@ var Native = class extends EventEmitter {
|
|
|
1149
1324
|
}
|
|
1150
1325
|
};
|
|
1151
1326
|
//#endregion
|
|
1327
|
+
//#region src/navigation/query.ts
|
|
1328
|
+
/** 仅核对请求显式携带的参数,保留宿主附加参数及其原始编码形式。 */
|
|
1329
|
+
function matchesRouteQuery(actual, expected) {
|
|
1330
|
+
const expectedKeys = Object.keys(expected);
|
|
1331
|
+
if (!expectedKeys.length) return true;
|
|
1332
|
+
if (!actual || typeof actual !== "object" || Array.isArray(actual)) return false;
|
|
1333
|
+
const query = actual;
|
|
1334
|
+
return expectedKeys.every((key) => {
|
|
1335
|
+
if (!Object.prototype.hasOwnProperty.call(query, key) || query[key] == null) return false;
|
|
1336
|
+
const value = String(query[key]);
|
|
1337
|
+
if (value === expected[key]) return true;
|
|
1338
|
+
try {
|
|
1339
|
+
return decodeURIComponent(value) === expected[key];
|
|
1340
|
+
} catch {
|
|
1341
|
+
return false;
|
|
1342
|
+
}
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
//#endregion
|
|
1152
1346
|
//#region src/pageRouteFallback.ts
|
|
1153
1347
|
function normalizeFallbackIdPart(value) {
|
|
1154
1348
|
return encodeURIComponent(value).replace(/%/g, "_");
|
|
@@ -1420,7 +1614,7 @@ var Page = class Page {
|
|
|
1420
1614
|
async $(selector, options = {}) {
|
|
1421
1615
|
if (options.routeOnly || options.fallback !== false && this.preferAppServicePageProtocol) return await this.queryRouteElement(selector, options);
|
|
1422
1616
|
try {
|
|
1423
|
-
const element = await this.
|
|
1617
|
+
const element = await this.queryPageElements("Page.getElement", selector, options);
|
|
1424
1618
|
return Element.create(this.connection, {
|
|
1425
1619
|
...element,
|
|
1426
1620
|
pageId: this.id
|
|
@@ -1436,7 +1630,7 @@ var Page = class Page {
|
|
|
1436
1630
|
async $$(selector, options = {}) {
|
|
1437
1631
|
if (options.routeOnly || options.fallback !== false && this.preferAppServicePageProtocol) return await this.queryRouteElements(selector, options);
|
|
1438
1632
|
try {
|
|
1439
|
-
const { elements } = await this.
|
|
1633
|
+
const { elements } = await this.queryPageElements("Page.getElements", selector, options);
|
|
1440
1634
|
return elements.map((element) => {
|
|
1441
1635
|
return Element.create(this.connection, {
|
|
1442
1636
|
...element,
|
|
@@ -1449,6 +1643,15 @@ var Page = class Page {
|
|
|
1449
1643
|
return await this.queryRouteElements(selector, options);
|
|
1450
1644
|
}
|
|
1451
1645
|
}
|
|
1646
|
+
async queryPageElements(method, selector, options) {
|
|
1647
|
+
const timeout = options.timeout ?? PAGE_QUERY_TIMEOUT;
|
|
1648
|
+
try {
|
|
1649
|
+
return await this.send(method, { selector }, { timeout });
|
|
1650
|
+
} catch (error) {
|
|
1651
|
+
if (options.fallback === false || !isRecoverablePageProtocolError(error, method)) throw error;
|
|
1652
|
+
return await this.send(method, { selector }, { timeout });
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1452
1655
|
async getElementByXpath(selector, options = {}) {
|
|
1453
1656
|
try {
|
|
1454
1657
|
const element = await this.send("Page.getElementByXpath", { selector }, { timeout: options.timeout ?? PAGE_QUERY_TIMEOUT });
|
|
@@ -2084,7 +2287,7 @@ var Page = class Page {
|
|
|
2084
2287
|
return await this.callMethodWithOptions(method, {}, ...args);
|
|
2085
2288
|
}
|
|
2086
2289
|
async callMethodWithOptions(method, options = {}, ...args) {
|
|
2087
|
-
if (options.routeOnly || options.fallback !== false && this.preferAppServicePageProtocol) return await this.callRouteMethod(method, args, options.timeout);
|
|
2290
|
+
if (options.routeOnly || options.fallback !== false && (this.preferAppServicePageProtocol || this.connection.prefersAppServicePageMethod)) return await this.callRouteMethod(method, args, options.timeout);
|
|
2088
2291
|
try {
|
|
2089
2292
|
return (await this.send("Page.callMethod", {
|
|
2090
2293
|
method,
|
|
@@ -2404,7 +2607,7 @@ function isPluginNavigationUrl(value) {
|
|
|
2404
2607
|
}
|
|
2405
2608
|
function matchesRouteChange(options) {
|
|
2406
2609
|
const normalizedCurrentPath = normalizeRoutePath(options.currentPath);
|
|
2407
|
-
if (!options.expectedRoute || normalizedCurrentPath === options.expectedRoute) return
|
|
2610
|
+
if (!options.expectedRoute || normalizedCurrentPath === options.expectedRoute) return matchesRouteQuery(options.currentQuery, options.expectedQuery);
|
|
2408
2611
|
if (!isPluginNavigationUrl(options.requestedUrl) || !isPluginPath(options.currentPath)) return false;
|
|
2409
2612
|
return normalizedCurrentPath !== normalizeRoutePath(options.previousPath);
|
|
2410
2613
|
}
|
|
@@ -2582,6 +2785,10 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2582
2785
|
}, sendOptions);
|
|
2583
2786
|
return result;
|
|
2584
2787
|
}
|
|
2788
|
+
/** 探测并读取 AppService JS 堆;明确不支持时返回原因,连接故障与无效响应继续抛出。 */
|
|
2789
|
+
async getAppServiceHeapUsage(options = {}) {
|
|
2790
|
+
return await readAppServiceHeapUsage((method, params, sendOptions) => this.send(method, params, sendOptions), options);
|
|
2791
|
+
}
|
|
2585
2792
|
async pageScrollTo(scrollTop) {
|
|
2586
2793
|
await this.callWxMethod("pageScrollTo", {
|
|
2587
2794
|
scrollTop,
|
|
@@ -2677,7 +2884,7 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2677
2884
|
if (sdkVersion !== "dev" && cmpVersion(sdkVersion, "2.7.3") < 0) throw new Error(`SDKVersion is currently ${sdkVersion}, while automator(${version}) requires at least version 2.7.3`);
|
|
2678
2885
|
}
|
|
2679
2886
|
/**
|
|
2680
|
-
* @description
|
|
2887
|
+
* @description 等待原生协议返回真实页面标识和路径,避免返回半就绪自动化会话。
|
|
2681
2888
|
*/
|
|
2682
2889
|
async waitForAppReady(timeout = APP_READY_TIMEOUT) {
|
|
2683
2890
|
const startedAt = Date.now();
|
|
@@ -2685,13 +2892,15 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2685
2892
|
while (Date.now() - startedAt < timeout) {
|
|
2686
2893
|
const remainingTimeout = timeout - (Date.now() - startedAt);
|
|
2687
2894
|
try {
|
|
2688
|
-
await this.send("App.
|
|
2689
|
-
return;
|
|
2895
|
+
const page = await this.send("App.getCurrentPage", {}, { timeout: Math.min(APP_READY_PROBE_TIMEOUT, remainingTimeout) });
|
|
2896
|
+
if (page && typeof page === "object" && "pageId" in page && typeof page.pageId === "number" && Number.isSafeInteger(page.pageId) && page.pageId >= 0 && "path" in page && typeof page.path === "string" && page.path.trim()) return;
|
|
2897
|
+
lastError = /* @__PURE__ */ new Error("App.getCurrentPage returned no ready page with a valid pageId and path");
|
|
2690
2898
|
} catch (error) {
|
|
2899
|
+
if (!isCurrentPageProtocolTimeout(error) && !isPageMetaMissingError(error) && !isCurrentFrameTimedOutError(error)) throw error;
|
|
2691
2900
|
lastError = error;
|
|
2692
|
-
const remainingAfterProbe = timeout - (Date.now() - startedAt);
|
|
2693
|
-
if (remainingAfterProbe > 0) await sleep(Math.min(APP_READY_POLL_DELAY, remainingAfterProbe));
|
|
2694
2901
|
}
|
|
2902
|
+
const remainingAfterProbe = timeout - (Date.now() - startedAt);
|
|
2903
|
+
if (remainingAfterProbe > 0) await sleep(Math.min(APP_READY_POLL_DELAY, remainingAfterProbe));
|
|
2695
2904
|
}
|
|
2696
2905
|
throw lastError instanceof Error ? lastError : /* @__PURE__ */ new Error(`Timed out waiting App domain readiness after ${timeout}ms`);
|
|
2697
2906
|
}
|
|
@@ -2782,9 +2991,11 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2782
2991
|
logChangeRouteDebug(`poll method=${method} url=${url ?? "<none>"} current=${page?.path ?? "<none>"}`);
|
|
2783
2992
|
if (matchesRouteChange({
|
|
2784
2993
|
currentPath: page?.path,
|
|
2994
|
+
currentQuery: page?.query,
|
|
2785
2995
|
previousPath: currentPage?.path,
|
|
2786
2996
|
requestedUrl: url,
|
|
2787
|
-
expectedRoute
|
|
2997
|
+
expectedRoute,
|
|
2998
|
+
expectedQuery: expectedRouteQuery
|
|
2788
2999
|
})) {
|
|
2789
3000
|
logChangeRouteDebug(`ready method=${method} url=${url ?? "<none>"} current=${page?.path ?? "<none>"}`);
|
|
2790
3001
|
return page;
|
|
@@ -2801,9 +3012,11 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2801
3012
|
logChangeRouteDebug(`stack method=${method} url=${url ?? "<none>"} current=${stackTop.path}`);
|
|
2802
3013
|
if (matchesRouteChange({
|
|
2803
3014
|
currentPath: stackTop.path,
|
|
3015
|
+
currentQuery: stackTop.query,
|
|
2804
3016
|
previousPath: currentPage?.path,
|
|
2805
3017
|
requestedUrl: url,
|
|
2806
|
-
expectedRoute
|
|
3018
|
+
expectedRoute,
|
|
3019
|
+
expectedQuery: expectedRouteQuery
|
|
2807
3020
|
})) {
|
|
2808
3021
|
logChangeRouteDebug(`stack-ready method=${method} url=${url ?? "<none>"} current=${stackTop.path}`);
|
|
2809
3022
|
return stackTop;
|
|
@@ -3346,16 +3559,7 @@ const VERSION_CHECK_TIMEOUT = 3e4;
|
|
|
3346
3559
|
const AUTOMATOR_LAUNCH_RETRIES = 3;
|
|
3347
3560
|
const DEFAULT_RUNTIME_PROVIDER_ENV = "WEAPP_VITE_AUTOMATOR_RUNTIME_PROVIDER";
|
|
3348
3561
|
const LEGACY_RUNTIME_PROVIDER_ENV = "WEAPP_VITE_E2E_RUNTIME_PROVIDER";
|
|
3349
|
-
const EXTENSION_CONTEXT_INVALIDATED_RE = /Extension context invalidated/i;
|
|
3350
|
-
const RETRYABLE_LAUNCH_PORT_RE = /Wait timed out after \d+ ms|Failed connecting to ws:\/\/127\.0\.0\.1:\d+|Failed connecting to devtools websocket endpoint|Failed to launch wechat web devTools, please make sure cliPath is correctly specified/i;
|
|
3351
|
-
const WINDOWS_BATCH_CLI_RE = /\.(?:bat|cmd)$/i;
|
|
3352
3562
|
let localhostListenPatched = false;
|
|
3353
|
-
function isExtensionContextInvalidatedError(error) {
|
|
3354
|
-
return error instanceof Error && EXTENSION_CONTEXT_INVALIDATED_RE.test(error.message);
|
|
3355
|
-
}
|
|
3356
|
-
function isRetryableAutomatorPortLaunchError(error) {
|
|
3357
|
-
return error instanceof Error && RETRYABLE_LAUNCH_PORT_RE.test(error.message);
|
|
3358
|
-
}
|
|
3359
3563
|
function retainPortLeaseUntilSessionClose(miniProgram, portLease) {
|
|
3360
3564
|
let released = false;
|
|
3361
3565
|
const release = async () => {
|
|
@@ -3400,25 +3604,6 @@ function patchNetListenToLoopback() {
|
|
|
3400
3604
|
return rawListen.apply(this, args);
|
|
3401
3605
|
};
|
|
3402
3606
|
}
|
|
3403
|
-
/** IConnectOptions 的类型定义。 */
|
|
3404
|
-
function shouldUseWindowsCommandShell(cliPath) {
|
|
3405
|
-
return isWindows && WINDOWS_BATCH_CLI_RE.test(cliPath);
|
|
3406
|
-
}
|
|
3407
|
-
function escapeWindowsCmdArg(arg) {
|
|
3408
|
-
const escaped = arg.replace(/"/g, "\"\"").replace(/%/g, "%%");
|
|
3409
|
-
return /[\s"&<>^|()]/.test(arg) ? `"${escaped}"` : escaped;
|
|
3410
|
-
}
|
|
3411
|
-
function resolveWindowsBatchSpawn(cliPath, args) {
|
|
3412
|
-
return {
|
|
3413
|
-
file: process.env.ComSpec || "cmd.exe",
|
|
3414
|
-
args: [
|
|
3415
|
-
"/d",
|
|
3416
|
-
"/s",
|
|
3417
|
-
"/c",
|
|
3418
|
-
`"${[cliPath, ...args].map(escapeWindowsCmdArg).join(" ")}"`
|
|
3419
|
-
]
|
|
3420
|
-
};
|
|
3421
|
-
}
|
|
3422
3607
|
function resolveRuntimeProvider(options) {
|
|
3423
3608
|
return options.runtimeProvider || process.env[DEFAULT_RUNTIME_PROVIDER_ENV] || process.env[LEGACY_RUNTIME_PROVIDER_ENV] || "devtools";
|
|
3424
3609
|
}
|
|
@@ -3431,88 +3616,62 @@ var Launcher = class {
|
|
|
3431
3616
|
return await launchHeadlessAutomator({ projectPath: options.projectPath });
|
|
3432
3617
|
}
|
|
3433
3618
|
patchNetListenToLoopback();
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3619
|
+
return await new OperationLifecycle(options.timeout ?? DEFAULT_TIMEOUT, "automator launch", options.signal).run(async (scope) => {
|
|
3620
|
+
const attempts = options.port ? 1 : AUTOMATOR_LAUNCH_RETRIES;
|
|
3621
|
+
for (let attempt = 1;; attempt += 1) {
|
|
3622
|
+
scope.attempt();
|
|
3623
|
+
try {
|
|
3624
|
+
return await scope.step(() => this.launchWechatDevtools(options, scope));
|
|
3625
|
+
} catch (error) {
|
|
3626
|
+
scope.recordFailure(error);
|
|
3627
|
+
scope.throwIfAborted();
|
|
3628
|
+
if (!isRecoverableOperationError(error) || attempt >= attempts) throw error;
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
});
|
|
3443
3632
|
}
|
|
3444
|
-
async launchWechatDevtools(options) {
|
|
3445
|
-
const { cliPath = await this.resolveCliPath(),
|
|
3446
|
-
|
|
3447
|
-
const portLease = await acquireAutomatorPortLease(options.port)
|
|
3633
|
+
async launchWechatDevtools(options, scope) {
|
|
3634
|
+
const { cliPath = await scope.step(() => this.resolveCliPath(), { stage: "resolve-cli" }), projectConfig = {}, ticket = "", cwd = "", account = "", trustProject = false } = options;
|
|
3635
|
+
const { args = [], projectPath } = options;
|
|
3636
|
+
const portLease = await scope.step(() => acquireAutomatorPortLease(options.port), {
|
|
3637
|
+
stage: "port-lease",
|
|
3638
|
+
disposeLate: (lease) => lease.release()
|
|
3639
|
+
});
|
|
3640
|
+
const rawReleaseLease = portLease.release.bind(portLease);
|
|
3641
|
+
let releasedLease;
|
|
3642
|
+
portLease.release = () => releasedLease ??= rawReleaseLease();
|
|
3643
|
+
const disownLease = scope.own(() => portLease.release(), "port-lease");
|
|
3448
3644
|
let releasePortLeaseOnExit = true;
|
|
3645
|
+
let releaseChild = async () => {};
|
|
3449
3646
|
try {
|
|
3450
3647
|
const port = portLease.port;
|
|
3451
3648
|
if (!cliPath) throw new Error("Wechat web devTools not found, please specify cliPath option");
|
|
3452
3649
|
if (isWindows && endWith(cliPath, ".exe")) throw new Error("cliPath is not correct, it's usually named as 'cli' or 'cli.bat'");
|
|
3453
3650
|
if (!projectPath) throw new Error("projectPath is not provided");
|
|
3454
3651
|
const resolvedProjectPath = isRelative(projectPath) ? path.resolve(projectPath) : projectPath;
|
|
3455
|
-
if (
|
|
3456
|
-
if (!
|
|
3457
|
-
|
|
3458
|
-
let processError = null;
|
|
3459
|
-
let processExitCode = null;
|
|
3460
|
-
let processSignal = null;
|
|
3461
|
-
let successfulCliExitSettled = false;
|
|
3652
|
+
if (!await scope.step(() => import("node:fs/promises").then((fs) => fs.access(resolvedProjectPath).then(() => true).catch(() => false)), { stage: "project" })) throw new Error(`Project path ${resolvedProjectPath} doesn't exist`);
|
|
3653
|
+
if (!isEmpty(projectConfig)) await scope.step(() => this.extendProjectConfig(projectConfig, resolvedProjectPath), { stage: "project-config" });
|
|
3654
|
+
scope.throwIfAborted();
|
|
3462
3655
|
let httpFallbackAttempted = false;
|
|
3463
3656
|
let httpFallbackError = null;
|
|
3464
3657
|
let targetPort = port;
|
|
3465
|
-
const
|
|
3466
|
-
|
|
3467
|
-
try {
|
|
3468
|
-
const spawnTarget = shouldUseWindowsCommandShell(cliPath) ? resolveWindowsBatchSpawn(cliPath, args) : {
|
|
3469
|
-
file: cliPath,
|
|
3470
|
-
args
|
|
3471
|
-
};
|
|
3472
|
-
const child = spawn(spawnTarget.file, spawnTarget.args, {
|
|
3473
|
-
stdio: [
|
|
3474
|
-
"ignore",
|
|
3475
|
-
"pipe",
|
|
3476
|
-
"pipe"
|
|
3477
|
-
],
|
|
3478
|
-
cwd: cwd || void 0,
|
|
3479
|
-
...shouldUseWindowsCommandShell(cliPath) ? {
|
|
3480
|
-
windowsHide: true,
|
|
3481
|
-
windowsVerbatimArguments: true
|
|
3482
|
-
} : {}
|
|
3483
|
-
});
|
|
3484
|
-
child.on("error", (error) => {
|
|
3485
|
-
processError = error;
|
|
3486
|
-
});
|
|
3487
|
-
child.stdout?.on("data", (chunk) => {
|
|
3488
|
-
cliOutput.push(String(chunk));
|
|
3489
|
-
});
|
|
3490
|
-
child.stderr?.on("data", (chunk) => {
|
|
3491
|
-
cliOutput.push(String(chunk));
|
|
3492
|
-
});
|
|
3493
|
-
child.on("exit", (code, signal) => {
|
|
3494
|
-
processExitCode = code;
|
|
3495
|
-
processSignal = signal;
|
|
3496
|
-
if (code !== 0 || signal) processError = /* @__PURE__ */ new Error(`DevTools cli exited unexpectedly with code ${code ?? "null"}${signal ? ` and signal ${signal}` : ""}`);
|
|
3497
|
-
else successfulCliExitSettled = true;
|
|
3498
|
-
});
|
|
3499
|
-
child.unref();
|
|
3500
|
-
} catch (error) {
|
|
3501
|
-
processError = error;
|
|
3502
|
-
}
|
|
3658
|
+
const cli = spawnWechatCli(cliPath, args, cwd, scope);
|
|
3659
|
+
releaseChild = cli.release;
|
|
3503
3660
|
let miniProgram = null;
|
|
3504
3661
|
let lastConnectError = null;
|
|
3505
|
-
const
|
|
3506
|
-
|
|
3507
|
-
|
|
3662
|
+
const resolveRemainingTimeout = () => scope.remainingMs();
|
|
3663
|
+
await scope.step(() => waitUntil(async () => {
|
|
3664
|
+
scope.throwIfAborted();
|
|
3508
3665
|
try {
|
|
3509
|
-
if (
|
|
3510
|
-
if (
|
|
3511
|
-
|
|
3666
|
+
if (cli.error) return true;
|
|
3667
|
+
if (cli.success && readWechatLoginState(cli.output) === false) throw Object.assign(/* @__PURE__ */ new Error("DEVTOOLS_LOGIN_REQUIRED: need re-login"), { code: 10 });
|
|
3668
|
+
if (cli.success && !httpFallbackAttempted) {
|
|
3669
|
+
const servicePort = extractWechatDevtoolsServicePort(cli.output);
|
|
3512
3670
|
if (servicePort) {
|
|
3513
3671
|
httpFallbackAttempted = true;
|
|
3514
3672
|
try {
|
|
3515
3673
|
targetPort = await enableAutomatorViaHttp({
|
|
3674
|
+
signal: scope.signal,
|
|
3516
3675
|
account,
|
|
3517
3676
|
autoPort: port,
|
|
3518
3677
|
projectPath: resolvedProjectPath,
|
|
@@ -3528,43 +3687,59 @@ var Launcher = class {
|
|
|
3528
3687
|
}
|
|
3529
3688
|
const connectTimeout = resolveRemainingTimeout();
|
|
3530
3689
|
if (connectTimeout <= 0) return false;
|
|
3531
|
-
const candidate = await this.connectTool({
|
|
3690
|
+
const candidate = await scope.step(() => this.connectTool({
|
|
3691
|
+
signal: scope.signal,
|
|
3532
3692
|
timeout: Math.min(3e3, connectTimeout),
|
|
3533
3693
|
wsEndpoint: `ws://127.0.0.1:${targetPort}`
|
|
3694
|
+
}), {
|
|
3695
|
+
stage: "websocket",
|
|
3696
|
+
disposeLate: (program) => program.disconnect()
|
|
3534
3697
|
});
|
|
3698
|
+
let releasedCandidate = false;
|
|
3699
|
+
const releaseCandidate = () => {
|
|
3700
|
+
if (!releasedCandidate) {
|
|
3701
|
+
releasedCandidate = true;
|
|
3702
|
+
candidate.disconnect();
|
|
3703
|
+
}
|
|
3704
|
+
};
|
|
3705
|
+
const disownCandidate = scope.own(releaseCandidate, "websocket");
|
|
3535
3706
|
try {
|
|
3536
3707
|
const checkVersionTimeout = resolveRemainingTimeout();
|
|
3537
3708
|
if (checkVersionTimeout <= 0) {
|
|
3538
|
-
|
|
3709
|
+
releaseCandidate();
|
|
3539
3710
|
return false;
|
|
3540
3711
|
}
|
|
3541
|
-
await candidate.checkVersion(Math.min(VERSION_CHECK_TIMEOUT, checkVersionTimeout));
|
|
3712
|
+
await scope.step(() => candidate.checkVersion(Math.min(VERSION_CHECK_TIMEOUT, checkVersionTimeout)), { stage: "version" });
|
|
3542
3713
|
if (typeof candidate.waitForAppReady === "function") {
|
|
3543
3714
|
const appReadyTimeout = resolveRemainingTimeout();
|
|
3544
3715
|
if (appReadyTimeout <= 0) {
|
|
3545
|
-
|
|
3716
|
+
releaseCandidate();
|
|
3546
3717
|
return false;
|
|
3547
3718
|
}
|
|
3548
|
-
await candidate.waitForAppReady(appReadyTimeout);
|
|
3719
|
+
await scope.step(() => candidate.waitForAppReady(appReadyTimeout), { stage: "app-ready" });
|
|
3549
3720
|
}
|
|
3550
3721
|
} catch (error) {
|
|
3551
|
-
|
|
3722
|
+
disownCandidate();
|
|
3723
|
+
releaseCandidate();
|
|
3552
3724
|
lastConnectError = error;
|
|
3553
|
-
if (
|
|
3725
|
+
if (isRecoverableOperationError(error)) return false;
|
|
3554
3726
|
throw error;
|
|
3555
3727
|
}
|
|
3556
3728
|
miniProgram = candidate;
|
|
3557
3729
|
return true;
|
|
3558
3730
|
} catch (error) {
|
|
3559
3731
|
lastConnectError = error;
|
|
3732
|
+
scope.recordFailure(error);
|
|
3733
|
+
scope.throwIfAborted();
|
|
3734
|
+
if (!isRecoverableOperationError(error)) throw error;
|
|
3560
3735
|
return false;
|
|
3561
3736
|
}
|
|
3562
|
-
},
|
|
3737
|
+
}, scope.remainingMs(), 1e3, scope.signal), { stage: "readiness" });
|
|
3563
3738
|
if (!miniProgram) {
|
|
3564
3739
|
if (httpFallbackError) throw httpFallbackError;
|
|
3565
|
-
if (
|
|
3740
|
+
if (cli.error) throw new Error("Failed to launch wechat web devTools, please make sure cliPath is correctly specified", { cause: cli.error });
|
|
3566
3741
|
if (lastConnectError) throw lastConnectError;
|
|
3567
|
-
if (
|
|
3742
|
+
if (cli.exited) throw new Error("Failed to launch wechat web devTools, please make sure http port is open");
|
|
3568
3743
|
throw new Error("Failed connecting to devtools websocket endpoint");
|
|
3569
3744
|
}
|
|
3570
3745
|
const resolvedMiniProgram = miniProgram;
|
|
@@ -3574,33 +3749,32 @@ var Launcher = class {
|
|
|
3574
3749
|
wsEndpoint: `ws://127.0.0.1:${targetPort}`
|
|
3575
3750
|
});
|
|
3576
3751
|
releasePortLeaseOnExit = !retainPortLeaseUntilSessionClose(resolvedMiniProgram, portLease);
|
|
3577
|
-
|
|
3752
|
+
disownLease();
|
|
3578
3753
|
return resolvedMiniProgram;
|
|
3579
3754
|
} finally {
|
|
3580
|
-
|
|
3755
|
+
await releaseChild();
|
|
3756
|
+
if (releasePortLeaseOnExit) {
|
|
3757
|
+
disownLease();
|
|
3758
|
+
await portLease.release();
|
|
3759
|
+
}
|
|
3581
3760
|
}
|
|
3582
3761
|
}
|
|
3583
3762
|
async connect(options) {
|
|
3584
3763
|
if (normalizePlatform(options.platform) === "swan") return await new SwanLauncher().connect(options);
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3764
|
+
return await new OperationLifecycle(options.timeout ?? DEFAULT_TIMEOUT, "automator connect", options.signal).run(async (scope) => {
|
|
3765
|
+
scope.attempt();
|
|
3766
|
+
const miniProgram = await scope.step(() => this.connectTool({
|
|
3767
|
+
...options,
|
|
3768
|
+
signal: scope.signal,
|
|
3769
|
+
timeout: scope.remainingMs()
|
|
3770
|
+
}), {
|
|
3771
|
+
stage: "websocket",
|
|
3772
|
+
disposeLate: (program) => program.disconnect()
|
|
3773
|
+
});
|
|
3774
|
+
scope.own(() => miniProgram.disconnect(), "websocket");
|
|
3775
|
+
await scope.step(() => miniProgram.checkVersion(scope.remainingMs()), { stage: "version" });
|
|
3595
3776
|
return miniProgram;
|
|
3596
|
-
}
|
|
3597
|
-
try {
|
|
3598
|
-
miniProgram.disconnect();
|
|
3599
|
-
} catch (cleanupError) {
|
|
3600
|
-
throw new AggregateError([error, cleanupError], "Automator connection and cleanup both failed", { cause: error });
|
|
3601
|
-
}
|
|
3602
|
-
throw error;
|
|
3603
|
-
}
|
|
3777
|
+
});
|
|
3604
3778
|
}
|
|
3605
3779
|
async extendProjectConfig(projectConfig, projectPath) {
|
|
3606
3780
|
const projectConfigPath = path.resolve(projectPath, "project.config.json");
|
|
@@ -3612,9 +3786,9 @@ var Launcher = class {
|
|
|
3612
3786
|
}
|
|
3613
3787
|
async connectTool(options) {
|
|
3614
3788
|
try {
|
|
3615
|
-
return new MiniProgram(await Connection.create(options.wsEndpoint, options.timeout));
|
|
3616
|
-
} catch {
|
|
3617
|
-
throw new Error(`Failed connecting to ${options.wsEndpoint}, check if target project window is opened with automation enabled
|
|
3789
|
+
return new MiniProgram(await Connection.create(options.wsEndpoint, options.timeout, options.signal));
|
|
3790
|
+
} catch (cause) {
|
|
3791
|
+
throw new Error(`Failed connecting to ${options.wsEndpoint}, check if target project window is opened with automation enabled`, { cause });
|
|
3618
3792
|
}
|
|
3619
3793
|
}
|
|
3620
3794
|
async resolveCliPath() {
|
|
@@ -3656,4 +3830,4 @@ var Automator = class {
|
|
|
3656
3830
|
*/
|
|
3657
3831
|
const SmartappAutomator = smartappAutomatorRuntime;
|
|
3658
3832
|
//#endregion
|
|
3659
|
-
export { Automator, Connection, ContextElement, CustomElement, Element, InputElement, Launcher, MiniProgram, MovableViewElement, Native, Page, ScrollViewElement, SliderElement, SmartappAutomator, SwiperElement, SwitchElement, TextareaElement, Transport, decodeQrCode, extractPluginId, isPluginPath, normalizePlatform, printQrCode };
|
|
3833
|
+
export { Automator, Connection, ContextElement, CustomElement, Element, InputElement, Launcher, MiniProgram, MovableViewElement, Native, OPERATION_TIMEOUT_CODE, OperationLifecycle, Page, ScrollViewElement, SliderElement, SmartappAutomator, SwiperElement, SwitchElement, TextareaElement, Transport, acquireAutomatorPortLease, classifyOperationError, decodeQrCode, extractPluginId, isPluginPath, isRecoverableOperationError, normalizePlatform, printQrCode, readWechatLoginState };
|