browserscale-ts 1.5.0 → 1.7.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 +30 -0
- package/dist/browser.d.ts +4 -1
- package/dist/browser.js +5 -0
- package/dist/browserscale.browser.js +1581 -46
- package/dist/client.d.ts +316 -7
- package/dist/client.js +588 -7
- package/dist/dom-mirror.d.ts +271 -0
- package/dist/dom-mirror.js +613 -0
- package/dist/gen/wrc_pb.d.ts +1251 -87
- package/dist/gen/wrc_pb.js +214 -44
- package/dist/index.d.ts +31 -1
- package/dist/index.js +61 -0
- package/dist/internal/convert.d.ts +8 -2
- package/dist/internal/convert.js +39 -0
- package/dist/network-capture.d.ts +84 -0
- package/dist/network-capture.js +107 -0
- package/dist/scripts.d.ts +205 -0
- package/dist/scripts.js +234 -0
- package/dist/types.d.ts +157 -0
- package/dist/ws-transport.d.ts +15 -1
- package/dist/ws-transport.js +155 -10
- package/package.json +1 -1
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import type { DomEvent } from "./gen/wrc_pb.ts";
|
|
2
|
+
/**
|
|
3
|
+
* A node in the mirrored page, in CDP's `DOM.Node` shape — the same shape
|
|
4
|
+
* {@link CloudBrowser.getDOM} returns, with two additions that the mirror
|
|
5
|
+
* needs and a caller usually wants anyway: {@link DomNode.frameId} and
|
|
6
|
+
* {@link DomNode.contentFrameId}.
|
|
7
|
+
*
|
|
8
|
+
* An `<iframe>` is an ordinary element here. The document it hosts is its one
|
|
9
|
+
* entry in {@link DomNode.children}, present once the element has been
|
|
10
|
+
* expanded, and nothing about walking the tree has to know a process boundary
|
|
11
|
+
* runs through it.
|
|
12
|
+
*/
|
|
13
|
+
export interface DomNode {
|
|
14
|
+
nodeId: number;
|
|
15
|
+
backendNodeId: number;
|
|
16
|
+
nodeType: number;
|
|
17
|
+
nodeName: string;
|
|
18
|
+
localName?: string;
|
|
19
|
+
nodeValue?: string;
|
|
20
|
+
/** Flat `[name, value, name, value, ...]`, as CDP sends it. */
|
|
21
|
+
attributes?: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Total children in the page, whether or not they are in `children`. An
|
|
24
|
+
* `<iframe>` reports 1: the document it hosts.
|
|
25
|
+
*/
|
|
26
|
+
childNodeCount?: number;
|
|
27
|
+
/** Present once the node has been expanded. */
|
|
28
|
+
children?: DomNode[];
|
|
29
|
+
/** Author shadow roots, when the mirror was started with `pierce`. */
|
|
30
|
+
shadowRoots?: DomNode[];
|
|
31
|
+
/**
|
|
32
|
+
* The frame this node lives in. Always set.
|
|
33
|
+
*
|
|
34
|
+
* Together with `backendNodeId` this is the node's address: node ids are
|
|
35
|
+
* handed out per renderer and restart per frame, so two frames can and do
|
|
36
|
+
* use the same one, and the id on its own is ambiguous across a page.
|
|
37
|
+
*/
|
|
38
|
+
frameId: string;
|
|
39
|
+
/**
|
|
40
|
+
* For an element that hosts a frame (`<iframe>`, `<frame>`, `<object>`):
|
|
41
|
+
* the frame it hosts, which is a different frame from `frameId` and is the
|
|
42
|
+
* one its child document's ids belong to.
|
|
43
|
+
*/
|
|
44
|
+
contentFrameId?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The opening snapshot: the main frame's document. Child frames are not in it
|
|
48
|
+
* — their documents are fetched by expanding the `<iframe>` elements that host
|
|
49
|
+
* them, which is also what starts mirroring them.
|
|
50
|
+
*/
|
|
51
|
+
export interface DomSnapshot {
|
|
52
|
+
root: string;
|
|
53
|
+
frameId: string;
|
|
54
|
+
/** The page sequence this snapshot is the baseline for. */
|
|
55
|
+
seq: number;
|
|
56
|
+
}
|
|
57
|
+
/** Why a mirror had to be rebuilt. */
|
|
58
|
+
export type DomResyncReason = "documentReplaced" | "overflow" | "rendererGone" | "slowReader" | string;
|
|
59
|
+
export interface DomMirrorOptions {
|
|
60
|
+
/**
|
|
61
|
+
* Levels to fetch up front. Default 2, which is `#document` → `<html>` →
|
|
62
|
+
* `<head>`/`<body>` — enough to draw a collapsed tree. Fetching everything
|
|
63
|
+
* (-1) works but gives up what the mirror is for.
|
|
64
|
+
*/
|
|
65
|
+
depth?: number;
|
|
66
|
+
/** Descend into author shadow roots. Fixed for the life of the mirror. */
|
|
67
|
+
pierce?: boolean;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Called after the tree changed. `root` is a fresh object whenever anything
|
|
71
|
+
* below it changed, so it can be compared by identity and rendered with
|
|
72
|
+
* memoized components.
|
|
73
|
+
*/
|
|
74
|
+
export type DomChangeHandler = (mirror: DomMirror) => void;
|
|
75
|
+
/** Called when the mirror had to be rebuilt, after the new tree is in place. */
|
|
76
|
+
export type DomResyncHandler = (reason: DomResyncReason) => void;
|
|
77
|
+
/** @internal The transport calls the mirror needs. Supplied by CloudBrowser. */
|
|
78
|
+
export interface DomMirrorTransport {
|
|
79
|
+
start(opts: DomMirrorOptions): Promise<DomSnapshot>;
|
|
80
|
+
stop(): Promise<void>;
|
|
81
|
+
children(backendNodeId: number, frameId: string, depth?: number): Promise<{
|
|
82
|
+
children: string;
|
|
83
|
+
seq: number;
|
|
84
|
+
}>;
|
|
85
|
+
release(backendNodeId: number, frameId: string): Promise<void>;
|
|
86
|
+
reveal(backendNodeId: number, frameId: string): Promise<{
|
|
87
|
+
path: string;
|
|
88
|
+
seq: number;
|
|
89
|
+
}>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A live copy of a page's DOM, across every frame in it.
|
|
93
|
+
*
|
|
94
|
+
* Returned by {@link CloudBrowser.mirrorDom}. The browser sends the top of the
|
|
95
|
+
* tree once and from then on only what changed in the part you expanded, so a
|
|
96
|
+
* page that churns inside a collapsed subtree costs one number per batch
|
|
97
|
+
* instead of a re-serialized document.
|
|
98
|
+
*
|
|
99
|
+
* It is one tree. An `<iframe>` is an element whose one child is the document
|
|
100
|
+
* it hosts; expanding it fetches that document and starts mirroring the frame,
|
|
101
|
+
* collapsing it stops again, and a frame navigating arrives as its owner's
|
|
102
|
+
* child being replaced. Underneath there is still one mirror per document,
|
|
103
|
+
* because a mutation observer is bound to a single Document and an
|
|
104
|
+
* out-of-process iframe is a different Document in a different process — but
|
|
105
|
+
* that is engine bookkeeping, not something a caller models.
|
|
106
|
+
*
|
|
107
|
+
* Node ids restart per frame, so a node's address is the pair
|
|
108
|
+
* ({@link DomNode.frameId}, `backendNodeId`) and never the id alone.
|
|
109
|
+
*
|
|
110
|
+
* The tree is treated as immutable: applying a change replaces the nodes from
|
|
111
|
+
* the root down to the one that moved and leaves every other object identical.
|
|
112
|
+
* A UI can therefore re-render from `root` and let `React.memo` (or any
|
|
113
|
+
* identity check) skip the parts that did not move.
|
|
114
|
+
*
|
|
115
|
+
* ```ts
|
|
116
|
+
* const mirror = await browser.mirrorDom({ pierce: true }, () => render(mirror.root));
|
|
117
|
+
* await mirror.expand(bodyNode); // start reporting changes inside <body>
|
|
118
|
+
* await mirror.collapse(bodyNode); // stop again
|
|
119
|
+
* await mirror.stop();
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export declare class DomMirror {
|
|
123
|
+
private readonly transport;
|
|
124
|
+
private readonly options;
|
|
125
|
+
private readonly onChange;
|
|
126
|
+
private readonly onResync?;
|
|
127
|
+
private readonly abort;
|
|
128
|
+
private readonly finished;
|
|
129
|
+
private nodes;
|
|
130
|
+
private slots;
|
|
131
|
+
private expandedKeys;
|
|
132
|
+
/**
|
|
133
|
+
* Per node: the page sequence its current state was defined at, by a read
|
|
134
|
+
* payload or by an edit.
|
|
135
|
+
*
|
|
136
|
+
* Reads and events reach a client over two different channels — a unary call
|
|
137
|
+
* and a stream — so an event can turn up that the read reply already folded
|
|
138
|
+
* in. Applying it twice would duplicate an insertion, which is the one entry
|
|
139
|
+
* type that is not idempotent. Comparing against the node's own watermark
|
|
140
|
+
* rather than a single page-wide one keeps that from silently discarding a
|
|
141
|
+
* change to an unrelated part of the tree.
|
|
142
|
+
*/
|
|
143
|
+
private asOf;
|
|
144
|
+
private seqValue;
|
|
145
|
+
private rootNode;
|
|
146
|
+
private mainFrame;
|
|
147
|
+
/**
|
|
148
|
+
* Events are held until a snapshot exists to apply them to, and dropped if
|
|
149
|
+
* they predate it. Without this a batch that lands between subscribing and
|
|
150
|
+
* the snapshot arriving would either be applied to nothing or applied twice.
|
|
151
|
+
*/
|
|
152
|
+
private ready;
|
|
153
|
+
private pendingEvents;
|
|
154
|
+
private stopped;
|
|
155
|
+
private failure;
|
|
156
|
+
/** @internal Constructed by CloudBrowser; not part of the public API. */
|
|
157
|
+
constructor(init: {
|
|
158
|
+
stream: AsyncIterable<DomEvent>;
|
|
159
|
+
transport: DomMirrorTransport;
|
|
160
|
+
options: DomMirrorOptions;
|
|
161
|
+
onChange: DomChangeHandler;
|
|
162
|
+
onResync?: DomResyncHandler;
|
|
163
|
+
abort: AbortController;
|
|
164
|
+
});
|
|
165
|
+
/** The main frame's document, or null before the first snapshot arrived. */
|
|
166
|
+
get root(): DomNode | null;
|
|
167
|
+
/** The page's main frame. */
|
|
168
|
+
get mainFrameId(): string;
|
|
169
|
+
/**
|
|
170
|
+
* Every frame with a document in the tree, main frame first. A frame whose
|
|
171
|
+
* `<iframe>` has not been expanded is not mirrored and not listed.
|
|
172
|
+
*/
|
|
173
|
+
get frameIds(): string[];
|
|
174
|
+
/**
|
|
175
|
+
* The page sequence of the last change applied. One clock for the whole
|
|
176
|
+
* page: a change in an out-of-process iframe and one in the main document
|
|
177
|
+
* are ordered against each other.
|
|
178
|
+
*/
|
|
179
|
+
get seq(): number;
|
|
180
|
+
/** Looks up a node by its address. */
|
|
181
|
+
getNode(frameId: string, backendNodeId: number): DomNode | undefined;
|
|
182
|
+
/**
|
|
183
|
+
* Whether this node's children are known. Changes inside a node that is not
|
|
184
|
+
* expanded arrive only as an updated `childNodeCount`.
|
|
185
|
+
*/
|
|
186
|
+
isExpanded(node: DomNode): boolean;
|
|
187
|
+
/** Why the mirror ended: null while running and after a clean stop. */
|
|
188
|
+
get error(): Error | null;
|
|
189
|
+
/**
|
|
190
|
+
* Fetches a node's children and starts reporting changes inside them. This
|
|
191
|
+
* is what a tree view calls when the user opens a node.
|
|
192
|
+
*
|
|
193
|
+
* On an `<iframe>` the one child is the document it hosts, and this call is
|
|
194
|
+
* what starts mirroring that frame. Nothing about the result says a process
|
|
195
|
+
* boundary was crossed; it is a child list like any other.
|
|
196
|
+
*
|
|
197
|
+
* @param depth levels below the node, default 1
|
|
198
|
+
*/
|
|
199
|
+
expand(node: DomNode, depth?: number): Promise<void>;
|
|
200
|
+
/**
|
|
201
|
+
* Stops reporting changes inside a node, called when the user closes it. The
|
|
202
|
+
* node itself stays in the tree and keeps reporting its child count. A child
|
|
203
|
+
* frame below it stops being mirrored too.
|
|
204
|
+
*
|
|
205
|
+
* Skipping this is not an error, it is a slow leak: the browser's revealed
|
|
206
|
+
* set only grows, and eventually it is no longer filtering anything.
|
|
207
|
+
*/
|
|
208
|
+
collapse(node: DomNode): Promise<void>;
|
|
209
|
+
/**
|
|
210
|
+
* Brings a node into the tree together with its ancestors and their
|
|
211
|
+
* siblings, and starts reporting changes along that path.
|
|
212
|
+
*
|
|
213
|
+
* Use it to focus a node you do not hold — an `inspectAtPosition` hit, say.
|
|
214
|
+
* You cannot walk up to it yourself: it is not in your tree, so there is
|
|
215
|
+
* nothing to walk from.
|
|
216
|
+
*
|
|
217
|
+
* The node may be in a frame nobody opened, and that works: the chain comes
|
|
218
|
+
* back crossing the frame boundaries it has to, and those frames start being
|
|
219
|
+
* mirrored, exactly as if you had expanded your way there by hand.
|
|
220
|
+
*
|
|
221
|
+
* @returns the ancestor chain, the main document first, or an empty array if
|
|
222
|
+
* the node is not on the page
|
|
223
|
+
*/
|
|
224
|
+
reveal(backendNodeId: number, frameId?: string): Promise<DomNode[]>;
|
|
225
|
+
/**
|
|
226
|
+
* Throws away the local copy of the whole page and fetches a fresh one.
|
|
227
|
+
* Happens automatically whenever the browser says the copy is void, so you
|
|
228
|
+
* rarely need to call it.
|
|
229
|
+
*/
|
|
230
|
+
resync(reason?: DomResyncReason): Promise<void>;
|
|
231
|
+
/** Resolves once the mirror ends — stop(), a dead session, a transport failure. */
|
|
232
|
+
wait(): Promise<void>;
|
|
233
|
+
/** Stops mirroring and detaches the reader. Idempotent, safe in a `finally`. */
|
|
234
|
+
stop(): Promise<void>;
|
|
235
|
+
/** @internal Called by CloudBrowser with the opening snapshot. */
|
|
236
|
+
install(snapshot: DomSnapshot): void;
|
|
237
|
+
private reset;
|
|
238
|
+
private pump;
|
|
239
|
+
private handle;
|
|
240
|
+
private apply;
|
|
241
|
+
/** Records that a node's state is current as of `seq`. Never moves back. */
|
|
242
|
+
private mark;
|
|
243
|
+
/**
|
|
244
|
+
* Replaces `key` and every ancestor with copies, so the path from the root
|
|
245
|
+
* to the changed node has new identities and nothing else does. Returns the
|
|
246
|
+
* fresh copy of `key`, which the caller then edits in place.
|
|
247
|
+
*
|
|
248
|
+
* The walk crosses frame boundaries without noticing them: a document is a
|
|
249
|
+
* child of the `<iframe>` hosting it like any other, so a change deep inside
|
|
250
|
+
* an out-of-process frame still produces a new `root`.
|
|
251
|
+
*/
|
|
252
|
+
private touch;
|
|
253
|
+
/**
|
|
254
|
+
* Registers a payload subtree and returns the copy that lives in the tree.
|
|
255
|
+
*
|
|
256
|
+
* `frame` is the id space the payload's ids belong to, and it changes here
|
|
257
|
+
* and nowhere else: a document node names its own frame, and everything
|
|
258
|
+
* below it counts in that frame. That is the whole of what crossing into an
|
|
259
|
+
* iframe means to a client.
|
|
260
|
+
*/
|
|
261
|
+
private adopt;
|
|
262
|
+
/** Replaces a node's child list from a fresh payload for the same node. */
|
|
263
|
+
private mergeChildren;
|
|
264
|
+
private dropChildren;
|
|
265
|
+
/**
|
|
266
|
+
* Removes a subtree from the index. It descends through hosted documents
|
|
267
|
+
* like through anything else — they are children, and a frame stops being
|
|
268
|
+
* mirrored exactly when the element hosting it stops being expanded.
|
|
269
|
+
*/
|
|
270
|
+
private forget;
|
|
271
|
+
}
|