@weapp-vite/miniprogram-automator 1.2.16 → 1.2.18
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 +15 -0
- package/dist/index.d.mts +55 -23
- package/dist/index.mjs +198 -26
- package/dist/{launch-Dqnd-Lqe.mjs → launch-Bvji0XA8.mjs} +23737 -2009
- package/package.json +10 -6
package/README.md
CHANGED
|
@@ -126,6 +126,21 @@ await page.waitFor(500)
|
|
|
126
126
|
|
|
127
127
|
> 降级元素上的不支持操作会**立即抛出带替代建议的错误**,不会再像旧版那样挂起 30s 才超时。
|
|
128
128
|
|
|
129
|
+
### 4.6 结构化错误日志
|
|
130
|
+
|
|
131
|
+
`enableLog(timeout, { structured: true })` 先通过 `App.CDPEnable` 注册事件通道,再启用 Runtime,保留 CDP 原始日志参数,并读取 Error 的非枚举 `name`、`message`、`stack` 数据属性,避免 SDK 将错误序列化为 `{}`。普通对象仍是原始 RemoteObject;不会执行 getter 或远程函数。默认不传选项时继续使用 SDK 日志格式。
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
miniProgram.addListener('console', (entry) => {
|
|
135
|
+
console.log(entry)
|
|
136
|
+
})
|
|
137
|
+
await miniProgram.enableLog(3_000, { structured: true })
|
|
138
|
+
await miniProgram.reLaunch('/pages/index/index')
|
|
139
|
+
await miniProgram.flushConsole()
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
先用 `addListener` 被动注册,再显式等待 `enableLog`,可以记录初始化期间的日志。`flushConsole()` 等待调用时已收到的错误属性检查完成;每次查询最多等待 1 秒,失败会在原参数上保留 `inspectionError`。断开连接时尚未完成的日志也会携带该诊断发布。只有宿主明确返回 CDP 命令不支持时才回退 SDK 格式;其他初始化错误仍然抛出。重复调用 `enableLog` 会刷新 Runtime 订阅,并保留已选择的格式。
|
|
143
|
+
|
|
129
144
|
## 5. 主要导出
|
|
130
145
|
|
|
131
146
|
| 导出 | 说明 |
|
package/dist/index.d.mts
CHANGED
|
@@ -63,6 +63,28 @@ declare class Connection extends EventEmitter {
|
|
|
63
63
|
static create(url: string, timeout?: number): Promise<Connection>;
|
|
64
64
|
}
|
|
65
65
|
//#endregion
|
|
66
|
+
//#region src/structuredConsole.d.ts
|
|
67
|
+
interface ConsoleLogOptions {
|
|
68
|
+
/** 使用 CDP 原始参数,并读取 Error 的非枚举数据属性;默认保留 SDK 日志格式。 */
|
|
69
|
+
structured?: boolean;
|
|
70
|
+
}
|
|
71
|
+
interface ConsoleRemoteObject {
|
|
72
|
+
type?: string;
|
|
73
|
+
subtype?: string;
|
|
74
|
+
className?: string;
|
|
75
|
+
objectId?: string;
|
|
76
|
+
description?: string;
|
|
77
|
+
value?: unknown;
|
|
78
|
+
/** 属性查询失败时保留原参数和诊断,不把不可检查的错误当作成功。 */
|
|
79
|
+
inspectionError?: string;
|
|
80
|
+
[key: string]: unknown;
|
|
81
|
+
}
|
|
82
|
+
interface StructuredConsoleEntry {
|
|
83
|
+
type: string;
|
|
84
|
+
args: ConsoleRemoteObject[];
|
|
85
|
+
[key: string]: unknown;
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
66
88
|
//#region src/Native.d.ts
|
|
67
89
|
/** ISwitchTabOptions 的类型定义。 */
|
|
68
90
|
interface ISwitchTabOptions {
|
|
@@ -169,45 +191,45 @@ declare class Element {
|
|
|
169
191
|
static create(connection: Connection, options: IElementOptions, elementMap: ElementMap): Element;
|
|
170
192
|
}
|
|
171
193
|
/** CustomElement 的实现。 */
|
|
172
|
-
declare class CustomElement extends Element {
|
|
194
|
+
export declare class CustomElement extends Element {
|
|
173
195
|
setData(data: any): Promise<void>;
|
|
174
196
|
data(path?: string): Promise<any>;
|
|
175
197
|
callMethod(method: string, ...args: any[]): Promise<any>;
|
|
176
198
|
}
|
|
177
199
|
/** InputElement 的实现。 */
|
|
178
|
-
declare class InputElement extends Element {
|
|
200
|
+
export declare class InputElement extends Element {
|
|
179
201
|
input(value: string): Promise<void>;
|
|
180
202
|
}
|
|
181
203
|
/** TextareaElement 的实现。 */
|
|
182
|
-
declare class TextareaElement extends Element {
|
|
204
|
+
export declare class TextareaElement extends Element {
|
|
183
205
|
input(value: string): Promise<void>;
|
|
184
206
|
}
|
|
185
207
|
/** ScrollViewElement 的实现。 */
|
|
186
|
-
declare class ScrollViewElement extends Element {
|
|
208
|
+
export declare class ScrollViewElement extends Element {
|
|
187
209
|
scrollTo(x: number, y: number): Promise<void>;
|
|
188
210
|
property(name: string): Promise<any>;
|
|
189
211
|
scrollWidth(): Promise<any>;
|
|
190
212
|
scrollHeight(): Promise<any>;
|
|
191
213
|
}
|
|
192
214
|
/** SwiperElement 的实现。 */
|
|
193
|
-
declare class SwiperElement extends Element {
|
|
215
|
+
export declare class SwiperElement extends Element {
|
|
194
216
|
swipeTo(index: number): Promise<void>;
|
|
195
217
|
}
|
|
196
218
|
/** MovableViewElement 的实现。 */
|
|
197
|
-
declare class MovableViewElement extends Element {
|
|
219
|
+
export declare class MovableViewElement extends Element {
|
|
198
220
|
moveTo(x: number, y: number): Promise<void>;
|
|
199
221
|
property(name: string): Promise<any>;
|
|
200
222
|
}
|
|
201
223
|
/** SwitchElement 的实现。 */
|
|
202
|
-
declare class SwitchElement extends Element {
|
|
224
|
+
export declare class SwitchElement extends Element {
|
|
203
225
|
tap(): Promise<void>;
|
|
204
226
|
}
|
|
205
227
|
/** SliderElement 的实现。 */
|
|
206
|
-
declare class SliderElement extends Element {
|
|
228
|
+
export declare class SliderElement extends Element {
|
|
207
229
|
slideTo(value: number): Promise<void>;
|
|
208
230
|
}
|
|
209
231
|
/** ContextElement 的实现。 */
|
|
210
|
-
declare class ContextElement extends Element {
|
|
232
|
+
export declare class ContextElement extends Element {
|
|
211
233
|
callContextMethod(method: string, ...args: any[]): Promise<any>;
|
|
212
234
|
}
|
|
213
235
|
//#endregion
|
|
@@ -215,6 +237,7 @@ declare class ContextElement extends Element {
|
|
|
215
237
|
/** IPageOptions 的类型定义。 */
|
|
216
238
|
interface IPageOptions {
|
|
217
239
|
id: number;
|
|
240
|
+
hasStableIdentity?: boolean;
|
|
218
241
|
path: string;
|
|
219
242
|
query: any;
|
|
220
243
|
}
|
|
@@ -261,9 +284,12 @@ declare class Page {
|
|
|
261
284
|
path: string;
|
|
262
285
|
query: any;
|
|
263
286
|
private id;
|
|
287
|
+
private hasStableIdentity;
|
|
264
288
|
private elementMap;
|
|
265
289
|
private preferAppServicePageProtocol;
|
|
266
290
|
constructor(connection: Connection, options: IPageOptions);
|
|
291
|
+
/** 当前页面的协议帧身份,同路由新页面具有独立身份。 */
|
|
292
|
+
get pageId(): number;
|
|
267
293
|
updateFromOptions(options: IPageOptions): void;
|
|
268
294
|
waitFor(condition: WaitCondition): Promise<void>;
|
|
269
295
|
$(selector: string, options?: PageQueryOptions): Promise<Element | null>;
|
|
@@ -313,6 +339,7 @@ interface IToolClearCacheOptions {
|
|
|
313
339
|
type AutomatorCallable = (...args: any[]) => any;
|
|
314
340
|
interface CurrentPageOptions {
|
|
315
341
|
appFunctionFallback?: boolean;
|
|
342
|
+
pageStackFallback?: boolean;
|
|
316
343
|
retries?: number;
|
|
317
344
|
timeout?: number;
|
|
318
345
|
}
|
|
@@ -324,6 +351,9 @@ declare class MiniProgram extends EventEmitter {
|
|
|
324
351
|
private connection;
|
|
325
352
|
private appBindings;
|
|
326
353
|
private logEnabled;
|
|
354
|
+
private logEnabling?;
|
|
355
|
+
private structuredLogs;
|
|
356
|
+
private structuredConsole;
|
|
327
357
|
private pageMap;
|
|
328
358
|
private nativeIns?;
|
|
329
359
|
constructor(connection: Connection);
|
|
@@ -351,7 +381,9 @@ declare class MiniProgram extends EventEmitter {
|
|
|
351
381
|
close(): Promise<void>;
|
|
352
382
|
remote(auto?: boolean): Promise<void>;
|
|
353
383
|
disconnect(): void;
|
|
354
|
-
|
|
384
|
+
/** 等待已接收的结构化日志完成 Error 属性检查并发布。 */
|
|
385
|
+
flushConsole(): Promise<void>;
|
|
386
|
+
enableLog(timeout?: number, options?: ConsoleLogOptions): Promise<void>;
|
|
355
387
|
on(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
356
388
|
exposeFunction(name: string, bindingFunction: AutomatorCallable): Promise<void>;
|
|
357
389
|
checkVersion(timeout?: number): Promise<void>;
|
|
@@ -553,8 +585,8 @@ declare function devices(deviceType: string, _connectType?: string): Promise<Sma
|
|
|
553
585
|
/**
|
|
554
586
|
* @file 小程序自动化平台适配定义。
|
|
555
587
|
*/
|
|
556
|
-
type MiniprogramAutomatorPlatform = 'wechat' | 'swan' | 'baidu';
|
|
557
|
-
interface IExternalAutomatorModule {
|
|
588
|
+
export type MiniprogramAutomatorPlatform = 'wechat' | 'swan' | 'baidu';
|
|
589
|
+
export interface IExternalAutomatorModule {
|
|
558
590
|
launch: (options: Record<string, unknown>) => Promise<unknown>;
|
|
559
591
|
connect?: (options: Record<string, unknown>) => Promise<unknown>;
|
|
560
592
|
devices?: (deviceType: string, connectType?: string) => Promise<unknown>;
|
|
@@ -563,17 +595,17 @@ interface IExternalAutomatorModule {
|
|
|
563
595
|
log?: unknown;
|
|
564
596
|
time?: unknown;
|
|
565
597
|
}
|
|
566
|
-
type NormalizedMiniprogramAutomatorPlatform = Exclude<MiniprogramAutomatorPlatform, 'baidu'>;
|
|
567
|
-
declare function normalizePlatform(platform?: MiniprogramAutomatorPlatform): NormalizedMiniprogramAutomatorPlatform;
|
|
598
|
+
export type NormalizedMiniprogramAutomatorPlatform = Exclude<MiniprogramAutomatorPlatform, 'baidu'>;
|
|
599
|
+
export declare function normalizePlatform(platform?: MiniprogramAutomatorPlatform): NormalizedMiniprogramAutomatorPlatform;
|
|
568
600
|
//#endregion
|
|
569
601
|
//#region src/Launcher.d.ts
|
|
570
|
-
interface IConnectOptions {
|
|
602
|
+
export interface IConnectOptions {
|
|
571
603
|
wsEndpoint: string;
|
|
572
604
|
timeout?: number;
|
|
573
605
|
platform?: MiniprogramAutomatorPlatform;
|
|
574
606
|
}
|
|
575
607
|
/** ILaunchOptions 的类型定义。 */
|
|
576
|
-
interface ILaunchOptions {
|
|
608
|
+
export interface ILaunchOptions {
|
|
577
609
|
platform?: MiniprogramAutomatorPlatform;
|
|
578
610
|
cliPath?: string;
|
|
579
611
|
connectType?: string;
|
|
@@ -601,7 +633,7 @@ interface ILaunchOptions {
|
|
|
601
633
|
wdaProjPath?: string;
|
|
602
634
|
webModel?: string;
|
|
603
635
|
}
|
|
604
|
-
interface ILauncherSessionMetadata {
|
|
636
|
+
export interface ILauncherSessionMetadata {
|
|
605
637
|
port: number;
|
|
606
638
|
projectPath: string;
|
|
607
639
|
wsEndpoint: string;
|
|
@@ -635,16 +667,16 @@ declare class Automator {
|
|
|
635
667
|
//#endregion
|
|
636
668
|
//#region src/util.d.ts
|
|
637
669
|
/** decodeQrCode 的方法封装。 */
|
|
638
|
-
declare function decodeQrCode(qrCode: string): Promise<string>;
|
|
670
|
+
export declare function decodeQrCode(qrCode: string): Promise<string>;
|
|
639
671
|
/** printQrCode 的方法封装。 */
|
|
640
|
-
declare function printQrCode(content: string): Promise<void>;
|
|
672
|
+
export declare function printQrCode(content: string): Promise<void>;
|
|
641
673
|
/** isPluginPath 的方法封装。 */
|
|
642
|
-
declare function isPluginPath(p: unknown): boolean;
|
|
674
|
+
export declare function isPluginPath(p: unknown): boolean;
|
|
643
675
|
/** extractPluginId 的方法封装。 */
|
|
644
|
-
declare function extractPluginId(p: unknown): string;
|
|
676
|
+
export declare function extractPluginId(p: unknown): string;
|
|
645
677
|
//#endregion
|
|
646
678
|
//#region src/index.d.ts
|
|
647
|
-
declare const SmartappAutomator: {
|
|
679
|
+
export declare const SmartappAutomator: {
|
|
648
680
|
connect: typeof connect;
|
|
649
681
|
devices: typeof devices;
|
|
650
682
|
file: {
|
|
@@ -669,4 +701,4 @@ declare const SmartappAutomator: {
|
|
|
669
701
|
};
|
|
670
702
|
};
|
|
671
703
|
//#endregion
|
|
672
|
-
export { Automator, Connection,
|
|
704
|
+
export { Automator, Connection, type ConsoleLogOptions, type ConsoleRemoteObject, Element, Launcher, MiniProgram, Native, Page, type StructuredConsoleEntry, Transport };
|
package/dist/index.mjs
CHANGED
|
@@ -364,13 +364,14 @@ const ROUTE_ELEMENT_SNAPSHOT_FUNCTION_DECLARATION = `function (route, query, sel
|
|
|
364
364
|
}
|
|
365
365
|
selectorQuery
|
|
366
366
|
.selectAll(selector)
|
|
367
|
-
.
|
|
367
|
+
// 宿主 JSON.parse 创建可跨 DevTools evaluate 上下文传输的查询选项。
|
|
368
|
+
.fields(JSON.parse(JSON.stringify({
|
|
368
369
|
id: true,
|
|
369
370
|
dataset: true,
|
|
370
371
|
rect: true,
|
|
371
372
|
size: true,
|
|
372
373
|
computedStyle: Array.isArray(styleNames) ? styleNames : []
|
|
373
|
-
}, function (nodes) {
|
|
374
|
+
})), function (nodes) {
|
|
374
375
|
finish(nodes);
|
|
375
376
|
})
|
|
376
377
|
.exec();
|
|
@@ -942,7 +943,7 @@ var Connection = class Connection extends EventEmitter {
|
|
|
942
943
|
//#endregion
|
|
943
944
|
//#region src/headless.ts
|
|
944
945
|
async function launchHeadlessAutomator(options) {
|
|
945
|
-
return await (await import("./launch-
|
|
946
|
+
return await (await import("./launch-Bvji0XA8.mjs")).launch({ projectPath: options.projectPath });
|
|
946
947
|
}
|
|
947
948
|
//#endregion
|
|
948
949
|
//#region src/launcher/portLease.ts
|
|
@@ -1056,20 +1057,20 @@ async function enableAutomatorViaHttp(options) {
|
|
|
1056
1057
|
if (options.trustProject) endpoint.searchParams.set("trustProject", "true");
|
|
1057
1058
|
const response = await fetch(endpoint, { redirect: "follow" });
|
|
1058
1059
|
const body = await response.text();
|
|
1059
|
-
if (!response.ok) throw new Error(`WeChat DevTools HTTP automator fallback failed with status ${response.status}
|
|
1060
|
+
if (!response.ok) throw new Error(`WeChat DevTools HTTP automator fallback failed with status ${response.status}`);
|
|
1060
1061
|
let result;
|
|
1061
1062
|
try {
|
|
1062
1063
|
result = JSON.parse(body);
|
|
1063
|
-
} catch
|
|
1064
|
-
throw new Error("WeChat DevTools HTTP automator fallback returned invalid JSON"
|
|
1064
|
+
} catch {
|
|
1065
|
+
throw new Error("WeChat DevTools HTTP automator fallback returned invalid JSON");
|
|
1065
1066
|
}
|
|
1066
1067
|
if (result && typeof result === "object" && "autoPort" in result) {
|
|
1067
1068
|
const autoPort = Number(result.autoPort);
|
|
1068
|
-
if (!Number.isInteger(autoPort) || autoPort <= 0 || autoPort > 65535) throw new Error(
|
|
1069
|
+
if (!Number.isInteger(autoPort) || autoPort <= 0 || autoPort > 65535) throw new Error("WeChat DevTools HTTP automator fallback returned invalid autoPort");
|
|
1069
1070
|
return autoPort;
|
|
1070
1071
|
}
|
|
1071
|
-
if (typeof result === "string" && /^s\d+$/.test(result)) return options.autoPort;
|
|
1072
|
-
throw new Error(
|
|
1072
|
+
if (typeof result === "string" && (/^s\d+$/.test(result) || /^[0-9a-f]{32}$/i.test(result))) return options.autoPort;
|
|
1073
|
+
throw new Error("WeChat DevTools HTTP automator fallback returned unsupported response");
|
|
1073
1074
|
}
|
|
1074
1075
|
const AUTOMATOR_VALUE_OPTIONS = /* @__PURE__ */ new Set([
|
|
1075
1076
|
"--auto-account",
|
|
@@ -1097,7 +1098,7 @@ function resolveWechatDevtoolsBootstrapArgs(args) {
|
|
|
1097
1098
|
}
|
|
1098
1099
|
//#endregion
|
|
1099
1100
|
//#region package.json
|
|
1100
|
-
var version = "1.2.
|
|
1101
|
+
var version = "1.2.18";
|
|
1101
1102
|
//#endregion
|
|
1102
1103
|
//#region src/Native.ts
|
|
1103
1104
|
/** Native 的实现。 */
|
|
@@ -1384,16 +1385,24 @@ var Page = class Page {
|
|
|
1384
1385
|
path = "";
|
|
1385
1386
|
query = {};
|
|
1386
1387
|
id;
|
|
1388
|
+
hasStableIdentity;
|
|
1387
1389
|
elementMap = /* @__PURE__ */ new Map();
|
|
1388
1390
|
preferAppServicePageProtocol = false;
|
|
1389
1391
|
constructor(connection, options) {
|
|
1390
1392
|
this.connection = connection;
|
|
1391
1393
|
this.id = options.id;
|
|
1394
|
+
this.hasStableIdentity = options.hasStableIdentity !== false;
|
|
1392
1395
|
this.path = options.path;
|
|
1393
1396
|
this.query = options.query;
|
|
1394
1397
|
this.preferAppServicePageProtocol = connection.prefersAppServicePageProtocol;
|
|
1395
1398
|
}
|
|
1399
|
+
/** 当前页面的协议帧身份,同路由新页面具有独立身份。 */
|
|
1400
|
+
get pageId() {
|
|
1401
|
+
if (!this.hasStableIdentity) throw new Error("DevTools did not provide a stable page frame identity");
|
|
1402
|
+
return this.id;
|
|
1403
|
+
}
|
|
1396
1404
|
updateFromOptions(options) {
|
|
1405
|
+
this.hasStableIdentity = options.hasStableIdentity !== false;
|
|
1397
1406
|
this.path = options.path;
|
|
1398
1407
|
this.query = options.query;
|
|
1399
1408
|
}
|
|
@@ -1443,6 +1452,7 @@ var Page = class Page {
|
|
|
1443
1452
|
async getElementByXpath(selector, options = {}) {
|
|
1444
1453
|
try {
|
|
1445
1454
|
const element = await this.send("Page.getElementByXpath", { selector }, { timeout: options.timeout ?? PAGE_QUERY_TIMEOUT });
|
|
1455
|
+
if (element === null) return null;
|
|
1446
1456
|
return Element.create(this.connection, {
|
|
1447
1457
|
...element,
|
|
1448
1458
|
pageId: this.id
|
|
@@ -1452,7 +1462,8 @@ var Page = class Page {
|
|
|
1452
1462
|
}
|
|
1453
1463
|
}
|
|
1454
1464
|
async getElementsByXpath(selector, options = {}) {
|
|
1455
|
-
const
|
|
1465
|
+
const response = await this.send("Page.getElementsByXpath", { selector }, { timeout: options.timeout ?? PAGE_QUERY_TIMEOUT });
|
|
1466
|
+
const elements = Array.isArray(response) ? response : response?.elements;
|
|
1456
1467
|
if (!Array.isArray(elements)) throw new TypeError("DevTools Page.getElementsByXpath 返回了无效响应:缺少 elements 数组。");
|
|
1457
1468
|
return elements.map((element) => {
|
|
1458
1469
|
return Element.create(this.connection, {
|
|
@@ -1614,12 +1625,13 @@ var Page = class Page {
|
|
|
1614
1625
|
}
|
|
1615
1626
|
query
|
|
1616
1627
|
.selectAll(selector)
|
|
1617
|
-
.
|
|
1628
|
+
// 宿主 JSON.parse 创建可跨 DevTools evaluate 上下文传输的查询选项。
|
|
1629
|
+
.fields(JSON.parse(JSON.stringify({
|
|
1618
1630
|
dataset: true,
|
|
1619
1631
|
id: true,
|
|
1620
1632
|
rect: true,
|
|
1621
1633
|
size: true
|
|
1622
|
-
}, function (nodes) {
|
|
1634
|
+
})), function (nodes) {
|
|
1623
1635
|
finish(nodes);
|
|
1624
1636
|
})
|
|
1625
1637
|
.exec();
|
|
@@ -1798,12 +1810,13 @@ var Page = class Page {
|
|
|
1798
1810
|
}
|
|
1799
1811
|
query
|
|
1800
1812
|
.selectAll(selector)
|
|
1801
|
-
.
|
|
1813
|
+
// 宿主 JSON.parse 创建可跨 DevTools evaluate 上下文传输的查询选项。
|
|
1814
|
+
.fields(JSON.parse(JSON.stringify({
|
|
1802
1815
|
dataset: true,
|
|
1803
1816
|
id: true,
|
|
1804
1817
|
rect: true,
|
|
1805
1818
|
size: true
|
|
1806
|
-
}, function (nodes) {
|
|
1819
|
+
})), function (nodes) {
|
|
1807
1820
|
finish(nodes);
|
|
1808
1821
|
})
|
|
1809
1822
|
.exec();
|
|
@@ -2182,6 +2195,100 @@ var Page = class Page {
|
|
|
2182
2195
|
}
|
|
2183
2196
|
};
|
|
2184
2197
|
//#endregion
|
|
2198
|
+
//#region src/structuredConsole.ts
|
|
2199
|
+
function isRecord(value) {
|
|
2200
|
+
return value !== null && typeof value === "object";
|
|
2201
|
+
}
|
|
2202
|
+
function isRemoteError(arg) {
|
|
2203
|
+
return isRecord(arg) && arg.type === "object" && typeof arg.objectId === "string" && (arg.subtype === "error" || /^(?:Error|EvalError|RangeError|ReferenceError|SyntaxError|TypeError|URIError|AggregateError)$/.test(arg.className ?? ""));
|
|
2204
|
+
}
|
|
2205
|
+
/** 按原始事件顺序发布日志;查询仅读取 Error 数据描述符,不执行 getter 或远程函数。 */
|
|
2206
|
+
var StructuredConsole = class {
|
|
2207
|
+
send;
|
|
2208
|
+
emit;
|
|
2209
|
+
enabled = false;
|
|
2210
|
+
pending = [];
|
|
2211
|
+
constructor(send, emit) {
|
|
2212
|
+
this.send = send;
|
|
2213
|
+
this.emit = emit;
|
|
2214
|
+
}
|
|
2215
|
+
receive = (event) => {
|
|
2216
|
+
if (!this.enabled || !isRecord(event) || event.domain !== "Runtime" || event.event !== "consoleAPICalled") return;
|
|
2217
|
+
const params = event.params;
|
|
2218
|
+
if (!isRecord(params) || typeof params.type !== "string" || !Array.isArray(params.args)) return;
|
|
2219
|
+
const original = params;
|
|
2220
|
+
const entry = {
|
|
2221
|
+
original,
|
|
2222
|
+
task: Promise.resolve()
|
|
2223
|
+
};
|
|
2224
|
+
this.pending.push(entry);
|
|
2225
|
+
entry.task = Promise.all(original.args.map((arg) => this.inspect(arg))).then((args) => {
|
|
2226
|
+
if (!entry.result) {
|
|
2227
|
+
entry.result = {
|
|
2228
|
+
...original,
|
|
2229
|
+
args
|
|
2230
|
+
};
|
|
2231
|
+
this.drain();
|
|
2232
|
+
}
|
|
2233
|
+
});
|
|
2234
|
+
};
|
|
2235
|
+
async flush() {
|
|
2236
|
+
await Promise.all(this.pending.map((entry) => entry.task));
|
|
2237
|
+
}
|
|
2238
|
+
stop() {
|
|
2239
|
+
this.enabled = false;
|
|
2240
|
+
for (const entry of this.pending) entry.result ??= {
|
|
2241
|
+
...entry.original,
|
|
2242
|
+
args: entry.original.args.map((arg) => isRemoteError(arg) ? {
|
|
2243
|
+
...arg,
|
|
2244
|
+
inspectionError: "Console connection closed before Error inspection completed"
|
|
2245
|
+
} : arg)
|
|
2246
|
+
};
|
|
2247
|
+
this.drain();
|
|
2248
|
+
}
|
|
2249
|
+
drain() {
|
|
2250
|
+
while (this.pending[0]?.result) {
|
|
2251
|
+
const entry = this.pending.shift();
|
|
2252
|
+
this.emit(entry.result);
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
async inspect(arg) {
|
|
2256
|
+
if (!isRemoteError(arg)) return arg;
|
|
2257
|
+
try {
|
|
2258
|
+
const response = await this.send("App.CDPCommand", {
|
|
2259
|
+
domain: "Runtime",
|
|
2260
|
+
method: "getProperties",
|
|
2261
|
+
params: {
|
|
2262
|
+
objectId: arg.objectId,
|
|
2263
|
+
ownProperties: true,
|
|
2264
|
+
accessorPropertiesOnly: false,
|
|
2265
|
+
generatePreview: false
|
|
2266
|
+
}
|
|
2267
|
+
}, { timeout: 1e3 });
|
|
2268
|
+
if (!isRecord(response) || !Array.isArray(response.result) || response.exceptionDetails) throw new Error("Runtime.getProperties did not return Error data descriptors");
|
|
2269
|
+
const value = { name: arg.className ?? "Error" };
|
|
2270
|
+
for (const descriptor of response.result) {
|
|
2271
|
+
if (!isRecord(descriptor) || ![
|
|
2272
|
+
"name",
|
|
2273
|
+
"message",
|
|
2274
|
+
"stack"
|
|
2275
|
+
].includes(String(descriptor.name))) continue;
|
|
2276
|
+
if (!("get" in descriptor) && !("set" in descriptor) && isRecord(descriptor.value) && typeof descriptor.value.value === "string") value[String(descriptor.name)] = descriptor.value.value;
|
|
2277
|
+
}
|
|
2278
|
+
return {
|
|
2279
|
+
...arg,
|
|
2280
|
+
value,
|
|
2281
|
+
...!("message" in value) && !("stack" in value) ? { inspectionError: "Error has no readable message or stack data descriptors" } : {}
|
|
2282
|
+
};
|
|
2283
|
+
} catch (error) {
|
|
2284
|
+
return {
|
|
2285
|
+
...arg,
|
|
2286
|
+
inspectionError: error instanceof Error ? error.message : String(error)
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
};
|
|
2291
|
+
//#endregion
|
|
2185
2292
|
//#region src/util.ts
|
|
2186
2293
|
/**
|
|
2187
2294
|
* @file 二维码与插件路径工具。
|
|
@@ -2328,11 +2435,16 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2328
2435
|
connection;
|
|
2329
2436
|
appBindings = /* @__PURE__ */ new Map();
|
|
2330
2437
|
logEnabled = false;
|
|
2438
|
+
logEnabling;
|
|
2439
|
+
structuredLogs = false;
|
|
2440
|
+
structuredConsole;
|
|
2331
2441
|
pageMap = /* @__PURE__ */ new Map();
|
|
2332
2442
|
nativeIns;
|
|
2333
2443
|
constructor(connection) {
|
|
2334
2444
|
super();
|
|
2335
2445
|
this.connection = connection;
|
|
2446
|
+
this.structuredConsole = new StructuredConsole((method, params, options) => this.send(method, params, options), (entry) => this.emit("console", entry));
|
|
2447
|
+
connection.on("App.CDPEvent", this.structuredConsole.receive);
|
|
2336
2448
|
connection.on("App.logAdded", this.onLogAdded);
|
|
2337
2449
|
connection.on("App.bindingCalled", this.onBindingCalled);
|
|
2338
2450
|
connection.on("App.exceptionThrown", this.onExceptionThrown);
|
|
@@ -2377,7 +2489,7 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2377
2489
|
continue;
|
|
2378
2490
|
}
|
|
2379
2491
|
}
|
|
2380
|
-
if (isCurrentPageProtocolTimeout(lastError) || isPageMetaMissingError(lastError) || isCurrentFrameTimedOutError(lastError)) try {
|
|
2492
|
+
if ((options.pageStackFallback ?? true) && (isCurrentPageProtocolTimeout(lastError) || isPageMetaMissingError(lastError) || isCurrentFrameTimedOutError(lastError))) try {
|
|
2381
2493
|
const { pageStack } = await this.send("App.getPageStack", {}, sendOptions);
|
|
2382
2494
|
const page = pageStack[pageStack.length - 1];
|
|
2383
2495
|
if (page) return this.createPageFromPayload(page, pageStack.length);
|
|
@@ -2498,12 +2610,54 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2498
2610
|
});
|
|
2499
2611
|
}
|
|
2500
2612
|
disconnect() {
|
|
2613
|
+
this.structuredConsole.stop();
|
|
2501
2614
|
this.connection.dispose();
|
|
2502
2615
|
}
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2616
|
+
/** 等待已接收的结构化日志完成 Error 属性检查并发布。 */
|
|
2617
|
+
async flushConsole() {
|
|
2618
|
+
await this.structuredConsole.flush();
|
|
2619
|
+
}
|
|
2620
|
+
enableLog(timeout, options = {}) {
|
|
2621
|
+
if (this.logEnabling) {
|
|
2622
|
+
if (options.structured !== void 0 && options.structured !== this.structuredLogs) return Promise.reject(/* @__PURE__ */ new Error("Cannot change console format while logging is being enabled"));
|
|
2623
|
+
return this.logEnabling;
|
|
2624
|
+
}
|
|
2625
|
+
this.structuredLogs = options.structured ?? this.structuredLogs;
|
|
2626
|
+
this.structuredConsole.enabled = this.structuredLogs;
|
|
2627
|
+
this.logEnabled = false;
|
|
2628
|
+
const startedAt = Date.now();
|
|
2629
|
+
const request = async (method, params = {}) => {
|
|
2630
|
+
if (timeout === void 0) return await this.send(method, params);
|
|
2631
|
+
const remaining = timeout - (Date.now() - startedAt);
|
|
2632
|
+
if (remaining <= 0) throw new Error(`Timed out enabling console logging within ${timeout}ms`);
|
|
2633
|
+
return await this.send(method, params, { timeout: remaining });
|
|
2634
|
+
};
|
|
2635
|
+
this.logEnabling = (async () => {
|
|
2636
|
+
if (!this.structuredLogs) await request("App.enableLog");
|
|
2637
|
+
let cdpMethod = this.structuredLogs ? "App.CDPEnable" : "App.CDPCommand";
|
|
2638
|
+
try {
|
|
2639
|
+
if (this.structuredLogs) {
|
|
2640
|
+
await request(cdpMethod);
|
|
2641
|
+
cdpMethod = "App.CDPCommand";
|
|
2642
|
+
}
|
|
2643
|
+
await request("App.CDPCommand", {
|
|
2644
|
+
domain: "Runtime",
|
|
2645
|
+
method: "enable",
|
|
2646
|
+
params: {}
|
|
2647
|
+
});
|
|
2648
|
+
} catch (error) {
|
|
2649
|
+
if (!(error instanceof Error) || error.message !== `appservice ${cdpMethod} unimplemented`) throw error;
|
|
2650
|
+
if (this.structuredLogs) {
|
|
2651
|
+
this.structuredConsole.enabled = false;
|
|
2652
|
+
await request("App.enableLog");
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
this.logEnabled = true;
|
|
2656
|
+
})().finally(async () => {
|
|
2657
|
+
await this.flushConsole();
|
|
2658
|
+
this.logEnabling = void 0;
|
|
2659
|
+
});
|
|
2660
|
+
return this.logEnabling;
|
|
2507
2661
|
}
|
|
2508
2662
|
on(event, listener) {
|
|
2509
2663
|
if (event === "console" && !this.logEnabled) this.enableLog().catch(() => {});
|
|
@@ -2710,8 +2864,10 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2710
2864
|
}
|
|
2711
2865
|
}
|
|
2712
2866
|
createPageFromPayload(page, fallbackId = 1) {
|
|
2867
|
+
const protocolId = page.pageId ?? page.__wxWebviewId__ ?? page.__webviewId__;
|
|
2713
2868
|
return Page.create(this.connection, {
|
|
2714
2869
|
id: resolvePagePayloadId(page, fallbackId),
|
|
2870
|
+
hasStableIdentity: typeof protocolId === "number" && Number.isSafeInteger(protocolId) && protocolId >= 0,
|
|
2715
2871
|
path: resolvePagePayloadPath(page),
|
|
2716
2872
|
query: page.query ?? page.options ?? {}
|
|
2717
2873
|
}, this.pageMap);
|
|
@@ -2725,9 +2881,9 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2725
2881
|
const { result } = await this.send("App.callFunction", {
|
|
2726
2882
|
functionDeclaration: `function () {
|
|
2727
2883
|
var pages = typeof getCurrentPages === 'function' ? getCurrentPages() : [];
|
|
2728
|
-
return pages.map(function (page
|
|
2884
|
+
return pages.map(function (page) {
|
|
2729
2885
|
return {
|
|
2730
|
-
pageId: page.pageId || page.__wxWebviewId__ || page.__webviewId__
|
|
2886
|
+
pageId: page.pageId || page.__wxWebviewId__ || page.__webviewId__,
|
|
2731
2887
|
path: page.path || page.route || page.__route__ || '',
|
|
2732
2888
|
query: page.query || page.options || {}
|
|
2733
2889
|
};
|
|
@@ -2747,7 +2903,7 @@ var MiniProgram = class extends EventEmitter {
|
|
|
2747
2903
|
return options ? await this.connection.send(method, params, options) : await this.connection.send(method, params);
|
|
2748
2904
|
}
|
|
2749
2905
|
onLogAdded = (payload) => {
|
|
2750
|
-
this.emit("console", payload);
|
|
2906
|
+
if (!this.structuredConsole.enabled) this.emit("console", payload);
|
|
2751
2907
|
};
|
|
2752
2908
|
onBindingCalled = (payload) => {
|
|
2753
2909
|
const binding = this.appBindings.get(payload.name);
|
|
@@ -3426,9 +3582,25 @@ var Launcher = class {
|
|
|
3426
3582
|
}
|
|
3427
3583
|
async connect(options) {
|
|
3428
3584
|
if (normalizePlatform(options.platform) === "swan") return await new SwanLauncher().connect(options);
|
|
3429
|
-
const
|
|
3430
|
-
|
|
3431
|
-
|
|
3585
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
3586
|
+
const deadlineAt = performance.now() + timeout;
|
|
3587
|
+
const miniProgram = await this.connectTool({
|
|
3588
|
+
...options,
|
|
3589
|
+
timeout
|
|
3590
|
+
});
|
|
3591
|
+
try {
|
|
3592
|
+
const remaining = Math.ceil(deadlineAt - performance.now());
|
|
3593
|
+
if (remaining <= 0) throw new Error(`Timed out connecting to automator after ${timeout}ms`);
|
|
3594
|
+
await miniProgram.checkVersion(remaining);
|
|
3595
|
+
return miniProgram;
|
|
3596
|
+
} catch (error) {
|
|
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
|
+
}
|
|
3432
3604
|
}
|
|
3433
3605
|
async extendProjectConfig(projectConfig, projectPath) {
|
|
3434
3606
|
const projectConfigPath = path.resolve(projectPath, "project.config.json");
|