-
Notifications
You must be signed in to change notification settings - Fork 0
/
puppeteerUtils.ts
96 lines (85 loc) · 3.79 KB
/
puppeteerUtils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import type {ElementHandle, Frame, JSHandle, Page} from 'puppeteer';
import {IsTuple} from 'ts-essentials';
import TypedArray = NodeJS.TypedArray;
export function getPageFromHandle(handle: JSHandle): Page | null {
return handle.executionContext().frame()?.page() ?? null;
}
/** @return Stack starting with this frame, going up */
export function getFrameStack(frame: Frame): [Frame] & Frame[] {
const frames: [Frame] & Frame[] = [frame];
let curFrame: Frame | null = frame;
while ((curFrame = curFrame.parentFrame()))
frames.push(curFrame);
return frames;
}
/** Typed version of {@link Page#exposeFunction} */
export async function exposeFunction<Name extends keyof Window & string,
Func extends typeof window[Name] & ((this: typeof global, ...args: never) => void)>(
page: Page, name: Name, func: Func) {
await page.exposeFunction(name, func);
}
/**
* Like {@link JSHandle#jsonValue}, but retains some non-serializable objects as {@link JSHandle}s
*/
export async function unwrapHandle<T>(handle: JSHandle<T>): Promise<UnwrappedHandle<T>> {
return await unwrapHandleEx(handle, () => true) as UnwrappedHandle<T>;
}
/**
* Like {@link JSHandle#jsonValue}, but retains non-serializable objects as {@link JSHandle}s
*/
export async function unwrapHandleEx<T>(
handle: JSHandle<T>,
shouldUnwrap: (className: string) => boolean = className => ['Object', 'Proxy'].includes(className),
): Promise<UnwrappedHandleEx<T>> {
const remoteObject = handle.remoteObject();
// Leave functions & symbols wrapped
if (['function', 'symbol'].includes(remoteObject.type))
return handle as UnwrappedHandleEx<T>;
if (remoteObject.type === 'object') {
if ([undefined, 'proxy'].includes(remoteObject.subtype)) {
if (shouldUnwrap(remoteObject.className!))
return Object.fromEntries(await Promise.all([...await handle.getProperties()]
.map(async ([k, v]) => [k, await unwrapHandleEx(v, shouldUnwrap)]))) as UnwrappedHandleEx<T>;
} else {
if (remoteObject.subtype === 'null')
return null as UnwrappedHandleEx<T>;
if (remoteObject.subtype === 'array')
return await Promise.all([...await handle.getProperties()]
.map(async ([, v]) => await unwrapHandleEx(v, shouldUnwrap))) as UnwrappedHandleEx<T>;
}
return handle as UnwrappedHandleEx<T>;
} else // Return other types such as numbers, booleans, bigints, etc. unwrapped
return await handle.jsonValue() as UnwrappedHandleEx<T>;
}
export type UnwrappedHandle<T> = T extends string | boolean | number | null | undefined | bigint
? T
: T extends Node
? ElementHandle<T>
: T extends (infer V)[]
? T extends IsTuple<T>
? { [K in keyof T]: UnwrappedHandle<T[K]> }
: UnwrappedHandle<V>[]
: T extends RegExp | Date | Map<unknown, unknown> | Set<unknown> | WeakMap<object, unknown> | WeakSet<object>
| Iterator<unknown, never, never> | Error | Promise<unknown> | TypedArray | ArrayBuffer | DataView
// eslint-disable-next-line @typescript-eslint/ban-types
| Function | symbol
? JSHandle<T>
: T extends object
? { [K in keyof T]: UnwrappedHandle<T[K]> }
: unknown;
export type UnwrappedHandleEx<T> = T extends string | boolean | number | null | undefined | bigint
? T
: T extends Node
? ElementHandle<T>
: T extends (infer V)[]
? T extends IsTuple<T>
? { [K in keyof T]: UnwrappedHandleEx<T[K]> }
: UnwrappedHandleEx<V>[]
: T extends RegExp | Date | Map<unknown, unknown> | Set<unknown> | WeakMap<object, unknown> | WeakSet<object>
| Iterator<unknown, never, never> | Error | Promise<unknown> | TypedArray | ArrayBuffer | DataView
// eslint-disable-next-line @typescript-eslint/ban-types
| Function | symbol
? JSHandle<T>
: T extends object
? { [K in keyof T]: UnwrappedHandleEx<T[K]> } | JSHandle<T>
: unknown;