@weapp-vite/miniprogram-automator 1.3.0 → 1.5.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 +74 -0
- package/dist/index.d.mts +39 -54
- package/dist/index.mjs +742 -155
- package/dist/{launch-anJvfi-6.mjs → launch-DuW7r68J.mjs} +2217 -1027
- package/dist/operation.d.mts +1 -7
- package/dist/operation.mjs +3 -6
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -6,12 +6,13 @@ 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
|
+
import { stripVTControlCharacters } from "node:util";
|
|
9
10
|
import fs from "node:fs/promises";
|
|
10
11
|
import os from "node:os";
|
|
11
12
|
import { spawn } from "node:child_process";
|
|
12
13
|
import { decodeQrCodeFromBase64, renderTerminalQrCode } from "@weapp-vite/qr";
|
|
13
14
|
import { Buffer } from "node:buffer";
|
|
14
|
-
|
|
15
|
+
// src/internal/compat/async.ts
|
|
15
16
|
/**
|
|
16
17
|
* @file 异步控制工具。
|
|
17
18
|
*/
|
|
@@ -45,8 +46,7 @@ async function waitUntil(condition, timeout = 0, interval = 250, signal) {
|
|
|
45
46
|
await sleep$1(Math.min(interval, remaining), signal);
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
-
//#region src/internal/compat/format.ts
|
|
49
|
+
// src/internal/compat/format.ts
|
|
50
50
|
/**
|
|
51
51
|
* @file 标识与格式化工具。
|
|
52
52
|
*/
|
|
@@ -62,8 +62,7 @@ function dateFormat(mask, value = /* @__PURE__ */ new Date()) {
|
|
|
62
62
|
function uuid() {
|
|
63
63
|
return randomUUID();
|
|
64
64
|
}
|
|
65
|
-
|
|
66
|
-
//#region src/internal/compat/network.ts
|
|
65
|
+
// src/internal/compat/network.ts
|
|
67
66
|
/**
|
|
68
67
|
* @file 网络与环境工具。
|
|
69
68
|
*/
|
|
@@ -97,8 +96,7 @@ function isRelative(value) {
|
|
|
97
96
|
return !absolutePathPattern.test(value);
|
|
98
97
|
}
|
|
99
98
|
const isWindows = process.platform === "win32";
|
|
100
|
-
|
|
101
|
-
//#region src/internal/compat/typeGuards.ts
|
|
99
|
+
// src/internal/compat/typeGuards.ts
|
|
102
100
|
/**
|
|
103
101
|
* @file 基础类型守卫工具。
|
|
104
102
|
*/
|
|
@@ -126,8 +124,7 @@ function isPlainObject(value) {
|
|
|
126
124
|
const prototype = Object.getPrototypeOf(value);
|
|
127
125
|
return prototype === null || prototype === Object.prototype;
|
|
128
126
|
}
|
|
129
|
-
|
|
130
|
-
//#region src/internal/compat/object.ts
|
|
127
|
+
// src/internal/compat/object.ts
|
|
131
128
|
/**
|
|
132
129
|
* @file 对象与序列化工具。
|
|
133
130
|
*/
|
|
@@ -194,8 +191,7 @@ function isEmpty(value) {
|
|
|
194
191
|
function stringify(value) {
|
|
195
192
|
return stableSerialize(value);
|
|
196
193
|
}
|
|
197
|
-
|
|
198
|
-
//#region src/internal/compat/strings.ts
|
|
194
|
+
// src/internal/compat/strings.ts
|
|
199
195
|
/**
|
|
200
196
|
* @file 字符串与版本处理工具。
|
|
201
197
|
*/
|
|
@@ -234,8 +230,303 @@ function cmpVersion(versionA, versionB) {
|
|
|
234
230
|
}
|
|
235
231
|
return 0;
|
|
236
232
|
}
|
|
237
|
-
|
|
238
|
-
|
|
233
|
+
// src/componentCaller/runtime.ts
|
|
234
|
+
/** 在同一个 Runtime 上下文持有真实 caller,不转用其他 ContextManager 的 token。 */
|
|
235
|
+
function createComponentCallerSession(expected, observerTimeout) {
|
|
236
|
+
const root = this;
|
|
237
|
+
const adapter = root.__glassEaselAdapter__;
|
|
238
|
+
const environment = adapter?.getEnv?.();
|
|
239
|
+
if (typeof environment?.getAdapterPage !== "function") throw new TypeError("Component caller backend selection is unavailable");
|
|
240
|
+
if (environment.getAdapterPage(expected.pageId)) return {
|
|
241
|
+
backend: "glass-easel",
|
|
242
|
+
dispose: () => ({
|
|
243
|
+
state: "disposed",
|
|
244
|
+
added: false,
|
|
245
|
+
removed: false
|
|
246
|
+
})
|
|
247
|
+
};
|
|
248
|
+
const bridge = root.WeixinJSBridge;
|
|
249
|
+
if (!bridge || typeof bridge.addSubscribeListener !== "function" || typeof bridge.removeSubscribeListener !== "function" || !adapter || typeof adapter.getVirtualDOM !== "function") throw new Error("Exact component caller bridge is unavailable");
|
|
250
|
+
const add = bridge.addSubscribeListener;
|
|
251
|
+
const remove = bridge.removeSubscribeListener;
|
|
252
|
+
const subscribe = bridge.subscribe;
|
|
253
|
+
const virtualDOM = adapter.getVirtualDOM();
|
|
254
|
+
const exparser = virtualDOM?.getExparser?.();
|
|
255
|
+
if (typeof virtualDOM?.getNodeById !== "function" || typeof virtualDOM.getNodeId !== "function" || typeof virtualDOM.getPageId !== "function" || typeof exparser?.Element?.getMethodCaller !== "function") throw new TypeError("Exact component caller mapping is unavailable");
|
|
256
|
+
let state = "created";
|
|
257
|
+
let caller;
|
|
258
|
+
let node;
|
|
259
|
+
let virtualId;
|
|
260
|
+
let added = false;
|
|
261
|
+
let removed = false;
|
|
262
|
+
let totalInvocations = 0;
|
|
263
|
+
let matchingInvocations = 0;
|
|
264
|
+
let timer;
|
|
265
|
+
const errors = [];
|
|
266
|
+
const eventName = "exparserComponentInvoker";
|
|
267
|
+
function identitiesMatch() {
|
|
268
|
+
return root.WeixinJSBridge === bridge && root.__glassEaselAdapter__ === adapter && bridge.subscribe === subscribe && bridge.addSubscribeListener === add && bridge.removeSubscribeListener === remove && adapter.getVirtualDOM() === virtualDOM && virtualDOM.getExparser() === exparser;
|
|
269
|
+
}
|
|
270
|
+
function snapshot() {
|
|
271
|
+
return {
|
|
272
|
+
state,
|
|
273
|
+
added,
|
|
274
|
+
removed,
|
|
275
|
+
totalInvocations,
|
|
276
|
+
matchingInvocations,
|
|
277
|
+
errors: [...errors],
|
|
278
|
+
identitiesMatch: identitiesMatch()
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
function listener(message, sender) {
|
|
282
|
+
totalInvocations += 1;
|
|
283
|
+
if (state !== "armed" && state !== "resolved") return;
|
|
284
|
+
if (message?.method !== "resolveComponentCaller") return;
|
|
285
|
+
matchingInvocations += 1;
|
|
286
|
+
try {
|
|
287
|
+
const args = message.params;
|
|
288
|
+
if (matchingInvocations !== 1 || !Array.isArray(args) || args.length !== 1 || args[0] === null || typeof args[0] !== "object" || Array.isArray(args[0])) throw new Error("Component caller resolver payload is ambiguous");
|
|
289
|
+
const params = args[0];
|
|
290
|
+
const id = params.nodeId;
|
|
291
|
+
if (!Number.isSafeInteger(message.callbackId) || message.callbackId <= 0 || sender !== expected.pageId || params.webviewId !== expected.pageId || !(typeof id === "string" && id.length > 0 || Number.isSafeInteger(id) && id >= 0)) throw new Error("Component caller resolver identity disagrees");
|
|
292
|
+
if (!identitiesMatch()) throw new Error("Component caller bridge identity changed");
|
|
293
|
+
const resolvedNode = virtualDOM.getNodeById(id, expected.pageId);
|
|
294
|
+
if (!resolvedNode || virtualDOM.getNodeId(resolvedNode, expected.pageId) !== id || virtualDOM.getPageId(resolvedNode) !== expected.pageId) throw new Error("Component caller virtual node identity disagrees");
|
|
295
|
+
const resolvedCaller = exparser.Element.getMethodCaller(resolvedNode);
|
|
296
|
+
if (!resolvedCaller || !["object", "function"].includes(typeof resolvedCaller)) throw new Error("Component caller is unavailable");
|
|
297
|
+
virtualId = id;
|
|
298
|
+
node = resolvedNode;
|
|
299
|
+
caller = resolvedCaller;
|
|
300
|
+
state = "resolved";
|
|
301
|
+
} catch (error) {
|
|
302
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
303
|
+
state = "failed";
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function removeListener() {
|
|
307
|
+
if (added && !removed) {
|
|
308
|
+
remove.call(bridge, eventName, listener);
|
|
309
|
+
removed = true;
|
|
310
|
+
}
|
|
311
|
+
if (timer !== void 0) {
|
|
312
|
+
clearTimeout(timer);
|
|
313
|
+
timer = void 0;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function dispose() {
|
|
317
|
+
try {
|
|
318
|
+
removeListener();
|
|
319
|
+
state = "disposed";
|
|
320
|
+
} finally {
|
|
321
|
+
caller = void 0;
|
|
322
|
+
node = void 0;
|
|
323
|
+
virtualId = void 0;
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
state,
|
|
327
|
+
added,
|
|
328
|
+
removed,
|
|
329
|
+
totalInvocations,
|
|
330
|
+
matchingInvocations,
|
|
331
|
+
errors: [...errors]
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
backend: "exparser",
|
|
336
|
+
arm() {
|
|
337
|
+
if (state !== "created") throw new Error("Component caller observer cannot be armed twice");
|
|
338
|
+
state = "armed";
|
|
339
|
+
timer = setTimeout(() => {
|
|
340
|
+
try {
|
|
341
|
+
dispose();
|
|
342
|
+
} catch (error) {
|
|
343
|
+
errors.push(error instanceof Error ? error.message : String(error));
|
|
344
|
+
state = "failed";
|
|
345
|
+
}
|
|
346
|
+
}, observerTimeout);
|
|
347
|
+
added = true;
|
|
348
|
+
add.call(bridge, eventName, listener);
|
|
349
|
+
return snapshot();
|
|
350
|
+
},
|
|
351
|
+
snapshot,
|
|
352
|
+
disarm() {
|
|
353
|
+
removeListener();
|
|
354
|
+
if (state !== "resolved" || matchingInvocations !== 1 || errors.length || !identitiesMatch()) throw new Error(`Exact component caller resolution failed: ${JSON.stringify(snapshot())}`);
|
|
355
|
+
state = "ready";
|
|
356
|
+
return snapshot();
|
|
357
|
+
},
|
|
358
|
+
invoke(method, args) {
|
|
359
|
+
if (state !== "ready" || !removed || errors.length || !identitiesMatch()) throw new Error("Component caller is not ready for a single invocation");
|
|
360
|
+
const pages = typeof getCurrentPages === "function" ? getCurrentPages() : [];
|
|
361
|
+
if (pages[pages.length - 1]?.__wxWebviewId__ !== expected.pageId || virtualDOM.getNodeById(virtualId, expected.pageId) !== node || virtualDOM.getNodeId(node, expected.pageId) !== virtualId || virtualDOM.getPageId(node) !== expected.pageId || exparser.Element.getMethodCaller(node) !== caller) throw new Error("Component caller changed before invocation");
|
|
362
|
+
state = "dispatched";
|
|
363
|
+
return caller[method](...args);
|
|
364
|
+
},
|
|
365
|
+
dispose
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
// src/componentCaller/index.ts
|
|
369
|
+
const queues = /* @__PURE__ */ new WeakMap();
|
|
370
|
+
const blocked = /* @__PURE__ */ new WeakMap();
|
|
371
|
+
const owners = /* @__PURE__ */ new WeakMap();
|
|
372
|
+
const TIMEOUT = 2500;
|
|
373
|
+
/** 关闭连接前确认全部自有 listener 已移除;失败时保留 token 与可重试的所有权。 */
|
|
374
|
+
function cleanupComponentCallers(connection) {
|
|
375
|
+
const pending = owners.get(connection);
|
|
376
|
+
if (!pending?.size) return;
|
|
377
|
+
return (async () => {
|
|
378
|
+
const errors = [];
|
|
379
|
+
for (const owner of [...pending]) try {
|
|
380
|
+
await owner.cleanup();
|
|
381
|
+
} catch (error) {
|
|
382
|
+
errors.push(error);
|
|
383
|
+
}
|
|
384
|
+
if (errors.length) throw new AggregateError(errors, "Component caller cleanup remains incomplete", { cause: errors[0] });
|
|
385
|
+
blocked.delete(connection);
|
|
386
|
+
})();
|
|
387
|
+
}
|
|
388
|
+
function result(response) {
|
|
389
|
+
if (response.exceptionDetails) throw new Error(response.exceptionDetails.text ?? "Component caller Runtime exception", { cause: response.exceptionDetails });
|
|
390
|
+
if (!response.result) throw new Error("Component caller Runtime result is missing");
|
|
391
|
+
return response.result;
|
|
392
|
+
}
|
|
393
|
+
function serializeArguments(args) {
|
|
394
|
+
const payload = JSON.parse(stringify({ params: { args } }));
|
|
395
|
+
return JSON.stringify(payload.params.args);
|
|
396
|
+
}
|
|
397
|
+
/** 保持原 SDK 的 returnByValue 解包,不等待业务返回的 Promise。 */
|
|
398
|
+
async function unwrap(connection, remote) {
|
|
399
|
+
if (remote.subtype === "null") return null;
|
|
400
|
+
if (remote.type === "undefined") return;
|
|
401
|
+
if (remote.value !== void 0) return remote.value;
|
|
402
|
+
if (remote.unserializableValue) {
|
|
403
|
+
const value = remote.unserializableValue;
|
|
404
|
+
if (/^-?\d+n$/.test(value)) return BigInt(value.slice(0, -1));
|
|
405
|
+
if ([
|
|
406
|
+
"NaN",
|
|
407
|
+
"Infinity",
|
|
408
|
+
"-Infinity",
|
|
409
|
+
"-0"
|
|
410
|
+
].includes(value)) return Number(value);
|
|
411
|
+
throw new Error(`Unsupported component return value: ${value}`);
|
|
412
|
+
}
|
|
413
|
+
if (remote.objectId) try {
|
|
414
|
+
const converted = await connection.send("App.CDPCommand", {
|
|
415
|
+
domain: "Runtime",
|
|
416
|
+
method: "callFunctionOn",
|
|
417
|
+
params: {
|
|
418
|
+
objectId: remote.objectId,
|
|
419
|
+
functionDeclaration: "function () { try { return JSON.stringify(this) } catch { return String(this) } }",
|
|
420
|
+
returnByValue: true
|
|
421
|
+
}
|
|
422
|
+
}, { timeout: TIMEOUT });
|
|
423
|
+
if (converted.result?.value !== void 0) try {
|
|
424
|
+
return JSON.parse(converted.result.value);
|
|
425
|
+
} catch {
|
|
426
|
+
return converted.result.value;
|
|
427
|
+
}
|
|
428
|
+
} catch {}
|
|
429
|
+
return remote.description ?? `[${remote.type}]`;
|
|
430
|
+
}
|
|
431
|
+
async function invoke(connection, target, method, args, native) {
|
|
432
|
+
let token;
|
|
433
|
+
let owner;
|
|
434
|
+
const errors = [];
|
|
435
|
+
let cleanupFailed = false;
|
|
436
|
+
let nativeBackend = false;
|
|
437
|
+
let value;
|
|
438
|
+
const cdp = async (domain, command, params) => {
|
|
439
|
+
return await connection.send("App.CDPCommand", {
|
|
440
|
+
domain,
|
|
441
|
+
method: command,
|
|
442
|
+
params
|
|
443
|
+
}, { timeout: TIMEOUT });
|
|
444
|
+
};
|
|
445
|
+
const wrapper = async (body) => result(await cdp("Runtime", "callFunctionOn", {
|
|
446
|
+
objectId: token,
|
|
447
|
+
functionDeclaration: `function () { return ${body} }`,
|
|
448
|
+
arguments: [],
|
|
449
|
+
returnByValue: true
|
|
450
|
+
}));
|
|
451
|
+
try {
|
|
452
|
+
if (!Number.isSafeInteger(target.nodeId) || target.nodeId < 0 || !Number.isSafeInteger(target.pageId)) throw new Error("Invalid component caller native target");
|
|
453
|
+
if ((await connection.send("App.getCurrentPage", {}, { timeout: TIMEOUT })).pageId !== target.pageId) throw new Error("Component caller target is not on the current page");
|
|
454
|
+
const created = result(await cdp("Runtime", "evaluate", {
|
|
455
|
+
expression: `(${createComponentCallerSession.toString()})(${JSON.stringify(target)}, 10000)`,
|
|
456
|
+
returnByValue: false
|
|
457
|
+
}));
|
|
458
|
+
if (!created.objectId) throw new Error("Component caller session has no owned Runtime token");
|
|
459
|
+
token = created.objectId;
|
|
460
|
+
let cleaning;
|
|
461
|
+
let disposed = false;
|
|
462
|
+
let released = false;
|
|
463
|
+
owner = { cleanup() {
|
|
464
|
+
cleaning ??= (async () => {
|
|
465
|
+
if (released) return;
|
|
466
|
+
if (!disposed) {
|
|
467
|
+
const snapshot = (await wrapper("this.dispose()")).value;
|
|
468
|
+
if (snapshot?.state !== "disposed" || snapshot.added && !snapshot.removed) throw new Error("Component caller listener removal was not confirmed");
|
|
469
|
+
disposed = true;
|
|
470
|
+
}
|
|
471
|
+
await cdp("Runtime", "releaseObject", { objectId: token });
|
|
472
|
+
released = true;
|
|
473
|
+
owners.get(connection)?.delete(owner);
|
|
474
|
+
})().catch((error) => {
|
|
475
|
+
cleaning = void 0;
|
|
476
|
+
throw error;
|
|
477
|
+
});
|
|
478
|
+
return cleaning;
|
|
479
|
+
} };
|
|
480
|
+
let pending = owners.get(connection);
|
|
481
|
+
if (!pending) {
|
|
482
|
+
pending = /* @__PURE__ */ new Set();
|
|
483
|
+
owners.set(connection, pending);
|
|
484
|
+
}
|
|
485
|
+
pending.add(owner);
|
|
486
|
+
const backend = (await wrapper("this.backend")).value;
|
|
487
|
+
if (backend === "glass-easel") nativeBackend = true;
|
|
488
|
+
else if (backend === "exparser") {
|
|
489
|
+
const argumentSource = serializeArguments(args);
|
|
490
|
+
await wrapper("this.arm()");
|
|
491
|
+
await cdp("WxComponent", "resolveComponentCaller", { nodeId: target.nodeId });
|
|
492
|
+
await wrapper("this.disarm()");
|
|
493
|
+
value = await unwrap(connection, await wrapper(`this.invoke(${JSON.stringify(method)}, ${argumentSource})`));
|
|
494
|
+
} else throw new Error("Unknown component caller backend");
|
|
495
|
+
} catch (error) {
|
|
496
|
+
errors.push(error);
|
|
497
|
+
} finally {
|
|
498
|
+
if (owner) try {
|
|
499
|
+
await owner.cleanup();
|
|
500
|
+
} catch (error) {
|
|
501
|
+
errors.push(error);
|
|
502
|
+
cleanupFailed = true;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (cleanupFailed) blocked.set(connection, new Error("Component caller cleanup failed; reconnect before invoking another component", { cause: errors[0] }));
|
|
506
|
+
if (errors.length === 1) throw errors[0];
|
|
507
|
+
if (errors.length) throw new AggregateError(errors, "Component caller invocation and cleanup failed", { cause: errors[0] });
|
|
508
|
+
if (nativeBackend) {
|
|
509
|
+
if (!native) throw new Error("Native component backend requires the original protocol");
|
|
510
|
+
return await native();
|
|
511
|
+
}
|
|
512
|
+
return value;
|
|
513
|
+
}
|
|
514
|
+
/** 同一连接串行观察 resolver,避免多个组件误认彼此的桥接消息。 */
|
|
515
|
+
async function invokeComponentMethod(connection, target, method, args, native) {
|
|
516
|
+
const pending = (queues.get(connection) ?? Promise.resolve()).then(async () => {
|
|
517
|
+
const failure = blocked.get(connection);
|
|
518
|
+
if (failure) throw failure;
|
|
519
|
+
return await invoke(connection, target, method, args, native);
|
|
520
|
+
});
|
|
521
|
+
const settled = pending.then(() => {}, () => {});
|
|
522
|
+
queues.set(connection, settled);
|
|
523
|
+
try {
|
|
524
|
+
return await pending;
|
|
525
|
+
} finally {
|
|
526
|
+
if (queues.get(connection) === settled) queues.delete(connection);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
// src/pageRouteSnapshot.ts
|
|
239
530
|
const ROUTE_ELEMENT_SNAPSHOT_TIMEOUT = 5e3;
|
|
240
531
|
const ROUTE_ELEMENT_SNAPSHOT_EMPTY_RETRIES = 2;
|
|
241
532
|
const ROUTE_ELEMENT_SNAPSHOT_RETRY_DELAY = 220;
|
|
@@ -452,8 +743,15 @@ async function readRouteElementSnapshot(connection, route, query, selector, inde
|
|
|
452
743
|
}
|
|
453
744
|
return null;
|
|
454
745
|
}
|
|
455
|
-
|
|
456
|
-
|
|
746
|
+
// src/Element.ts
|
|
747
|
+
function normalizeTouchOptions(options) {
|
|
748
|
+
const { changeTouches, ...payload } = options;
|
|
749
|
+
const changedTouches = options.changedTouches ?? changeTouches;
|
|
750
|
+
return changedTouches === void 0 ? payload : {
|
|
751
|
+
...payload,
|
|
752
|
+
changedTouches
|
|
753
|
+
};
|
|
754
|
+
}
|
|
457
755
|
const ELEMENT_QUERY_TIMEOUT = 2500;
|
|
458
756
|
const ELEMENT_WXML_TIMEOUT = 8e3;
|
|
459
757
|
/** Element 的实现。 */
|
|
@@ -559,16 +857,26 @@ var Element = class Element {
|
|
|
559
857
|
await this.send("Element.triggerEvent", payload);
|
|
560
858
|
}
|
|
561
859
|
async touchstart(options = {}) {
|
|
562
|
-
await this.send("Element.touchstart", options);
|
|
860
|
+
await this.send("Element.touchstart", normalizeTouchOptions(options));
|
|
563
861
|
}
|
|
564
862
|
async touchmove(options = {}) {
|
|
565
|
-
await this.send("Element.touchmove", options);
|
|
863
|
+
await this.send("Element.touchmove", normalizeTouchOptions(options));
|
|
566
864
|
}
|
|
567
865
|
async touchend(options = {}) {
|
|
568
|
-
await this.send("Element.touchend", options);
|
|
866
|
+
await this.send("Element.touchend", normalizeTouchOptions(options));
|
|
569
867
|
}
|
|
570
868
|
async dispatchEvent(options) {
|
|
571
|
-
|
|
869
|
+
const payload = normalizeTouchOptions(options);
|
|
870
|
+
if (!payload.$ && !payload.detail && (payload.eventName === "touchstart" || payload.eventName === "touchmove" || payload.eventName === "touchend") && (payload.touches !== void 0 || payload.changedTouches !== void 0)) {
|
|
871
|
+
const { eventName, ...touchOptions } = payload;
|
|
872
|
+
await this.send(`Element.${eventName}`, touchOptions);
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
const { detail, ...event } = payload;
|
|
876
|
+
await this.send("Element.dispatchEvent", detail === void 0 ? event : {
|
|
877
|
+
...event,
|
|
878
|
+
eventData: detail
|
|
879
|
+
});
|
|
572
880
|
}
|
|
573
881
|
async _property(name) {
|
|
574
882
|
return await this.getter(name, "getProperties", "properties");
|
|
@@ -750,6 +1058,18 @@ var CustomElement = class extends Element {
|
|
|
750
1058
|
return (await this.send("Element.getData", payload)).data;
|
|
751
1059
|
}
|
|
752
1060
|
async callMethod(method, ...args) {
|
|
1061
|
+
if (this.connection.prefersComponentCallerBridge) {
|
|
1062
|
+
const nodeId = this.nodeId === null ? NaN : Number(this.nodeId);
|
|
1063
|
+
return await invokeComponentMethod(this.connection, {
|
|
1064
|
+
nodeId,
|
|
1065
|
+
pageId: this.pageId
|
|
1066
|
+
}, method, args, async () => {
|
|
1067
|
+
return (await this.send("Element.callMethod", {
|
|
1068
|
+
method,
|
|
1069
|
+
args
|
|
1070
|
+
})).result;
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
753
1073
|
return (await this.send("Element.callMethod", {
|
|
754
1074
|
method,
|
|
755
1075
|
args
|
|
@@ -823,11 +1143,13 @@ var ContextElement = class extends Element {
|
|
|
823
1143
|
})).result;
|
|
824
1144
|
}
|
|
825
1145
|
};
|
|
826
|
-
|
|
827
|
-
//#region src/Transport.ts
|
|
1146
|
+
// src/Transport.ts
|
|
828
1147
|
/** Transport 的实现。 */
|
|
829
1148
|
var Transport = class extends EventEmitter {
|
|
830
1149
|
ws;
|
|
1150
|
+
closed = false;
|
|
1151
|
+
terminating = false;
|
|
1152
|
+
closing;
|
|
831
1153
|
constructor(ws) {
|
|
832
1154
|
super();
|
|
833
1155
|
this.ws = ws;
|
|
@@ -835,6 +1157,7 @@ var Transport = class extends EventEmitter {
|
|
|
835
1157
|
this.emit("message", typeof data === "string" ? data : data.toString());
|
|
836
1158
|
});
|
|
837
1159
|
ws.on("close", () => {
|
|
1160
|
+
this.closed = true;
|
|
838
1161
|
this.emit("close");
|
|
839
1162
|
});
|
|
840
1163
|
}
|
|
@@ -842,11 +1165,39 @@ var Transport = class extends EventEmitter {
|
|
|
842
1165
|
this.ws.send(message);
|
|
843
1166
|
}
|
|
844
1167
|
close() {
|
|
845
|
-
this.
|
|
1168
|
+
if (this.closing) return this.closing;
|
|
1169
|
+
if (this.closed || this.ws.readyState === 3) return this.closing = Promise.resolve();
|
|
1170
|
+
this.closing = new Promise((resolve, reject) => {
|
|
1171
|
+
let timer;
|
|
1172
|
+
const finish = () => {
|
|
1173
|
+
clearTimeout(timer);
|
|
1174
|
+
this.off("close", finish);
|
|
1175
|
+
resolve();
|
|
1176
|
+
};
|
|
1177
|
+
timer = setTimeout(() => {
|
|
1178
|
+
this.off("close", finish);
|
|
1179
|
+
reject(Object.assign(/* @__PURE__ */ new Error("DevTools websocket did not close within 2000ms"), { code: "DEVTOOLS_DISCONNECT_TIMEOUT" }));
|
|
1180
|
+
}, 2e3);
|
|
1181
|
+
this.once("close", finish);
|
|
1182
|
+
try {
|
|
1183
|
+
if (!this.terminating) {
|
|
1184
|
+
this.terminating = true;
|
|
1185
|
+
this.ws.terminate();
|
|
1186
|
+
}
|
|
1187
|
+
} catch (error) {
|
|
1188
|
+
this.terminating = false;
|
|
1189
|
+
clearTimeout(timer);
|
|
1190
|
+
this.off("close", finish);
|
|
1191
|
+
reject(error);
|
|
1192
|
+
}
|
|
1193
|
+
}).catch((error) => {
|
|
1194
|
+
this.closing = void 0;
|
|
1195
|
+
throw error;
|
|
1196
|
+
});
|
|
1197
|
+
return this.closing;
|
|
846
1198
|
}
|
|
847
1199
|
};
|
|
848
|
-
|
|
849
|
-
//#region src/Connection.ts
|
|
1200
|
+
// src/Connection.ts
|
|
850
1201
|
/**
|
|
851
1202
|
* @file 自动化协议连接实现。
|
|
852
1203
|
*/
|
|
@@ -864,13 +1215,17 @@ const APP_SERVICE_PAGE_METHOD_VERSIONS = /* @__PURE__ */ new Set([
|
|
|
864
1215
|
"2.02.2610082",
|
|
865
1216
|
"2.02.2610092"
|
|
866
1217
|
]);
|
|
1218
|
+
/** 已确认 Exparser caller token 与 Runtime 对象表错绑的 IDE 与 SDK 精确组合。 */
|
|
1219
|
+
const COMPONENT_CALLER_BRIDGE_COMBINATIONS = /* @__PURE__ */ new Map([["2.02.2608080", /* @__PURE__ */ new Set(["3.17.5"])], ["2.02.2609231", /* @__PURE__ */ new Set(["3.17.5", "3.17.6"])]]);
|
|
867
1220
|
/** Connection 的实现。 */
|
|
868
1221
|
var Connection = class Connection extends EventEmitter {
|
|
869
1222
|
transport;
|
|
870
1223
|
disposed = false;
|
|
1224
|
+
disposing;
|
|
871
1225
|
callbacks = /* @__PURE__ */ new Map();
|
|
872
1226
|
useAppServicePageProtocol = false;
|
|
873
1227
|
useAppServicePageMethod = false;
|
|
1228
|
+
useComponentCallerBridge = false;
|
|
874
1229
|
constructor(transport) {
|
|
875
1230
|
super();
|
|
876
1231
|
this.transport = transport;
|
|
@@ -910,18 +1265,51 @@ var Connection = class Connection extends EventEmitter {
|
|
|
910
1265
|
});
|
|
911
1266
|
}
|
|
912
1267
|
dispose() {
|
|
913
|
-
if (this.
|
|
914
|
-
|
|
915
|
-
|
|
1268
|
+
if (this.disposing) return this.disposing;
|
|
1269
|
+
let resolve;
|
|
1270
|
+
let reject;
|
|
1271
|
+
this.disposing = new Promise((done, fail) => {
|
|
1272
|
+
resolve = done;
|
|
1273
|
+
reject = fail;
|
|
1274
|
+
}).catch((error) => {
|
|
1275
|
+
this.disposing = void 0;
|
|
1276
|
+
throw error;
|
|
1277
|
+
});
|
|
1278
|
+
const cleanup = cleanupComponentCallers(this);
|
|
1279
|
+
if (cleanup) cleanup.then(() => this.closeTransport(resolve, reject), reject);
|
|
1280
|
+
else this.closeTransport(resolve, reject);
|
|
1281
|
+
return this.disposing;
|
|
1282
|
+
}
|
|
1283
|
+
closeTransport(resolve, reject) {
|
|
1284
|
+
const errors = [];
|
|
1285
|
+
try {
|
|
1286
|
+
this.onClose();
|
|
1287
|
+
} catch (error) {
|
|
1288
|
+
errors.push(error);
|
|
1289
|
+
}
|
|
916
1290
|
this.transport.off("message", this.onMessage);
|
|
917
1291
|
this.transport.off("close", this.onClose);
|
|
918
|
-
|
|
1292
|
+
const finish = () => {
|
|
1293
|
+
if (errors.length === 1) reject(errors[0]);
|
|
1294
|
+
else if (errors.length) reject(new AggregateError(errors, "Connection close notification and transport cleanup failed", { cause: errors[0] }));
|
|
1295
|
+
else resolve();
|
|
1296
|
+
};
|
|
1297
|
+
try {
|
|
1298
|
+
Promise.resolve(this.transport.close()).then(finish, (error) => {
|
|
1299
|
+
errors.push(error);
|
|
1300
|
+
finish();
|
|
1301
|
+
});
|
|
1302
|
+
} catch (error) {
|
|
1303
|
+
errors.push(error);
|
|
1304
|
+
finish();
|
|
1305
|
+
}
|
|
919
1306
|
}
|
|
920
1307
|
/** 根据 DevTools 版本选择稳定的 Page 协议实现。 */
|
|
921
1308
|
configureToolInfo(info) {
|
|
922
1309
|
const version = String(info.version ?? "");
|
|
923
1310
|
this.useAppServicePageProtocol = APP_SERVICE_PAGE_PROTOCOL_VERSIONS.has(version);
|
|
924
1311
|
this.useAppServicePageMethod = this.useAppServicePageProtocol || APP_SERVICE_PAGE_METHOD_VERSIONS.has(version);
|
|
1312
|
+
this.useComponentCallerBridge = COMPONENT_CALLER_BRIDGE_COMBINATIONS.get(version)?.has(info.SDKVersion ?? "") ?? false;
|
|
925
1313
|
}
|
|
926
1314
|
get prefersAppServicePageProtocol() {
|
|
927
1315
|
return this.useAppServicePageProtocol;
|
|
@@ -930,6 +1318,10 @@ var Connection = class Connection extends EventEmitter {
|
|
|
930
1318
|
get prefersAppServicePageMethod() {
|
|
931
1319
|
return this.useAppServicePageMethod;
|
|
932
1320
|
}
|
|
1321
|
+
/** 仅为实测存在跨上下文 token 错绑的 IDE 与基础库启用真实 caller 桥接。 */
|
|
1322
|
+
get prefersComponentCallerBridge() {
|
|
1323
|
+
return this.useComponentCallerBridge;
|
|
1324
|
+
}
|
|
933
1325
|
onMessage = (message) => {
|
|
934
1326
|
debugProtocol(`${dateFormat("yyyy-mm-dd HH:MM:ss:l")} ◀ RECV ${message}`);
|
|
935
1327
|
const { id, method, error, result, params } = JSON.parse(message);
|
|
@@ -942,17 +1334,21 @@ var Connection = class Connection extends EventEmitter {
|
|
|
942
1334
|
this.callbacks.delete(id);
|
|
943
1335
|
clearTimeout(callback.timeout);
|
|
944
1336
|
if (error) {
|
|
945
|
-
|
|
1337
|
+
const message = typeof error.message === "string" ? error.message : error.message === void 0 ? closeErrTip : JSON.stringify(error.message);
|
|
1338
|
+
callback.reject(new Error(message, { cause: error }));
|
|
946
1339
|
return;
|
|
947
1340
|
}
|
|
948
1341
|
callback.resolve(result);
|
|
949
1342
|
};
|
|
950
1343
|
onClose = () => {
|
|
1344
|
+
if (this.disposed) return;
|
|
1345
|
+
this.disposed = true;
|
|
951
1346
|
for (const callback of this.callbacks.values()) {
|
|
952
1347
|
clearTimeout(callback.timeout);
|
|
953
1348
|
callback.reject(/* @__PURE__ */ new Error(closeErrTip));
|
|
954
1349
|
}
|
|
955
1350
|
this.callbacks.clear();
|
|
1351
|
+
this.emit("close");
|
|
956
1352
|
};
|
|
957
1353
|
static create(url, timeout = CONNECT_TIMEOUT, signal) {
|
|
958
1354
|
signal?.throwIfAborted();
|
|
@@ -997,13 +1393,28 @@ var Connection = class Connection extends EventEmitter {
|
|
|
997
1393
|
});
|
|
998
1394
|
}
|
|
999
1395
|
};
|
|
1000
|
-
|
|
1001
|
-
//#region src/headless.ts
|
|
1396
|
+
// src/headless.ts
|
|
1002
1397
|
async function launchHeadlessAutomator(options) {
|
|
1003
|
-
return await (await import("./launch-
|
|
1398
|
+
return await (await import("./launch-DuW7r68J.mjs")).launch({ projectPath: options.projectPath });
|
|
1399
|
+
}
|
|
1400
|
+
// src/launcher/diagnostics.ts
|
|
1401
|
+
/** 将本次 CLI 的有界输出附到原始错误,避免泄露登录票据及账号参数。 */
|
|
1402
|
+
function attachCliDiagnostics(error, state, secrets = []) {
|
|
1403
|
+
const original = error instanceof Error ? error : new Error("Failed to launch WeChat DevTools", { cause: error });
|
|
1404
|
+
if (original.cliOutput !== void 0) return original;
|
|
1405
|
+
let output = stripVTControlCharacters(state.output).trim();
|
|
1406
|
+
for (const secret of secrets.filter(Boolean).sort((a, b) => b.length - a.length)) output = output.split(secret).join("[redacted]");
|
|
1407
|
+
output = output.replace(/(authorization["']?\s*[:=]\s*["']?)(?:Bearer|Basic)\s+[^\s"',;}]+/gi, "$1[redacted]");
|
|
1408
|
+
output = output.replace(/((?:--)?(?:auto-account|account|ticket|access[_-]?token|refresh[_-]?token|token|authorization)["']?\s*(?:[:=]\s*|\s))(["']?)([^\s"',;}]+)\2/gi, "$1[redacted]");
|
|
1409
|
+
if (!output) return original;
|
|
1410
|
+
const truncated = state.outputTruncated || output.length > 32768;
|
|
1411
|
+
output = output.slice(-32768);
|
|
1412
|
+
const target = Object.isExtensible(original) ? original : new Error(original.message, { cause: original });
|
|
1413
|
+
target.cliOutput = output;
|
|
1414
|
+
target.message += `\ncli output${truncated ? " (truncated; last 32768 characters)" : ""}:\n${output}`;
|
|
1415
|
+
return target;
|
|
1004
1416
|
}
|
|
1005
|
-
|
|
1006
|
-
//#region src/launcher/portLease.ts
|
|
1417
|
+
// src/launcher/portLease.ts
|
|
1007
1418
|
/**
|
|
1008
1419
|
* @file DevTools 自动化端口租约。
|
|
1009
1420
|
*/
|
|
@@ -1026,7 +1437,7 @@ function isProcessAlive(pid) {
|
|
|
1026
1437
|
}
|
|
1027
1438
|
}
|
|
1028
1439
|
async function removePortLock(leasePath) {
|
|
1029
|
-
await fs.rm(leasePath, { force: true })
|
|
1440
|
+
await fs.rm(leasePath, { force: true });
|
|
1030
1441
|
}
|
|
1031
1442
|
async function removeStalePortLock(leasePath) {
|
|
1032
1443
|
try {
|
|
@@ -1058,14 +1469,18 @@ async function createPortLock(port) {
|
|
|
1058
1469
|
return leasePath;
|
|
1059
1470
|
}
|
|
1060
1471
|
function createLease(port, leasePath) {
|
|
1061
|
-
let
|
|
1472
|
+
let releasing;
|
|
1062
1473
|
return {
|
|
1063
1474
|
path: leasePath,
|
|
1064
1475
|
port,
|
|
1065
|
-
release:
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1476
|
+
release: () => {
|
|
1477
|
+
releasing ??= (async () => {
|
|
1478
|
+
if (leasePath) await removePortLock(leasePath);
|
|
1479
|
+
})().catch((error) => {
|
|
1480
|
+
releasing = void 0;
|
|
1481
|
+
throw error;
|
|
1482
|
+
});
|
|
1483
|
+
return releasing;
|
|
1069
1484
|
}
|
|
1070
1485
|
};
|
|
1071
1486
|
}
|
|
@@ -1097,8 +1512,7 @@ async function acquireAutomatorPortLease(preferredPort) {
|
|
|
1097
1512
|
}
|
|
1098
1513
|
return createLease(await getPort(0, "127.0.0.1"));
|
|
1099
1514
|
}
|
|
1100
|
-
|
|
1101
|
-
//#region src/launcher/wechatCliFallback.ts
|
|
1515
|
+
// src/launcher/wechatCliFallback.ts
|
|
1102
1516
|
const WECHAT_DEVTOOLS_SERVICE_PORT_RE = /listening\s+on\s+http:\/\/127\.0\.0\.1:(\d{2,5})/i;
|
|
1103
1517
|
function extractWechatDevtoolsServicePort(output) {
|
|
1104
1518
|
const port = Number(output.match(WECHAT_DEVTOOLS_SERVICE_PORT_RE)?.[1]);
|
|
@@ -1157,8 +1571,7 @@ function resolveWechatDevtoolsBootstrapArgs(args) {
|
|
|
1157
1571
|
bootstrapArgs.push("islogin");
|
|
1158
1572
|
return bootstrapArgs;
|
|
1159
1573
|
}
|
|
1160
|
-
|
|
1161
|
-
//#region src/launcher/process.ts
|
|
1574
|
+
// src/launcher/process.ts
|
|
1162
1575
|
const WINDOWS_BATCH_CLI_RE = /\.(?:bat|cmd)$/i;
|
|
1163
1576
|
function shouldUseWindowsCommandShell(cliPath) {
|
|
1164
1577
|
return isWindows && WINDOWS_BATCH_CLI_RE.test(cliPath);
|
|
@@ -1185,7 +1598,8 @@ function spawnWechatCli(cliPath, args, cwd, scope) {
|
|
|
1185
1598
|
error: null,
|
|
1186
1599
|
success: false,
|
|
1187
1600
|
exited: false,
|
|
1188
|
-
output: ""
|
|
1601
|
+
output: "",
|
|
1602
|
+
outputTruncated: false
|
|
1189
1603
|
};
|
|
1190
1604
|
const bootstrapArgs = resolveWechatDevtoolsBootstrapArgs(args);
|
|
1191
1605
|
const target = shouldUseWindowsCommandShell(cliPath) ? resolveWindowsBatchSpawn(cliPath, bootstrapArgs) : {
|
|
@@ -1221,7 +1635,9 @@ function spawnWechatCli(cliPath, args, cwd, scope) {
|
|
|
1221
1635
|
state.error = error;
|
|
1222
1636
|
});
|
|
1223
1637
|
const output = (chunk) => {
|
|
1224
|
-
|
|
1638
|
+
const combined = state.output + String(chunk);
|
|
1639
|
+
state.outputTruncated ||= combined.length > 32768;
|
|
1640
|
+
state.output = combined.slice(-32768);
|
|
1225
1641
|
};
|
|
1226
1642
|
child.stdout?.on("data", output);
|
|
1227
1643
|
child.stderr?.on("data", output);
|
|
@@ -1234,11 +1650,9 @@ function spawnWechatCli(cliPath, args, cwd, scope) {
|
|
|
1234
1650
|
child.unref();
|
|
1235
1651
|
return Object.assign(state, { release });
|
|
1236
1652
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
//#endregion
|
|
1241
|
-
//#region src/appServiceHeap.ts
|
|
1653
|
+
// package.json
|
|
1654
|
+
var version = "1.5.0";
|
|
1655
|
+
// src/appServiceHeap.ts
|
|
1242
1656
|
function isRecord$1(value) {
|
|
1243
1657
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1244
1658
|
}
|
|
@@ -1274,8 +1688,112 @@ async function readAppServiceHeapUsage(send, options = {}) {
|
|
|
1274
1688
|
totalSize: response.totalSize
|
|
1275
1689
|
};
|
|
1276
1690
|
}
|
|
1277
|
-
|
|
1278
|
-
|
|
1691
|
+
// src/cdp/index.ts
|
|
1692
|
+
/** 单个连接共享 CDP 启用状态与订阅;不创建额外 WebSocket。 */
|
|
1693
|
+
var CDPController = class {
|
|
1694
|
+
connection;
|
|
1695
|
+
send;
|
|
1696
|
+
enabling;
|
|
1697
|
+
enabled = false;
|
|
1698
|
+
stopped = false;
|
|
1699
|
+
subscriptions = /* @__PURE__ */ new Set();
|
|
1700
|
+
session;
|
|
1701
|
+
constructor(connection, send) {
|
|
1702
|
+
this.connection = connection;
|
|
1703
|
+
this.send = send;
|
|
1704
|
+
connection.on("close", this.stop);
|
|
1705
|
+
this.session = {
|
|
1706
|
+
enable: (options) => this.enable(options),
|
|
1707
|
+
sendCommand: (domain, method, params = {}, options) => this.request("App.CDPCommand", {
|
|
1708
|
+
domain,
|
|
1709
|
+
method,
|
|
1710
|
+
params
|
|
1711
|
+
}, options),
|
|
1712
|
+
listProtocol: (options) => this.request("App.CDPListProtocol", {}, options),
|
|
1713
|
+
onEvent: (listener) => this.onEvent(listener)
|
|
1714
|
+
};
|
|
1715
|
+
}
|
|
1716
|
+
enable(options) {
|
|
1717
|
+
if (this.stopped) return Promise.reject(/* @__PURE__ */ new Error("CDP connection closed"));
|
|
1718
|
+
try {
|
|
1719
|
+
this.validateTimeout(options);
|
|
1720
|
+
} catch (error) {
|
|
1721
|
+
return Promise.reject(error);
|
|
1722
|
+
}
|
|
1723
|
+
if (!this.enabling) {
|
|
1724
|
+
let pending;
|
|
1725
|
+
try {
|
|
1726
|
+
pending = this.send("App.CDPEnable", {}, options);
|
|
1727
|
+
} catch (error) {
|
|
1728
|
+
pending = Promise.reject(error);
|
|
1729
|
+
}
|
|
1730
|
+
this.enabling = pending.then((result) => {
|
|
1731
|
+
this.enabled = true;
|
|
1732
|
+
return result;
|
|
1733
|
+
}, (error) => {
|
|
1734
|
+
this.enabling = void 0;
|
|
1735
|
+
throw error;
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1738
|
+
if (options?.timeout === void 0) return this.enabling;
|
|
1739
|
+
return new Promise((resolve, reject) => {
|
|
1740
|
+
const timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Timed out enabling CDP within ${options.timeout}ms`)), options.timeout);
|
|
1741
|
+
this.enabling.then(resolve, reject).finally(() => clearTimeout(timer));
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
/** 日志显式刷新时重新注册事件通道,兼容 AppService 重载。 */
|
|
1745
|
+
refresh(options) {
|
|
1746
|
+
if (this.enabled) {
|
|
1747
|
+
this.enabled = false;
|
|
1748
|
+
this.enabling = void 0;
|
|
1749
|
+
}
|
|
1750
|
+
return this.enable(options);
|
|
1751
|
+
}
|
|
1752
|
+
stop = () => {
|
|
1753
|
+
this.stopped = true;
|
|
1754
|
+
this.enabled = false;
|
|
1755
|
+
this.enabling = void 0;
|
|
1756
|
+
this.connection.off("close", this.stop);
|
|
1757
|
+
for (const unsubscribe of this.subscriptions) unsubscribe();
|
|
1758
|
+
};
|
|
1759
|
+
async request(method, params, options) {
|
|
1760
|
+
this.validateTimeout(options);
|
|
1761
|
+
const startedAt = Date.now();
|
|
1762
|
+
await this.enable(options);
|
|
1763
|
+
if (this.stopped) throw new Error("CDP connection closed");
|
|
1764
|
+
if (options?.timeout === void 0) return await this.send(method, params);
|
|
1765
|
+
const timeout = options.timeout - (Date.now() - startedAt);
|
|
1766
|
+
if (timeout <= 0) throw new Error(`Timed out executing ${method} within ${options.timeout}ms`);
|
|
1767
|
+
return await this.send(method, params, { timeout });
|
|
1768
|
+
}
|
|
1769
|
+
validateTimeout(options) {
|
|
1770
|
+
if (options?.timeout !== void 0 && (!Number.isFinite(options.timeout) || options.timeout <= 0)) throw new RangeError("CDP timeout must be finite and positive");
|
|
1771
|
+
}
|
|
1772
|
+
onEvent(listener) {
|
|
1773
|
+
if (this.stopped) throw new Error("CDP connection closed");
|
|
1774
|
+
const receive = (payload) => {
|
|
1775
|
+
listener(payload.domain, payload.event, payload.params);
|
|
1776
|
+
};
|
|
1777
|
+
const unsubscribe = () => {
|
|
1778
|
+
this.connection.off("App.CDPEvent", receive);
|
|
1779
|
+
this.subscriptions.delete(unsubscribe);
|
|
1780
|
+
};
|
|
1781
|
+
this.subscriptions.add(unsubscribe);
|
|
1782
|
+
this.connection.on("App.CDPEvent", receive);
|
|
1783
|
+
return unsubscribe;
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
const controllers = /* @__PURE__ */ new WeakMap();
|
|
1787
|
+
/** 一个协议连接仅拥有一份 CDP 状态,包括被不同 MiniProgram 包装的连接。 */
|
|
1788
|
+
function getCDPController(connection, send) {
|
|
1789
|
+
let controller = controllers.get(connection);
|
|
1790
|
+
if (!controller) {
|
|
1791
|
+
controller = new CDPController(connection, send);
|
|
1792
|
+
controllers.set(connection, controller);
|
|
1793
|
+
}
|
|
1794
|
+
return controller;
|
|
1795
|
+
}
|
|
1796
|
+
// src/Native.ts
|
|
1279
1797
|
/** Native 的实现。 */
|
|
1280
1798
|
var Native = class extends EventEmitter {
|
|
1281
1799
|
connection;
|
|
@@ -1323,8 +1841,7 @@ var Native = class extends EventEmitter {
|
|
|
1323
1841
|
});
|
|
1324
1842
|
}
|
|
1325
1843
|
};
|
|
1326
|
-
|
|
1327
|
-
//#region src/navigation/query.ts
|
|
1844
|
+
// src/navigation/query.ts
|
|
1328
1845
|
/** 仅核对请求显式携带的参数,保留宿主附加参数及其原始编码形式。 */
|
|
1329
1846
|
function matchesRouteQuery(actual, expected) {
|
|
1330
1847
|
const expectedKeys = Object.keys(expected);
|
|
@@ -1342,8 +1859,7 @@ function matchesRouteQuery(actual, expected) {
|
|
|
1342
1859
|
}
|
|
1343
1860
|
});
|
|
1344
1861
|
}
|
|
1345
|
-
|
|
1346
|
-
//#region src/pageRouteFallback.ts
|
|
1862
|
+
// src/pageRouteFallback.ts
|
|
1347
1863
|
function normalizeFallbackIdPart(value) {
|
|
1348
1864
|
return encodeURIComponent(value).replace(/%/g, "_");
|
|
1349
1865
|
}
|
|
@@ -1368,8 +1884,7 @@ function createRouteFallbackElement(connection, elementMap, pageId, selector, no
|
|
|
1368
1884
|
routeFallbackScopeSelectors: [...scopeSelectors]
|
|
1369
1885
|
}, elementMap);
|
|
1370
1886
|
}
|
|
1371
|
-
|
|
1372
|
-
//#region src/pageRouteMethod.ts
|
|
1887
|
+
// src/pageRouteMethod.ts
|
|
1373
1888
|
const PAGE_CALL_METHOD_FALLBACK_RETRIES$1 = 3;
|
|
1374
1889
|
const PAGE_CALL_METHOD_FALLBACK_RETRY_DELAY$1 = 300;
|
|
1375
1890
|
const ROUTE_METHOD_CALL_STORE = "__weappViteAutomatorMethodCalls__";
|
|
@@ -1520,8 +2035,7 @@ async function callRouteMethodViaAppService(connection, route, query, method, ar
|
|
|
1520
2035
|
await sleep$1(PAGE_CALL_METHOD_FALLBACK_RETRY_DELAY$1);
|
|
1521
2036
|
}
|
|
1522
2037
|
}
|
|
1523
|
-
|
|
1524
|
-
//#region src/Page.ts
|
|
2038
|
+
// src/Page.ts
|
|
1525
2039
|
const PAGE_QUERY_TIMEOUT = 2500;
|
|
1526
2040
|
const PAGE_DATA_TIMEOUT = 12e3;
|
|
1527
2041
|
const PAGE_DATA_PROTOCOL_TIMEOUT = 2500;
|
|
@@ -2397,8 +2911,7 @@ var Page = class Page {
|
|
|
2397
2911
|
return page;
|
|
2398
2912
|
}
|
|
2399
2913
|
};
|
|
2400
|
-
|
|
2401
|
-
//#region src/structuredConsole.ts
|
|
2914
|
+
// src/structuredConsole.ts
|
|
2402
2915
|
function isRecord(value) {
|
|
2403
2916
|
return value !== null && typeof value === "object";
|
|
2404
2917
|
}
|
|
@@ -2491,8 +3004,7 @@ var StructuredConsole = class {
|
|
|
2491
3004
|
}
|
|
2492
3005
|
}
|
|
2493
3006
|
};
|
|
2494
|
-
|
|
2495
|
-
//#region src/util.ts
|
|
3007
|
+
// src/util.ts
|
|
2496
3008
|
/**
|
|
2497
3009
|
* @file 二维码与插件路径工具。
|
|
2498
3010
|
*/
|
|
@@ -2519,8 +3031,7 @@ function extractPluginId(p) {
|
|
|
2519
3031
|
const match = p.match(regPluginId);
|
|
2520
3032
|
return match ? match[1] : "";
|
|
2521
3033
|
}
|
|
2522
|
-
|
|
2523
|
-
//#region src/MiniProgram.ts
|
|
3034
|
+
// src/MiniProgram.ts
|
|
2524
3035
|
const CLOSE_STEP_TIMEOUT = 2e3;
|
|
2525
3036
|
const CURRENT_PAGE_RETRIES = 3;
|
|
2526
3037
|
const CURRENT_PAGE_RETRY_DELAY = 400;
|
|
@@ -2641,16 +3152,21 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2641
3152
|
logEnabling;
|
|
2642
3153
|
structuredLogs = false;
|
|
2643
3154
|
structuredConsole;
|
|
3155
|
+
cdp;
|
|
2644
3156
|
pageMap = /* @__PURE__ */ new Map();
|
|
2645
3157
|
nativeIns;
|
|
3158
|
+
disconnecting;
|
|
3159
|
+
closing;
|
|
2646
3160
|
constructor(connection) {
|
|
2647
3161
|
super();
|
|
2648
3162
|
this.connection = connection;
|
|
3163
|
+
this.cdp = getCDPController(connection, (method, params, options) => this.send(method, params, options));
|
|
2649
3164
|
this.structuredConsole = new StructuredConsole((method, params, options) => this.send(method, params, options), (entry) => this.emit("console", entry));
|
|
2650
3165
|
connection.on("App.CDPEvent", this.structuredConsole.receive);
|
|
2651
3166
|
connection.on("App.logAdded", this.onLogAdded);
|
|
2652
3167
|
connection.on("App.bindingCalled", this.onBindingCalled);
|
|
2653
3168
|
connection.on("App.exceptionThrown", this.onExceptionThrown);
|
|
3169
|
+
connection.on("close", this.stopSubscriptions);
|
|
2654
3170
|
}
|
|
2655
3171
|
async pageStack() {
|
|
2656
3172
|
const { pageStack } = await this.send("App.getPageStack");
|
|
@@ -2795,16 +3311,24 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2795
3311
|
duration: 0
|
|
2796
3312
|
});
|
|
2797
3313
|
}
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
3314
|
+
close() {
|
|
3315
|
+
this.closing ??= (async () => {
|
|
3316
|
+
try {
|
|
3317
|
+
try {
|
|
3318
|
+
await withTimeout(this.send("App.exit"), CLOSE_STEP_TIMEOUT);
|
|
3319
|
+
} catch {}
|
|
3320
|
+
await sleep(1e3);
|
|
3321
|
+
try {
|
|
3322
|
+
await withTimeout(this.send("Tool.close"), CLOSE_STEP_TIMEOUT);
|
|
3323
|
+
} catch {}
|
|
3324
|
+
} finally {
|
|
3325
|
+
await this.disconnect();
|
|
3326
|
+
}
|
|
3327
|
+
})().catch((error) => {
|
|
3328
|
+
this.closing = void 0;
|
|
3329
|
+
throw error;
|
|
3330
|
+
});
|
|
3331
|
+
return this.closing;
|
|
2808
3332
|
}
|
|
2809
3333
|
async remote(auto = false) {
|
|
2810
3334
|
const { qrCode } = await this.send("Tool.enableRemoteDebug", { auto });
|
|
@@ -2817,9 +3341,23 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2817
3341
|
});
|
|
2818
3342
|
}
|
|
2819
3343
|
disconnect() {
|
|
2820
|
-
this.
|
|
2821
|
-
this.
|
|
3344
|
+
if (this.disconnecting) return this.disconnecting;
|
|
3345
|
+
this.stopSubscriptions();
|
|
3346
|
+
this.disconnecting = Promise.resolve(this.connection.dispose()).catch((error) => {
|
|
3347
|
+
this.disconnecting = void 0;
|
|
3348
|
+
throw error;
|
|
3349
|
+
});
|
|
3350
|
+
return this.disconnecting;
|
|
2822
3351
|
}
|
|
3352
|
+
stopSubscriptions = () => {
|
|
3353
|
+
this.structuredConsole.stop();
|
|
3354
|
+
this.cdp.stop();
|
|
3355
|
+
this.connection.off("App.CDPEvent", this.structuredConsole.receive);
|
|
3356
|
+
this.connection.off("App.logAdded", this.onLogAdded);
|
|
3357
|
+
this.connection.off("App.bindingCalled", this.onBindingCalled);
|
|
3358
|
+
this.connection.off("App.exceptionThrown", this.onExceptionThrown);
|
|
3359
|
+
this.connection.off("close", this.stopSubscriptions);
|
|
3360
|
+
};
|
|
2823
3361
|
/** 等待已接收的结构化日志完成 Error 属性检查并发布。 */
|
|
2824
3362
|
async flushConsole() {
|
|
2825
3363
|
await this.structuredConsole.flush();
|
|
@@ -2844,7 +3382,12 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2844
3382
|
let cdpMethod = this.structuredLogs ? "App.CDPEnable" : "App.CDPCommand";
|
|
2845
3383
|
try {
|
|
2846
3384
|
if (this.structuredLogs) {
|
|
2847
|
-
await
|
|
3385
|
+
if (timeout === void 0) await this.cdp.refresh();
|
|
3386
|
+
else {
|
|
3387
|
+
const remaining = timeout - (Date.now() - startedAt);
|
|
3388
|
+
if (remaining <= 0) throw new Error(`Timed out enabling console logging within ${timeout}ms`);
|
|
3389
|
+
await this.cdp.refresh({ timeout: remaining });
|
|
3390
|
+
}
|
|
2848
3391
|
cdpMethod = "App.CDPCommand";
|
|
2849
3392
|
}
|
|
2850
3393
|
await request("App.CDPCommand", {
|
|
@@ -3112,6 +3655,11 @@ var MiniProgram = class extends EventEmitter {
|
|
|
3112
3655
|
}
|
|
3113
3656
|
throw lastError;
|
|
3114
3657
|
}
|
|
3658
|
+
/** 返回当前连接共享的 CDP 调试入口。 */
|
|
3659
|
+
CDP() {
|
|
3660
|
+
return this.cdp.session;
|
|
3661
|
+
}
|
|
3662
|
+
/** 发送原始自动化协议,沿用连接的请求超时及清理契约。 */
|
|
3115
3663
|
async send(method, params = {}, options) {
|
|
3116
3664
|
return options ? await this.connection.send(method, params, options) : await this.connection.send(method, params);
|
|
3117
3665
|
}
|
|
@@ -3129,13 +3677,11 @@ var MiniProgram = class extends EventEmitter {
|
|
|
3129
3677
|
this.emit("exception", payload);
|
|
3130
3678
|
};
|
|
3131
3679
|
};
|
|
3132
|
-
|
|
3133
|
-
//#region src/platform.ts
|
|
3680
|
+
// src/platform.ts
|
|
3134
3681
|
function normalizePlatform(platform = "wechat") {
|
|
3135
3682
|
return platform === "baidu" ? "swan" : platform;
|
|
3136
3683
|
}
|
|
3137
|
-
|
|
3138
|
-
//#region src/smartapp/errors.ts
|
|
3684
|
+
// src/smartapp/errors.ts
|
|
3139
3685
|
/**
|
|
3140
3686
|
* @file 百度智能小程序自动化错误定义。
|
|
3141
3687
|
*/
|
|
@@ -3148,8 +3694,7 @@ var SmartappUnsupportedError = class extends Error {
|
|
|
3148
3694
|
function unsupported(feature) {
|
|
3149
3695
|
throw new SmartappUnsupportedError(feature);
|
|
3150
3696
|
}
|
|
3151
|
-
|
|
3152
|
-
//#region src/smartapp/time.ts
|
|
3697
|
+
// src/smartapp/time.ts
|
|
3153
3698
|
/**
|
|
3154
3699
|
* @file 百度智能小程序自动化时间工具。
|
|
3155
3700
|
*/
|
|
@@ -3163,8 +3708,7 @@ const time = {
|
|
|
3163
3708
|
currentTimestamp,
|
|
3164
3709
|
delay
|
|
3165
3710
|
};
|
|
3166
|
-
|
|
3167
|
-
//#region src/smartapp/Element.ts
|
|
3711
|
+
// src/smartapp/Element.ts
|
|
3168
3712
|
function getCoreElementId(element, selectors, index) {
|
|
3169
3713
|
if (!element) return `${selectors}:${index}`;
|
|
3170
3714
|
const elementId = Reflect.get(element, "id");
|
|
@@ -3231,8 +3775,7 @@ var SmartappElement = class {
|
|
|
3231
3775
|
return Buffer.alloc(0);
|
|
3232
3776
|
}
|
|
3233
3777
|
};
|
|
3234
|
-
|
|
3235
|
-
//#region src/smartapp/Page.ts
|
|
3778
|
+
// src/smartapp/Page.ts
|
|
3236
3779
|
var SmartappPage = class {
|
|
3237
3780
|
page;
|
|
3238
3781
|
path;
|
|
@@ -3255,8 +3798,7 @@ var SmartappPage = class {
|
|
|
3255
3798
|
return await this.page?.callMethod(method, ...args);
|
|
3256
3799
|
}
|
|
3257
3800
|
};
|
|
3258
|
-
|
|
3259
|
-
//#region src/smartapp/App.ts
|
|
3801
|
+
// src/smartapp/App.ts
|
|
3260
3802
|
var SmartappApp = class {
|
|
3261
3803
|
miniProgram;
|
|
3262
3804
|
appKey;
|
|
@@ -3315,11 +3857,10 @@ var SmartappApp = class {
|
|
|
3315
3857
|
return [];
|
|
3316
3858
|
}
|
|
3317
3859
|
async close() {
|
|
3318
|
-
this.miniProgram?.disconnect();
|
|
3860
|
+
await this.miniProgram?.disconnect();
|
|
3319
3861
|
}
|
|
3320
3862
|
};
|
|
3321
|
-
|
|
3322
|
-
//#region src/smartapp/Device.ts
|
|
3863
|
+
// src/smartapp/Device.ts
|
|
3323
3864
|
var SmartappDevice = class {
|
|
3324
3865
|
type;
|
|
3325
3866
|
connectType;
|
|
@@ -3393,12 +3934,11 @@ var SmartappDevice = class {
|
|
|
3393
3934
|
return false;
|
|
3394
3935
|
}
|
|
3395
3936
|
async close() {
|
|
3396
|
-
await this.driver.close?.();
|
|
3397
|
-
|
|
3937
|
+
const errors = (await Promise.allSettled([Promise.resolve().then(() => this.driver.close?.()), Promise.resolve().then(() => this.driver.miniProgram?.disconnect())])).filter((result) => result.status === "rejected").map((result) => result.reason);
|
|
3938
|
+
if (errors.length) throw new AggregateError(errors, "Smartapp device cleanup failed");
|
|
3398
3939
|
}
|
|
3399
3940
|
};
|
|
3400
|
-
|
|
3401
|
-
//#region src/smartapp/file.ts
|
|
3941
|
+
// src/smartapp/file.ts
|
|
3402
3942
|
/**
|
|
3403
3943
|
* @file 百度智能小程序自动化文件工具。
|
|
3404
3944
|
*/
|
|
@@ -3433,8 +3973,7 @@ const file = {
|
|
|
3433
3973
|
rmdir,
|
|
3434
3974
|
wget
|
|
3435
3975
|
};
|
|
3436
|
-
|
|
3437
|
-
//#region src/smartapp/image.ts
|
|
3976
|
+
// src/smartapp/image.ts
|
|
3438
3977
|
async function drawBorder(imageBuffer) {
|
|
3439
3978
|
return imageBuffer;
|
|
3440
3979
|
}
|
|
@@ -3449,8 +3988,7 @@ const image = {
|
|
|
3449
3988
|
drawBorder,
|
|
3450
3989
|
extract
|
|
3451
3990
|
};
|
|
3452
|
-
|
|
3453
|
-
//#region src/smartapp/log.ts
|
|
3991
|
+
// src/smartapp/log.ts
|
|
3454
3992
|
const rawConsole = console;
|
|
3455
3993
|
const loggers = /* @__PURE__ */ new Map();
|
|
3456
3994
|
function createLogger(label) {
|
|
@@ -3474,8 +4012,7 @@ const log = {
|
|
|
3474
4012
|
get,
|
|
3475
4013
|
set
|
|
3476
4014
|
};
|
|
3477
|
-
|
|
3478
|
-
//#region src/smartapp/index.ts
|
|
4015
|
+
// src/smartapp/index.ts
|
|
3479
4016
|
const DEFAULT_CONNECT_TYPE = "usb";
|
|
3480
4017
|
function resolveDeviceType(deviceType) {
|
|
3481
4018
|
switch (deviceType) {
|
|
@@ -3525,8 +4062,7 @@ const SmartappAutomator$1 = {
|
|
|
3525
4062
|
log,
|
|
3526
4063
|
time
|
|
3527
4064
|
};
|
|
3528
|
-
|
|
3529
|
-
//#region src/SwanLauncher.ts
|
|
4065
|
+
// src/SwanLauncher.ts
|
|
3530
4066
|
const smartappAutomatorRuntime = SmartappAutomator$1;
|
|
3531
4067
|
function toSmartappLaunchOptions(options) {
|
|
3532
4068
|
const { cliPath, platform: _platform, runtimeProvider: _runtimeProvider, ...rest } = options;
|
|
@@ -3549,8 +4085,7 @@ var SwanLauncher = class {
|
|
|
3549
4085
|
return await this.automator.connect({ ...options });
|
|
3550
4086
|
}
|
|
3551
4087
|
};
|
|
3552
|
-
|
|
3553
|
-
//#region src/Launcher.ts
|
|
4088
|
+
// src/Launcher.ts
|
|
3554
4089
|
/**
|
|
3555
4090
|
* @file 开发者工具启动与连接流程。
|
|
3556
4091
|
*/
|
|
@@ -3561,29 +4096,34 @@ const DEFAULT_RUNTIME_PROVIDER_ENV = "WEAPP_VITE_AUTOMATOR_RUNTIME_PROVIDER";
|
|
|
3561
4096
|
const LEGACY_RUNTIME_PROVIDER_ENV = "WEAPP_VITE_E2E_RUNTIME_PROVIDER";
|
|
3562
4097
|
let localhostListenPatched = false;
|
|
3563
4098
|
function retainPortLeaseUntilSessionClose(miniProgram, portLease) {
|
|
3564
|
-
let released
|
|
4099
|
+
let released;
|
|
3565
4100
|
const release = async () => {
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
4101
|
+
released ??= portLease.release().catch((error) => {
|
|
4102
|
+
released = void 0;
|
|
4103
|
+
throw error;
|
|
4104
|
+
});
|
|
4105
|
+
await released;
|
|
3569
4106
|
};
|
|
3570
4107
|
const target = miniProgram;
|
|
3571
4108
|
const rawClose = target.close;
|
|
3572
4109
|
const rawDisconnect = target.disconnect;
|
|
3573
4110
|
if (typeof rawClose !== "function" && typeof rawDisconnect !== "function") return false;
|
|
3574
|
-
if (typeof rawDisconnect === "function")
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
4111
|
+
if (typeof rawDisconnect === "function") {
|
|
4112
|
+
let disconnecting;
|
|
4113
|
+
target.disconnect = function disconnectWithPortLeaseRelease() {
|
|
4114
|
+
disconnecting ??= (async () => {
|
|
4115
|
+
await rawDisconnect.call(this);
|
|
4116
|
+
await release();
|
|
4117
|
+
})().catch((error) => {
|
|
4118
|
+
disconnecting = void 0;
|
|
4119
|
+
throw error;
|
|
4120
|
+
});
|
|
4121
|
+
return disconnecting;
|
|
4122
|
+
};
|
|
4123
|
+
}
|
|
3581
4124
|
if (typeof rawClose === "function") target.close = async function closeWithPortLeaseRelease() {
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
} finally {
|
|
3585
|
-
await release();
|
|
3586
|
-
}
|
|
4125
|
+
await rawClose.call(this);
|
|
4126
|
+
await release();
|
|
3587
4127
|
};
|
|
3588
4128
|
return true;
|
|
3589
4129
|
}
|
|
@@ -3625,7 +4165,7 @@ var Launcher = class {
|
|
|
3625
4165
|
} catch (error) {
|
|
3626
4166
|
scope.recordFailure(error);
|
|
3627
4167
|
scope.throwIfAborted();
|
|
3628
|
-
if (!isRecoverableOperationError(error) || attempt >= attempts) throw error;
|
|
4168
|
+
if (error instanceof AggregateError || !isRecoverableOperationError(error) || attempt >= attempts) throw error;
|
|
3629
4169
|
}
|
|
3630
4170
|
}
|
|
3631
4171
|
});
|
|
@@ -3639,10 +4179,35 @@ var Launcher = class {
|
|
|
3639
4179
|
});
|
|
3640
4180
|
const rawReleaseLease = portLease.release.bind(portLease);
|
|
3641
4181
|
let releasedLease;
|
|
3642
|
-
portLease.release = () => releasedLease ??= rawReleaseLease()
|
|
3643
|
-
|
|
4182
|
+
portLease.release = () => releasedLease ??= rawReleaseLease().catch((error) => {
|
|
4183
|
+
releasedLease = void 0;
|
|
4184
|
+
throw error;
|
|
4185
|
+
});
|
|
4186
|
+
const connectionTasks = [];
|
|
4187
|
+
const disconnectingConnections = /* @__PURE__ */ new WeakMap();
|
|
4188
|
+
const disconnectConnection = (program) => {
|
|
4189
|
+
const pending = disconnectingConnections.get(program);
|
|
4190
|
+
if (pending) return pending;
|
|
4191
|
+
const closing = Promise.resolve(program.disconnect?.()).catch((error) => {
|
|
4192
|
+
disconnectingConnections.delete(program);
|
|
4193
|
+
throw error;
|
|
4194
|
+
});
|
|
4195
|
+
disconnectingConnections.set(program, closing);
|
|
4196
|
+
return closing;
|
|
4197
|
+
};
|
|
4198
|
+
const releaseConnectionsAndLease = async () => {
|
|
4199
|
+
const results = await Promise.allSettled(connectionTasks);
|
|
4200
|
+
const errors = (await Promise.allSettled(results.map((result) => result.status === "fulfilled" ? disconnectConnection(result.value) : Promise.resolve()))).filter((result) => result.status === "rejected").map((result) => result.reason);
|
|
4201
|
+
if (errors.length) throw new AggregateError(errors, "Automator connections did not finish closing; port lease retained");
|
|
4202
|
+
await portLease.release();
|
|
4203
|
+
};
|
|
4204
|
+
const disownLease = scope.own(releaseConnectionsAndLease, "port-lease");
|
|
3644
4205
|
let releasePortLeaseOnExit = true;
|
|
3645
4206
|
let releaseChild = async () => {};
|
|
4207
|
+
let diagnoseFailure = (error) => error;
|
|
4208
|
+
let failure;
|
|
4209
|
+
let failed = false;
|
|
4210
|
+
let result;
|
|
3646
4211
|
try {
|
|
3647
4212
|
const port = portLease.port;
|
|
3648
4213
|
if (!cliPath) throw new Error("Wechat web devTools not found, please specify cliPath option");
|
|
@@ -3656,6 +4221,7 @@ var Launcher = class {
|
|
|
3656
4221
|
let httpFallbackError = null;
|
|
3657
4222
|
let targetPort = port;
|
|
3658
4223
|
const cli = spawnWechatCli(cliPath, args, cwd, scope);
|
|
4224
|
+
diagnoseFailure = (error) => attachCliDiagnostics(error, cli, [ticket, account]);
|
|
3659
4225
|
releaseChild = cli.release;
|
|
3660
4226
|
let miniProgram = null;
|
|
3661
4227
|
let lastConnectError = null;
|
|
@@ -3687,40 +4253,49 @@ var Launcher = class {
|
|
|
3687
4253
|
}
|
|
3688
4254
|
const connectTimeout = resolveRemainingTimeout();
|
|
3689
4255
|
if (connectTimeout <= 0) return false;
|
|
3690
|
-
const candidate = await scope.step(() =>
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
4256
|
+
const candidate = await scope.step(() => {
|
|
4257
|
+
const connecting = this.connectTool({
|
|
4258
|
+
signal: scope.signal,
|
|
4259
|
+
timeout: Math.min(3e3, connectTimeout),
|
|
4260
|
+
wsEndpoint: `ws://127.0.0.1:${targetPort}`
|
|
4261
|
+
});
|
|
4262
|
+
connectionTasks.push(connecting);
|
|
4263
|
+
return connecting;
|
|
4264
|
+
}, {
|
|
3695
4265
|
stage: "websocket",
|
|
3696
|
-
disposeLate:
|
|
4266
|
+
disposeLate: disconnectConnection
|
|
3697
4267
|
});
|
|
3698
|
-
let releasedCandidate
|
|
4268
|
+
let releasedCandidate;
|
|
3699
4269
|
const releaseCandidate = () => {
|
|
3700
|
-
|
|
3701
|
-
releasedCandidate =
|
|
3702
|
-
|
|
3703
|
-
}
|
|
4270
|
+
return releasedCandidate ??= disconnectConnection(candidate).catch((error) => {
|
|
4271
|
+
releasedCandidate = void 0;
|
|
4272
|
+
throw error;
|
|
4273
|
+
});
|
|
3704
4274
|
};
|
|
3705
4275
|
const disownCandidate = scope.own(releaseCandidate, "websocket");
|
|
3706
4276
|
try {
|
|
3707
4277
|
const checkVersionTimeout = resolveRemainingTimeout();
|
|
3708
4278
|
if (checkVersionTimeout <= 0) {
|
|
3709
|
-
releaseCandidate
|
|
4279
|
+
await scope.step(releaseCandidate);
|
|
3710
4280
|
return false;
|
|
3711
4281
|
}
|
|
3712
4282
|
await scope.step(() => candidate.checkVersion(Math.min(VERSION_CHECK_TIMEOUT, checkVersionTimeout)), { stage: "version" });
|
|
3713
4283
|
if (typeof candidate.waitForAppReady === "function") {
|
|
3714
4284
|
const appReadyTimeout = resolveRemainingTimeout();
|
|
3715
4285
|
if (appReadyTimeout <= 0) {
|
|
3716
|
-
releaseCandidate
|
|
4286
|
+
await scope.step(releaseCandidate);
|
|
3717
4287
|
return false;
|
|
3718
4288
|
}
|
|
3719
4289
|
await scope.step(() => candidate.waitForAppReady(appReadyTimeout), { stage: "app-ready" });
|
|
3720
4290
|
}
|
|
3721
4291
|
} catch (error) {
|
|
4292
|
+
try {
|
|
4293
|
+
await scope.step(releaseCandidate);
|
|
4294
|
+
} catch (cleanupError) {
|
|
4295
|
+
if (cleanupError !== error) throw new AggregateError([error, cleanupError], "Automator validation and connection cleanup failed", { cause: error });
|
|
4296
|
+
throw error;
|
|
4297
|
+
}
|
|
3722
4298
|
disownCandidate();
|
|
3723
|
-
releaseCandidate();
|
|
3724
4299
|
lastConnectError = error;
|
|
3725
4300
|
if (isRecoverableOperationError(error)) return false;
|
|
3726
4301
|
throw error;
|
|
@@ -3731,7 +4306,7 @@ var Launcher = class {
|
|
|
3731
4306
|
lastConnectError = error;
|
|
3732
4307
|
scope.recordFailure(error);
|
|
3733
4308
|
scope.throwIfAborted();
|
|
3734
|
-
if (!isRecoverableOperationError(error)) throw error;
|
|
4309
|
+
if (error instanceof AggregateError || !isRecoverableOperationError(error)) throw error;
|
|
3735
4310
|
return false;
|
|
3736
4311
|
}
|
|
3737
4312
|
}, scope.remainingMs(), 1e3, scope.signal), { stage: "readiness" });
|
|
@@ -3749,15 +4324,30 @@ var Launcher = class {
|
|
|
3749
4324
|
wsEndpoint: `ws://127.0.0.1:${targetPort}`
|
|
3750
4325
|
});
|
|
3751
4326
|
releasePortLeaseOnExit = !retainPortLeaseUntilSessionClose(resolvedMiniProgram, portLease);
|
|
4327
|
+
if (!releasePortLeaseOnExit) disownLease();
|
|
4328
|
+
result = resolvedMiniProgram;
|
|
4329
|
+
} catch (error) {
|
|
4330
|
+
failed = true;
|
|
4331
|
+
failure = diagnoseFailure(error);
|
|
4332
|
+
}
|
|
4333
|
+
const cleanupErrors = [];
|
|
4334
|
+
try {
|
|
4335
|
+
await scope.step(releaseChild);
|
|
4336
|
+
} catch (error) {
|
|
4337
|
+
if (!failed || error !== failure) cleanupErrors.push(error);
|
|
4338
|
+
}
|
|
4339
|
+
if (releasePortLeaseOnExit) try {
|
|
4340
|
+
await scope.step(releaseConnectionsAndLease);
|
|
3752
4341
|
disownLease();
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
await releaseChild();
|
|
3756
|
-
if (releasePortLeaseOnExit) {
|
|
3757
|
-
disownLease();
|
|
3758
|
-
await portLease.release();
|
|
3759
|
-
}
|
|
4342
|
+
} catch (error) {
|
|
4343
|
+
if ((!failed || error !== failure) && !cleanupErrors.includes(error)) cleanupErrors.push(error);
|
|
3760
4344
|
}
|
|
4345
|
+
if (cleanupErrors.length) {
|
|
4346
|
+
if (failed) throw new AggregateError([failure, ...cleanupErrors], "Automator launch and resource cleanup failed", { cause: failure });
|
|
4347
|
+
throw new AggregateError(cleanupErrors, "Automator launch resource cleanup failed");
|
|
4348
|
+
}
|
|
4349
|
+
if (failed) throw failure;
|
|
4350
|
+
return result;
|
|
3761
4351
|
}
|
|
3762
4352
|
async connect(options) {
|
|
3763
4353
|
if (normalizePlatform(options.platform) === "swan") return await new SwanLauncher().connect(options);
|
|
@@ -3802,8 +4392,7 @@ var Launcher = class {
|
|
|
3802
4392
|
}
|
|
3803
4393
|
}
|
|
3804
4394
|
};
|
|
3805
|
-
|
|
3806
|
-
//#region src/Automator.ts
|
|
4395
|
+
// src/Automator.ts
|
|
3807
4396
|
/** Automator 的实现。 */
|
|
3808
4397
|
var Automator = class {
|
|
3809
4398
|
Element = Element;
|
|
@@ -3823,11 +4412,9 @@ var Automator = class {
|
|
|
3823
4412
|
return await this.launcher.launch(options);
|
|
3824
4413
|
}
|
|
3825
4414
|
};
|
|
3826
|
-
|
|
3827
|
-
//#region src/index.ts
|
|
4415
|
+
// src/index.ts
|
|
3828
4416
|
/**
|
|
3829
4417
|
* @file miniprogram-automator 对外导出入口。
|
|
3830
4418
|
*/
|
|
3831
4419
|
const SmartappAutomator = smartappAutomatorRuntime;
|
|
3832
|
-
//#endregion
|
|
3833
4420
|
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 };
|