@orkestrel/browser 0.0.7 → 0.0.8
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 +4 -5
- package/dist/src/core/index.cjs +234 -377
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +96 -199
- package/dist/src/core/index.d.ts +96 -199
- package/dist/src/core/index.js +233 -359
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +7 -5
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.js +7 -5
- package/dist/src/server/index.js.map +1 -1
- package/package.json +8 -7
package/dist/src/core/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { attempt, instanceOf, isArray, isBoolean, isFiniteNumber, isInstance, isInteger, isRecord, isString, parseArray, parseEnum, parseJSON } from "@orkestrel/contract";
|
|
1
|
+
import { attempt, instanceOf, isArray, isBoolean, isFiniteNumber, isFunction, isInstance, isInteger, isRecord, isString, parseArray, parseEnum, parseJSON } from "@orkestrel/contract";
|
|
2
2
|
import { Emitter } from "@orkestrel/emitter";
|
|
3
|
+
import { createHTML, renderText } from "@orkestrel/html";
|
|
3
4
|
//#region src/core/constants.ts
|
|
4
5
|
/** Base64 alphabet, index-ordered, used to build {@link BASE64_LOOKUP}. */
|
|
5
6
|
var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
@@ -74,7 +75,7 @@ var BROWSER_MOUSE_BUTTON_MASKS = Object.freeze({
|
|
|
74
75
|
/** Tool identity embedded in HAR 1.2 documents. */
|
|
75
76
|
var BROWSER_HAR_CREATOR = Object.freeze({
|
|
76
77
|
name: "@orkestrel/browser",
|
|
77
|
-
version: "0.0.
|
|
78
|
+
version: "0.0.8"
|
|
78
79
|
});
|
|
79
80
|
/** Attribute used to tag temporary screenshot styles and masks. */
|
|
80
81
|
var BROWSER_SCREENSHOT_ATTRIBUTE = "data-orkestrel-screenshot";
|
|
@@ -2822,326 +2823,6 @@ function decodeBrowserSnapshot(value, styles = [], limit = BROWSER_SNAPSHOT_NODE
|
|
|
2822
2823
|
};
|
|
2823
2824
|
}
|
|
2824
2825
|
/**
|
|
2825
|
-
* Lazily walk every captured browser node in structural depth-first order.
|
|
2826
|
-
*
|
|
2827
|
-
* @remarks
|
|
2828
|
-
* Iframe content documents are entered at their owner element. Orphaned or
|
|
2829
|
-
* cyclic protocol nodes are still yielded once through an identity guard.
|
|
2830
|
-
*
|
|
2831
|
-
* @param snapshot - Captured browser snapshot
|
|
2832
|
-
* @returns A lazy node generator
|
|
2833
|
-
*/
|
|
2834
|
-
function* walkBrowserSnapshot(snapshot) {
|
|
2835
|
-
const seeds = [];
|
|
2836
|
-
for (const document of snapshot.documents) seeds.push(...document.nodes.filter((node) => node.parent === void 0));
|
|
2837
|
-
for (const document of snapshot.documents) seeds.push(...document.nodes);
|
|
2838
|
-
const visited = /* @__PURE__ */ new Set();
|
|
2839
|
-
for (const seed of seeds) {
|
|
2840
|
-
const stack = [seed];
|
|
2841
|
-
while (stack.length > 0) {
|
|
2842
|
-
const node = stack.pop();
|
|
2843
|
-
if (node === void 0) break;
|
|
2844
|
-
const identity = `${node.document}:${node.index}`;
|
|
2845
|
-
if (visited.has(identity)) continue;
|
|
2846
|
-
visited.add(identity);
|
|
2847
|
-
yield node;
|
|
2848
|
-
const children = childrenOfBrowserNode(snapshot, node);
|
|
2849
|
-
for (let index = children.length - 1; index >= 0; index -= 1) {
|
|
2850
|
-
const child = children[index];
|
|
2851
|
-
if (child !== void 0) stack.push(child);
|
|
2852
|
-
}
|
|
2853
|
-
}
|
|
2854
|
-
}
|
|
2855
|
-
}
|
|
2856
|
-
/**
|
|
2857
|
-
* Lazily walk every captured node in structural breadth-first order.
|
|
2858
|
-
*
|
|
2859
|
-
* @param snapshot - Captured browser snapshot
|
|
2860
|
-
* @returns A lazy breadth-first generator
|
|
2861
|
-
*/
|
|
2862
|
-
function* walkBrowserSnapshotBreadthFirst(snapshot) {
|
|
2863
|
-
const seeds = [];
|
|
2864
|
-
for (const document of snapshot.documents) seeds.push(...document.nodes.filter((node) => node.parent === void 0));
|
|
2865
|
-
for (const document of snapshot.documents) seeds.push(...document.nodes);
|
|
2866
|
-
const visited = /* @__PURE__ */ new Set();
|
|
2867
|
-
for (const seed of seeds) {
|
|
2868
|
-
const queue = [seed];
|
|
2869
|
-
let cursor = 0;
|
|
2870
|
-
while (cursor < queue.length) {
|
|
2871
|
-
const node = queue[cursor];
|
|
2872
|
-
cursor += 1;
|
|
2873
|
-
if (node === void 0) continue;
|
|
2874
|
-
const identity = `${node.document}:${node.index}`;
|
|
2875
|
-
if (visited.has(identity)) continue;
|
|
2876
|
-
visited.add(identity);
|
|
2877
|
-
yield node;
|
|
2878
|
-
queue.push(...childrenOfBrowserNode(snapshot, node));
|
|
2879
|
-
}
|
|
2880
|
-
}
|
|
2881
|
-
}
|
|
2882
|
-
/**
|
|
2883
|
-
* Lazily walk one captured browser-node subtree in depth-first order.
|
|
2884
|
-
*
|
|
2885
|
-
* @param snapshot - Captured browser snapshot
|
|
2886
|
-
* @param root - Root node, included as the first result
|
|
2887
|
-
* @returns A lazy subtree generator
|
|
2888
|
-
*/
|
|
2889
|
-
function* walkBrowserNode(snapshot, root) {
|
|
2890
|
-
const stack = [root];
|
|
2891
|
-
const visited = /* @__PURE__ */ new Set();
|
|
2892
|
-
while (stack.length > 0) {
|
|
2893
|
-
const node = stack.pop();
|
|
2894
|
-
if (node === void 0) break;
|
|
2895
|
-
const identity = `${node.document}:${node.index}`;
|
|
2896
|
-
if (visited.has(identity)) continue;
|
|
2897
|
-
visited.add(identity);
|
|
2898
|
-
yield node;
|
|
2899
|
-
const children = childrenOfBrowserNode(snapshot, node);
|
|
2900
|
-
for (let index = children.length - 1; index >= 0; index -= 1) {
|
|
2901
|
-
const child = children[index];
|
|
2902
|
-
if (child !== void 0) stack.push(child);
|
|
2903
|
-
}
|
|
2904
|
-
}
|
|
2905
|
-
}
|
|
2906
|
-
/**
|
|
2907
|
-
* Lazily walk one captured browser-node subtree in breadth-first order.
|
|
2908
|
-
*
|
|
2909
|
-
* @param snapshot - Captured browser snapshot
|
|
2910
|
-
* @param root - Root node, included as the first result
|
|
2911
|
-
* @returns A lazy breadth-first subtree generator
|
|
2912
|
-
*/
|
|
2913
|
-
function* walkBrowserNodeBreadthFirst(snapshot, root) {
|
|
2914
|
-
const queue = [root];
|
|
2915
|
-
const visited = /* @__PURE__ */ new Set();
|
|
2916
|
-
let cursor = 0;
|
|
2917
|
-
while (cursor < queue.length) {
|
|
2918
|
-
const node = queue[cursor];
|
|
2919
|
-
cursor += 1;
|
|
2920
|
-
if (node === void 0) continue;
|
|
2921
|
-
const identity = `${node.document}:${node.index}`;
|
|
2922
|
-
if (visited.has(identity)) continue;
|
|
2923
|
-
visited.add(identity);
|
|
2924
|
-
yield node;
|
|
2925
|
-
queue.push(...childrenOfBrowserNode(snapshot, node));
|
|
2926
|
-
}
|
|
2927
|
-
}
|
|
2928
|
-
/**
|
|
2929
|
-
* Lazily walk descendants of one captured node, excluding the node itself.
|
|
2930
|
-
*
|
|
2931
|
-
* @param snapshot - Captured browser snapshot
|
|
2932
|
-
* @param root - Root whose descendants are required
|
|
2933
|
-
* @returns A lazy descendant generator
|
|
2934
|
-
*/
|
|
2935
|
-
function* descendantsOfBrowserNode(snapshot, root) {
|
|
2936
|
-
let first = true;
|
|
2937
|
-
for (const node of walkBrowserNode(snapshot, root)) {
|
|
2938
|
-
if (first) {
|
|
2939
|
-
first = false;
|
|
2940
|
-
continue;
|
|
2941
|
-
}
|
|
2942
|
-
yield node;
|
|
2943
|
-
}
|
|
2944
|
-
}
|
|
2945
|
-
/**
|
|
2946
|
-
* Resolve the document containing a browser node.
|
|
2947
|
-
*
|
|
2948
|
-
* @param snapshot - Captured browser snapshot
|
|
2949
|
-
* @param node - Node whose document is required
|
|
2950
|
-
* @returns The containing document, or undefined
|
|
2951
|
-
*/
|
|
2952
|
-
function documentOfBrowserNode(snapshot, node) {
|
|
2953
|
-
return snapshot.documents.find((document) => document.index === node.document);
|
|
2954
|
-
}
|
|
2955
|
-
/**
|
|
2956
|
-
* Return direct children of a captured browser node.
|
|
2957
|
-
*
|
|
2958
|
-
* @remarks
|
|
2959
|
-
* Includes the root nodes of an iframe's linked content document. Shadow DOM
|
|
2960
|
-
* and template content already carry flattened parent indexes from CDP.
|
|
2961
|
-
*
|
|
2962
|
-
* @param snapshot - Captured browser snapshot
|
|
2963
|
-
* @param node - Parent node
|
|
2964
|
-
* @returns Direct children in snapshot order
|
|
2965
|
-
*/
|
|
2966
|
-
function childrenOfBrowserNode(snapshot, node) {
|
|
2967
|
-
const document = documentOfBrowserNode(snapshot, node);
|
|
2968
|
-
if (document === void 0) return [];
|
|
2969
|
-
const children = document.nodes.filter((candidate) => candidate.parent === node.index);
|
|
2970
|
-
if (node.content === void 0) return children;
|
|
2971
|
-
const content = snapshot.documents.find((candidate) => candidate.index === node.content);
|
|
2972
|
-
if (content === void 0) return children;
|
|
2973
|
-
return [...children, ...content.nodes.filter((candidate) => candidate.parent === void 0)];
|
|
2974
|
-
}
|
|
2975
|
-
/**
|
|
2976
|
-
* Resolve the structural parent of a captured browser node.
|
|
2977
|
-
*
|
|
2978
|
-
* @remarks
|
|
2979
|
-
* An iframe content-document root resolves to its owning iframe node.
|
|
2980
|
-
*
|
|
2981
|
-
* @param snapshot - Captured browser snapshot
|
|
2982
|
-
* @param node - Child node
|
|
2983
|
-
* @returns Parent node, or undefined for a top-level document root
|
|
2984
|
-
*/
|
|
2985
|
-
function parentOfBrowserNode(snapshot, node) {
|
|
2986
|
-
const document = documentOfBrowserNode(snapshot, node);
|
|
2987
|
-
if (document === void 0) return void 0;
|
|
2988
|
-
return node.parent === void 0 ? findBrowserNode(snapshot, (candidate) => candidate.content === node.document) : document.nodes[node.parent];
|
|
2989
|
-
}
|
|
2990
|
-
/**
|
|
2991
|
-
* Return every structural sibling except the supplied browser node.
|
|
2992
|
-
*
|
|
2993
|
-
* @param snapshot - Captured browser snapshot
|
|
2994
|
-
* @param node - Node whose siblings are required
|
|
2995
|
-
* @returns Siblings in structural order
|
|
2996
|
-
*/
|
|
2997
|
-
function siblingsOfBrowserNode(snapshot, node) {
|
|
2998
|
-
const parent = parentOfBrowserNode(snapshot, node);
|
|
2999
|
-
return (parent === void 0 ? documentOfBrowserNode(snapshot, node)?.nodes.filter((candidate) => candidate.parent === void 0) ?? [] : childrenOfBrowserNode(snapshot, parent)).filter((candidate) => candidate.document !== node.document || candidate.index !== node.index);
|
|
3000
|
-
}
|
|
3001
|
-
/**
|
|
3002
|
-
* Return structural siblings before the supplied browser node.
|
|
3003
|
-
*
|
|
3004
|
-
* @param snapshot - Captured browser snapshot
|
|
3005
|
-
* @param node - Reference node
|
|
3006
|
-
* @returns Earlier siblings in structural order
|
|
3007
|
-
*/
|
|
3008
|
-
function precedingSiblingsOfBrowserNode(snapshot, node) {
|
|
3009
|
-
const parent = parentOfBrowserNode(snapshot, node);
|
|
3010
|
-
const candidates = parent === void 0 ? documentOfBrowserNode(snapshot, node)?.nodes.filter((candidate) => candidate.parent === void 0) ?? [] : childrenOfBrowserNode(snapshot, parent);
|
|
3011
|
-
const index = candidates.findIndex((candidate) => candidate.document === node.document && candidate.index === node.index);
|
|
3012
|
-
return index < 0 ? [] : candidates.slice(0, index);
|
|
3013
|
-
}
|
|
3014
|
-
/**
|
|
3015
|
-
* Return structural siblings after the supplied browser node.
|
|
3016
|
-
*
|
|
3017
|
-
* @param snapshot - Captured browser snapshot
|
|
3018
|
-
* @param node - Reference node
|
|
3019
|
-
* @returns Later siblings in structural order
|
|
3020
|
-
*/
|
|
3021
|
-
function followingSiblingsOfBrowserNode(snapshot, node) {
|
|
3022
|
-
const parent = parentOfBrowserNode(snapshot, node);
|
|
3023
|
-
const candidates = parent === void 0 ? documentOfBrowserNode(snapshot, node)?.nodes.filter((candidate) => candidate.parent === void 0) ?? [] : childrenOfBrowserNode(snapshot, parent);
|
|
3024
|
-
const index = candidates.findIndex((candidate) => candidate.document === node.document && candidate.index === node.index);
|
|
3025
|
-
return index < 0 ? [] : candidates.slice(index + 1);
|
|
3026
|
-
}
|
|
3027
|
-
/**
|
|
3028
|
-
* Return the nearest-first ancestor chain for a captured browser node.
|
|
3029
|
-
*
|
|
3030
|
-
* @param snapshot - Captured browser snapshot
|
|
3031
|
-
* @param node - Descendant node
|
|
3032
|
-
* @returns Nearest-first ancestor list
|
|
3033
|
-
*/
|
|
3034
|
-
function ancestorsOfBrowserNode(snapshot, node) {
|
|
3035
|
-
const ancestors = [];
|
|
3036
|
-
const visited = /* @__PURE__ */ new Set();
|
|
3037
|
-
let current = node;
|
|
3038
|
-
while (true) {
|
|
3039
|
-
const parent = parentOfBrowserNode(snapshot, current);
|
|
3040
|
-
if (parent === void 0) break;
|
|
3041
|
-
const identity = `${parent.document}:${parent.index}`;
|
|
3042
|
-
if (visited.has(identity)) break;
|
|
3043
|
-
visited.add(identity);
|
|
3044
|
-
ancestors.push(parent);
|
|
3045
|
-
current = parent;
|
|
3046
|
-
}
|
|
3047
|
-
return ancestors;
|
|
3048
|
-
}
|
|
3049
|
-
/**
|
|
3050
|
-
* Test whether one captured node is below another across frame boundaries.
|
|
3051
|
-
*
|
|
3052
|
-
* @param snapshot - Captured browser snapshot
|
|
3053
|
-
* @param node - Candidate descendant
|
|
3054
|
-
* @param ancestor - Candidate ancestor
|
|
3055
|
-
* @returns Whether the relationship exists
|
|
3056
|
-
*/
|
|
3057
|
-
function isBrowserNodeDescendant(snapshot, node, ancestor) {
|
|
3058
|
-
return ancestorsOfBrowserNode(snapshot, node).some((candidate) => candidate.document === ancestor.document && candidate.index === ancestor.index);
|
|
3059
|
-
}
|
|
3060
|
-
/**
|
|
3061
|
-
* Resolve the nearest common ancestor of two captured browser nodes.
|
|
3062
|
-
*
|
|
3063
|
-
* @param snapshot - Captured browser snapshot
|
|
3064
|
-
* @param first - First node
|
|
3065
|
-
* @param second - Second node
|
|
3066
|
-
* @returns Common ancestor, or undefined
|
|
3067
|
-
*/
|
|
3068
|
-
function commonAncestorOfBrowserNodes(snapshot, first, second) {
|
|
3069
|
-
const firstAncestors = [first, ...ancestorsOfBrowserNode(snapshot, first)];
|
|
3070
|
-
const secondAncestors = [second, ...ancestorsOfBrowserNode(snapshot, second)];
|
|
3071
|
-
const identities = new Set(firstAncestors.map((node) => `${node.document}:${node.index}`));
|
|
3072
|
-
return secondAncestors.find((node) => identities.has(`${node.document}:${node.index}`));
|
|
3073
|
-
}
|
|
3074
|
-
/**
|
|
3075
|
-
* Compute the edge count between two captured browser nodes.
|
|
3076
|
-
*
|
|
3077
|
-
* @param snapshot - Captured browser snapshot
|
|
3078
|
-
* @param first - First node
|
|
3079
|
-
* @param second - Second node
|
|
3080
|
-
* @returns Tree distance, or undefined when no common ancestor exists
|
|
3081
|
-
*/
|
|
3082
|
-
function computeBrowserNodeDistance(snapshot, first, second) {
|
|
3083
|
-
const common = commonAncestorOfBrowserNodes(snapshot, first, second);
|
|
3084
|
-
if (common === void 0) return void 0;
|
|
3085
|
-
const firstPath = [first, ...ancestorsOfBrowserNode(snapshot, first)];
|
|
3086
|
-
const secondPath = [second, ...ancestorsOfBrowserNode(snapshot, second)];
|
|
3087
|
-
const firstDistance = firstPath.findIndex((node) => node.document === common.document && node.index === common.index);
|
|
3088
|
-
const secondDistance = secondPath.findIndex((node) => node.document === common.document && node.index === common.index);
|
|
3089
|
-
if (firstDistance < 0 || secondDistance < 0) return void 0;
|
|
3090
|
-
return firstDistance + secondDistance;
|
|
3091
|
-
}
|
|
3092
|
-
/**
|
|
3093
|
-
* Find the first captured node satisfying a predicate.
|
|
3094
|
-
*
|
|
3095
|
-
* @param snapshot - Captured browser snapshot
|
|
3096
|
-
* @param predicate - Node predicate
|
|
3097
|
-
* @returns First matching node, or undefined
|
|
3098
|
-
*/
|
|
3099
|
-
function findBrowserNode(snapshot, predicate) {
|
|
3100
|
-
for (const node of walkBrowserSnapshot(snapshot)) if (predicate(node)) return node;
|
|
3101
|
-
}
|
|
3102
|
-
/**
|
|
3103
|
-
* Find captured nodes satisfying a predicate.
|
|
3104
|
-
*
|
|
3105
|
-
* @param snapshot - Captured browser snapshot
|
|
3106
|
-
* @param predicate - Node predicate
|
|
3107
|
-
* @param limit - Optional maximum result count
|
|
3108
|
-
* @returns Matching nodes in snapshot order
|
|
3109
|
-
*/
|
|
3110
|
-
function findBrowserNodes(snapshot, predicate, limit) {
|
|
3111
|
-
if (limit !== void 0 && (!isInteger(limit) || limit < 0)) throw new BrowserError("Browser node result limit must be a non-negative integer", void 0, { limit });
|
|
3112
|
-
if (limit === 0) return [];
|
|
3113
|
-
const found = [];
|
|
3114
|
-
for (const node of walkBrowserSnapshot(snapshot)) {
|
|
3115
|
-
if (!predicate(node)) continue;
|
|
3116
|
-
found.push(node);
|
|
3117
|
-
if (limit !== void 0 && found.length >= limit) break;
|
|
3118
|
-
}
|
|
3119
|
-
return found;
|
|
3120
|
-
}
|
|
3121
|
-
/**
|
|
3122
|
-
* Find the first matching descendant beneath one captured node.
|
|
3123
|
-
*
|
|
3124
|
-
* @param snapshot - Captured browser snapshot
|
|
3125
|
-
* @param root - Root whose descendants are searched
|
|
3126
|
-
* @param predicate - Node predicate
|
|
3127
|
-
* @returns First matching descendant, or undefined
|
|
3128
|
-
*/
|
|
3129
|
-
function findBrowserDescendant(snapshot, root, predicate) {
|
|
3130
|
-
for (const node of descendantsOfBrowserNode(snapshot, root)) if (predicate(node)) return node;
|
|
3131
|
-
}
|
|
3132
|
-
/**
|
|
3133
|
-
* Find the nearest matching node from the candidate through its ancestors.
|
|
3134
|
-
*
|
|
3135
|
-
* @param snapshot - Captured browser snapshot
|
|
3136
|
-
* @param node - Starting node, included in the search
|
|
3137
|
-
* @param predicate - Node predicate
|
|
3138
|
-
* @returns Closest matching node, or undefined
|
|
3139
|
-
*/
|
|
3140
|
-
function closestBrowserNode(snapshot, node, predicate) {
|
|
3141
|
-
if (predicate(node)) return node;
|
|
3142
|
-
return ancestorsOfBrowserNode(snapshot, node).find(predicate);
|
|
3143
|
-
}
|
|
3144
|
-
/**
|
|
3145
2826
|
* Read one captured node attribute.
|
|
3146
2827
|
*
|
|
3147
2828
|
* @param node - Captured browser node
|
|
@@ -3152,6 +2833,23 @@ function attributeOfBrowserNode(node, name) {
|
|
|
3152
2833
|
return node.attributes[name];
|
|
3153
2834
|
}
|
|
3154
2835
|
/**
|
|
2836
|
+
* Test whether a browser-node matcher is a declarative query.
|
|
2837
|
+
*
|
|
2838
|
+
* @param value - Browser-node query or predicate
|
|
2839
|
+
* @returns Whether the matcher is a declarative query
|
|
2840
|
+
*
|
|
2841
|
+
* @example
|
|
2842
|
+
* ```ts
|
|
2843
|
+
* import { isBrowserNodeQuery } from '@src/core'
|
|
2844
|
+
*
|
|
2845
|
+
* isBrowserNodeQuery({ name: 'main' }) // true
|
|
2846
|
+
* isBrowserNodeQuery(() => true) // false
|
|
2847
|
+
* ```
|
|
2848
|
+
*/
|
|
2849
|
+
function isBrowserNodeQuery(value) {
|
|
2850
|
+
return !isFunction(value);
|
|
2851
|
+
}
|
|
2852
|
+
/**
|
|
3155
2853
|
* Test a captured node against a declarative query.
|
|
3156
2854
|
*
|
|
3157
2855
|
* @param node - Captured browser node
|
|
@@ -3179,36 +2877,6 @@ function isBrowserNodeVisible(node) {
|
|
|
3179
2877
|
const bounds = node.layout?.bounds;
|
|
3180
2878
|
return bounds !== void 0 && bounds[2] > 0 && bounds[3] > 0;
|
|
3181
2879
|
}
|
|
3182
|
-
/**
|
|
3183
|
-
* Build a deterministic, serializable path for a captured browser node.
|
|
3184
|
-
*
|
|
3185
|
-
* @param snapshot - Captured browser snapshot
|
|
3186
|
-
* @param node - Captured browser node
|
|
3187
|
-
* @returns Frame-qualified structural path
|
|
3188
|
-
*/
|
|
3189
|
-
function nodeToPath(snapshot, node) {
|
|
3190
|
-
const document = documentOfBrowserNode(snapshot, node);
|
|
3191
|
-
if (document === void 0) return `document:${node.document} > node:${node.index}`;
|
|
3192
|
-
const chain = [...ancestorsOfBrowserNode(snapshot, node)].reverse();
|
|
3193
|
-
chain.push(node);
|
|
3194
|
-
const segments = [];
|
|
3195
|
-
for (const current of chain) {
|
|
3196
|
-
if (current.type !== 1) {
|
|
3197
|
-
segments.push(`${current.name.toLowerCase()}:${current.index}`);
|
|
3198
|
-
continue;
|
|
3199
|
-
}
|
|
3200
|
-
let ordinal = 1;
|
|
3201
|
-
if (current.parent !== void 0) {
|
|
3202
|
-
const owner = documentOfBrowserNode(snapshot, current);
|
|
3203
|
-
for (const sibling of owner?.nodes ?? []) {
|
|
3204
|
-
if (sibling.index >= current.index) break;
|
|
3205
|
-
if (sibling.parent === current.parent && sibling.name === current.name) ordinal += 1;
|
|
3206
|
-
}
|
|
3207
|
-
}
|
|
3208
|
-
segments.push(`${current.name.toLowerCase()}:${ordinal}`);
|
|
3209
|
-
}
|
|
3210
|
-
return `frame(${JSON.stringify(document.frame)}) > ${segments.join(" > ")}`;
|
|
3211
|
-
}
|
|
3212
2880
|
//#endregion
|
|
3213
2881
|
//#region src/core/CDPClient.ts
|
|
3214
2882
|
/**
|
|
@@ -5011,9 +4679,7 @@ var BrowserLocator = class BrowserLocator {
|
|
|
5011
4679
|
case "enabled":
|
|
5012
4680
|
expression = `(() => { const element = ${compileLocatorExpression(this.#query)}; return Boolean(element && !element.matches(':disabled')) })()`;
|
|
5013
4681
|
break;
|
|
5014
|
-
case "editable":
|
|
5015
|
-
expression = `(() => { const element = ${compileLocatorExpression(this.#query)}; return Boolean(element && !element.matches(':disabled,[readonly]') && (element.isContentEditable || 'value' in element)) })()`;
|
|
5016
|
-
break;
|
|
4682
|
+
case "editable": expression = `(() => { const element = ${compileLocatorExpression(this.#query)}; return Boolean(element && !element.matches(':disabled,[readonly]') && (element.isContentEditable || 'value' in element)) })()`;
|
|
5017
4683
|
}
|
|
5018
4684
|
return await this.#frame.evaluate(expression) === true;
|
|
5019
4685
|
}
|
|
@@ -5174,7 +4840,7 @@ var BrowserFrame = class {
|
|
|
5174
4840
|
this.assert();
|
|
5175
4841
|
const [title, html, text, currentUrl] = await Promise.all([
|
|
5176
4842
|
this.#evaluate("document.title"),
|
|
5177
|
-
this.#
|
|
4843
|
+
this.#captureHTML(),
|
|
5178
4844
|
this.#evaluate(guardEvaluateExpression("document.body ? document.body.innerText : \"\"", BROWSER_RESULT_LIMIT)),
|
|
5179
4845
|
this.#evaluate("location.href")
|
|
5180
4846
|
]);
|
|
@@ -5187,6 +4853,11 @@ var BrowserFrame = class {
|
|
|
5187
4853
|
text: requireBrowserString(text, "Document text")
|
|
5188
4854
|
};
|
|
5189
4855
|
}
|
|
4856
|
+
async article() {
|
|
4857
|
+
this.assert();
|
|
4858
|
+
const html = requireBrowserString(await this.#captureHTML(), "Document HTML");
|
|
4859
|
+
return renderText(createHTML(html).distill().document);
|
|
4860
|
+
}
|
|
5190
4861
|
async click(selector, options) {
|
|
5191
4862
|
this.assert();
|
|
5192
4863
|
await this.#selectors.css(selector).click(options);
|
|
@@ -5247,6 +4918,9 @@ var BrowserFrame = class {
|
|
|
5247
4918
|
update(url) {
|
|
5248
4919
|
this.#url = url;
|
|
5249
4920
|
}
|
|
4921
|
+
async #captureHTML() {
|
|
4922
|
+
return await this.#evaluate(guardEvaluateExpression("document.documentElement.outerHTML", BROWSER_RESULT_LIMIT));
|
|
4923
|
+
}
|
|
5250
4924
|
async #evaluate(expression, timeout) {
|
|
5251
4925
|
const session = await this.#sessionId();
|
|
5252
4926
|
const params = {
|
|
@@ -6074,6 +5748,189 @@ var BrowserScriptManager = class {
|
|
|
6074
5748
|
}
|
|
6075
5749
|
};
|
|
6076
5750
|
//#endregion
|
|
5751
|
+
//#region src/core/BrowserSnapshot.ts
|
|
5752
|
+
/** A navigable, serializable browser DOM snapshot. */
|
|
5753
|
+
var BrowserSnapshot = class {
|
|
5754
|
+
#owners;
|
|
5755
|
+
documents;
|
|
5756
|
+
styles;
|
|
5757
|
+
constructor(input) {
|
|
5758
|
+
this.documents = Object.freeze([...input.documents]);
|
|
5759
|
+
this.styles = Object.freeze([...input.styles]);
|
|
5760
|
+
}
|
|
5761
|
+
walk(options) {
|
|
5762
|
+
return options?.order === "breadth" ? this.#breadth(options.root) : this.#depth(options?.root);
|
|
5763
|
+
}
|
|
5764
|
+
*descendants(node) {
|
|
5765
|
+
let first = true;
|
|
5766
|
+
for (const descendant of this.#depth(node)) {
|
|
5767
|
+
if (first) {
|
|
5768
|
+
first = false;
|
|
5769
|
+
continue;
|
|
5770
|
+
}
|
|
5771
|
+
yield descendant;
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5774
|
+
document(node) {
|
|
5775
|
+
return this.documents.find((document) => document.index === node.document);
|
|
5776
|
+
}
|
|
5777
|
+
children(node) {
|
|
5778
|
+
const document = this.document(node);
|
|
5779
|
+
if (document === void 0) return [];
|
|
5780
|
+
const children = document.nodes.filter((candidate) => candidate.parent === node.index);
|
|
5781
|
+
if (node.content === void 0) return children;
|
|
5782
|
+
const content = this.documents.find((candidate) => candidate.index === node.content);
|
|
5783
|
+
if (content === void 0) return children;
|
|
5784
|
+
return [...children, ...content.nodes.filter((candidate) => candidate.parent === void 0)];
|
|
5785
|
+
}
|
|
5786
|
+
parent(node) {
|
|
5787
|
+
const document = this.document(node);
|
|
5788
|
+
if (document === void 0) return void 0;
|
|
5789
|
+
return node.parent === void 0 ? this.#owner(node.document) : document.nodes[node.parent];
|
|
5790
|
+
}
|
|
5791
|
+
siblings(node, relation) {
|
|
5792
|
+
const candidates = this.#candidates(node);
|
|
5793
|
+
const index = candidates.findIndex((candidate) => candidate.document === node.document && candidate.index === node.index);
|
|
5794
|
+
if (relation === "preceding") return index < 0 ? [] : candidates.slice(0, index);
|
|
5795
|
+
if (relation === "following") return index < 0 ? [] : candidates.slice(index + 1);
|
|
5796
|
+
return candidates.filter((candidate) => candidate.document !== node.document || candidate.index !== node.index);
|
|
5797
|
+
}
|
|
5798
|
+
ancestors(node) {
|
|
5799
|
+
const ancestors = [];
|
|
5800
|
+
const visited = /* @__PURE__ */ new Set();
|
|
5801
|
+
let current = node;
|
|
5802
|
+
while (true) {
|
|
5803
|
+
const parent = this.parent(current);
|
|
5804
|
+
if (parent === void 0) break;
|
|
5805
|
+
const identity = this.#identity(parent);
|
|
5806
|
+
if (visited.has(identity)) break;
|
|
5807
|
+
visited.add(identity);
|
|
5808
|
+
ancestors.push(parent);
|
|
5809
|
+
current = parent;
|
|
5810
|
+
}
|
|
5811
|
+
return ancestors;
|
|
5812
|
+
}
|
|
5813
|
+
common(first, second) {
|
|
5814
|
+
const firstAncestors = this.#chain(first);
|
|
5815
|
+
const secondAncestors = this.#chain(second);
|
|
5816
|
+
const identities = new Set(firstAncestors.map((node) => this.#identity(node)));
|
|
5817
|
+
return secondAncestors.find((node) => identities.has(this.#identity(node)));
|
|
5818
|
+
}
|
|
5819
|
+
distance(first, second) {
|
|
5820
|
+
const common = this.common(first, second);
|
|
5821
|
+
if (common === void 0) return void 0;
|
|
5822
|
+
const identity = this.#identity(common);
|
|
5823
|
+
const firstDistance = this.#chain(first).findIndex((node) => this.#identity(node) === identity);
|
|
5824
|
+
const secondDistance = this.#chain(second).findIndex((node) => this.#identity(node) === identity);
|
|
5825
|
+
if (firstDistance < 0 || secondDistance < 0) return void 0;
|
|
5826
|
+
return firstDistance + secondDistance;
|
|
5827
|
+
}
|
|
5828
|
+
find(query) {
|
|
5829
|
+
for (const node of this.walk()) if (this.#match(node, query)) return node;
|
|
5830
|
+
}
|
|
5831
|
+
filter(query, limit) {
|
|
5832
|
+
if (limit !== void 0 && (!isInteger(limit) || limit < 0)) throw new BrowserError("Browser node result limit must be a non-negative integer", void 0, { limit });
|
|
5833
|
+
if (limit === 0) return [];
|
|
5834
|
+
const found = [];
|
|
5835
|
+
for (const node of this.walk()) {
|
|
5836
|
+
if (!this.#match(node, query)) continue;
|
|
5837
|
+
found.push(node);
|
|
5838
|
+
if (limit !== void 0 && found.length >= limit) break;
|
|
5839
|
+
}
|
|
5840
|
+
return found;
|
|
5841
|
+
}
|
|
5842
|
+
closest(node, query) {
|
|
5843
|
+
if (this.#match(node, query)) return node;
|
|
5844
|
+
for (const ancestor of this.ancestors(node)) if (this.#match(ancestor, query)) return ancestor;
|
|
5845
|
+
}
|
|
5846
|
+
path(node) {
|
|
5847
|
+
const document = this.document(node);
|
|
5848
|
+
if (document === void 0) return `document:${node.document} > node:${node.index}`;
|
|
5849
|
+
const chain = [...this.ancestors(node)].reverse();
|
|
5850
|
+
chain.push(node);
|
|
5851
|
+
const segments = [];
|
|
5852
|
+
for (const current of chain) {
|
|
5853
|
+
if (current.type !== 1) {
|
|
5854
|
+
segments.push(`${current.name.toLowerCase()}:${current.index}`);
|
|
5855
|
+
continue;
|
|
5856
|
+
}
|
|
5857
|
+
let ordinal = 1;
|
|
5858
|
+
if (current.parent !== void 0) {
|
|
5859
|
+
const owner = this.document(current);
|
|
5860
|
+
for (const sibling of owner?.nodes ?? []) {
|
|
5861
|
+
if (sibling.index >= current.index) break;
|
|
5862
|
+
if (sibling.parent === current.parent && sibling.name === current.name) ordinal += 1;
|
|
5863
|
+
}
|
|
5864
|
+
}
|
|
5865
|
+
segments.push(`${current.name.toLowerCase()}:${ordinal}`);
|
|
5866
|
+
}
|
|
5867
|
+
return `frame(${JSON.stringify(document.frame)}) > ${segments.join(" > ")}`;
|
|
5868
|
+
}
|
|
5869
|
+
#roots() {
|
|
5870
|
+
const roots = [];
|
|
5871
|
+
for (const document of this.documents) roots.push(...document.nodes.filter((node) => node.parent === void 0));
|
|
5872
|
+
for (const document of this.documents) roots.push(...document.nodes);
|
|
5873
|
+
return roots;
|
|
5874
|
+
}
|
|
5875
|
+
#owner(document) {
|
|
5876
|
+
const owners = this.#owners;
|
|
5877
|
+
if (owners !== void 0) return owners.get(document);
|
|
5878
|
+
const index = /* @__PURE__ */ new Map();
|
|
5879
|
+
for (const node of this.walk()) if (node.content !== void 0 && !index.has(node.content)) index.set(node.content, node);
|
|
5880
|
+
this.#owners = index;
|
|
5881
|
+
return index.get(document);
|
|
5882
|
+
}
|
|
5883
|
+
*#depth(root) {
|
|
5884
|
+
const visited = /* @__PURE__ */ new Set();
|
|
5885
|
+
for (const seed of root === void 0 ? this.#roots() : [root]) {
|
|
5886
|
+
const stack = [seed];
|
|
5887
|
+
while (stack.length > 0) {
|
|
5888
|
+
const node = stack.pop();
|
|
5889
|
+
if (node === void 0) break;
|
|
5890
|
+
const identity = this.#identity(node);
|
|
5891
|
+
if (visited.has(identity)) continue;
|
|
5892
|
+
visited.add(identity);
|
|
5893
|
+
yield node;
|
|
5894
|
+
const children = this.children(node);
|
|
5895
|
+
for (let index = children.length - 1; index >= 0; index -= 1) {
|
|
5896
|
+
const child = children[index];
|
|
5897
|
+
if (child !== void 0) stack.push(child);
|
|
5898
|
+
}
|
|
5899
|
+
}
|
|
5900
|
+
}
|
|
5901
|
+
}
|
|
5902
|
+
*#breadth(root) {
|
|
5903
|
+
const visited = /* @__PURE__ */ new Set();
|
|
5904
|
+
for (const seed of root === void 0 ? this.#roots() : [root]) {
|
|
5905
|
+
const queue = [seed];
|
|
5906
|
+
let cursor = 0;
|
|
5907
|
+
while (cursor < queue.length) {
|
|
5908
|
+
const node = queue[cursor];
|
|
5909
|
+
cursor += 1;
|
|
5910
|
+
if (node === void 0) continue;
|
|
5911
|
+
const identity = this.#identity(node);
|
|
5912
|
+
if (visited.has(identity)) continue;
|
|
5913
|
+
visited.add(identity);
|
|
5914
|
+
yield node;
|
|
5915
|
+
queue.push(...this.children(node));
|
|
5916
|
+
}
|
|
5917
|
+
}
|
|
5918
|
+
}
|
|
5919
|
+
#candidates(node) {
|
|
5920
|
+
const parent = this.parent(node);
|
|
5921
|
+
return parent === void 0 ? this.document(node)?.nodes.filter((candidate) => candidate.parent === void 0) ?? [] : this.children(parent);
|
|
5922
|
+
}
|
|
5923
|
+
#chain(node) {
|
|
5924
|
+
return [node, ...this.ancestors(node)];
|
|
5925
|
+
}
|
|
5926
|
+
#match(node, query) {
|
|
5927
|
+
return isBrowserNodeQuery(query) ? matchesBrowserNode(node, query) : query(node);
|
|
5928
|
+
}
|
|
5929
|
+
#identity(node) {
|
|
5930
|
+
return `${node.document}:${node.index}`;
|
|
5931
|
+
}
|
|
5932
|
+
};
|
|
5933
|
+
//#endregion
|
|
6077
5934
|
//#region src/core/BrowserWorker.ts
|
|
6078
5935
|
/**
|
|
6079
5936
|
* Dedicated, shared, or service worker attached through a flattened target session.
|
|
@@ -6362,11 +6219,11 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
|
|
|
6362
6219
|
async snapshot(options) {
|
|
6363
6220
|
this.assert();
|
|
6364
6221
|
const styles = options?.styles ?? [];
|
|
6365
|
-
return decodeBrowserSnapshot(await this.request("DOMSnapshot.captureSnapshot", {
|
|
6222
|
+
return new BrowserSnapshot(decodeBrowserSnapshot(await this.request("DOMSnapshot.captureSnapshot", {
|
|
6366
6223
|
computedStyles: [...styles],
|
|
6367
6224
|
includePaintOrder: options?.paint ?? false,
|
|
6368
6225
|
includeDOMRects: options?.rects ?? false
|
|
6369
|
-
}), styles, options?.limit ?? 1e5);
|
|
6226
|
+
}), styles, options?.limit ?? 1e5));
|
|
6370
6227
|
}
|
|
6371
6228
|
async codegen(options) {
|
|
6372
6229
|
this.assert();
|
|
@@ -7249,7 +7106,24 @@ var BrowserContext = class {
|
|
|
7249
7106
|
function createCDPClient(options) {
|
|
7250
7107
|
return new CDPClient(options);
|
|
7251
7108
|
}
|
|
7109
|
+
/**
|
|
7110
|
+
* Create a navigable browser snapshot from decoded serializable data.
|
|
7111
|
+
*
|
|
7112
|
+
* @param input - Captured documents and computed-style names
|
|
7113
|
+
* @returns A {@link BrowserSnapshotInterface}
|
|
7114
|
+
*
|
|
7115
|
+
* @example
|
|
7116
|
+
* ```ts
|
|
7117
|
+
* import { createBrowserSnapshot, decodeBrowserSnapshot } from '@src/core'
|
|
7118
|
+
*
|
|
7119
|
+
* const snapshot = createBrowserSnapshot(decodeBrowserSnapshot(captured, ['display']))
|
|
7120
|
+
* snapshot.find({ name: 'main' })
|
|
7121
|
+
* ```
|
|
7122
|
+
*/
|
|
7123
|
+
function createBrowserSnapshot(input) {
|
|
7124
|
+
return new BrowserSnapshot(input);
|
|
7125
|
+
}
|
|
7252
7126
|
//#endregion
|
|
7253
|
-
export { BASE64_CHARS, BASE64_LOOKUP, BROWSER_CODEGEN_BINDING_NAME, BROWSER_CODEGEN_SOURCE, BROWSER_DEFAULT_TIMEOUT_MS, BROWSER_DEFAULT_VIEWPORT_HEIGHT, BROWSER_DEFAULT_VIEWPORT_WIDTH, BROWSER_FRAME_WORLD_NAME, BROWSER_HAR_CREATOR, BROWSER_KEY_MODIFIERS, BROWSER_MOUSE_BUTTON_MASKS, BROWSER_RESULT_LIMIT, BROWSER_RESULT_LIMIT_PATTERN, BROWSER_RESULT_LIMIT_SENTINEL_PREFIX, BROWSER_SCREENSHOT_ATTRIBUTE, BROWSER_SNAPSHOT_NODE_LIMIT, BROWSER_STABLE_FRAME_COUNT, BROWSER_STOP_LOADING_TIMEOUT_MS, BROWSER_TEST_ID_ATTRIBUTE, BROWSER_WAIT_POLL_INTERVAL_MS, BrowserAccessibility, BrowserClock, BrowserCodegen, BrowserContext, BrowserCookieManager, BrowserCoverage, BrowserDiagnostics, BrowserDialog, BrowserDownload, BrowserEmulationManager, BrowserError, BrowserFileChooser, BrowserFrame, BrowserHARManager, BrowserHandle, BrowserKeyboard, BrowserLocator, BrowserMouse, BrowserNavigationManager, BrowserNetworkManager, BrowserPage, BrowserPerformance, BrowserPermissionManager, BrowserResultLimitError, BrowserRoute, BrowserScriptManager, BrowserSelectorError, BrowserSelectorManager, BrowserStorageManager, BrowserTouch, BrowserTracing, BrowserWebSocket, BrowserWorker, CDPClient, CDPConnectionError, CDPError, CDPTimeoutError,
|
|
7127
|
+
export { BASE64_CHARS, BASE64_LOOKUP, BROWSER_CODEGEN_BINDING_NAME, BROWSER_CODEGEN_SOURCE, BROWSER_DEFAULT_TIMEOUT_MS, BROWSER_DEFAULT_VIEWPORT_HEIGHT, BROWSER_DEFAULT_VIEWPORT_WIDTH, BROWSER_FRAME_WORLD_NAME, BROWSER_HAR_CREATOR, BROWSER_KEY_MODIFIERS, BROWSER_MOUSE_BUTTON_MASKS, BROWSER_RESULT_LIMIT, BROWSER_RESULT_LIMIT_PATTERN, BROWSER_RESULT_LIMIT_SENTINEL_PREFIX, BROWSER_SCREENSHOT_ATTRIBUTE, BROWSER_SNAPSHOT_NODE_LIMIT, BROWSER_STABLE_FRAME_COUNT, BROWSER_STOP_LOADING_TIMEOUT_MS, BROWSER_TEST_ID_ATTRIBUTE, BROWSER_WAIT_POLL_INTERVAL_MS, BrowserAccessibility, BrowserClock, BrowserCodegen, BrowserContext, BrowserCookieManager, BrowserCoverage, BrowserDiagnostics, BrowserDialog, BrowserDownload, BrowserEmulationManager, BrowserError, BrowserFileChooser, BrowserFrame, BrowserHARManager, BrowserHandle, BrowserKeyboard, BrowserLocator, BrowserMouse, BrowserNavigationManager, BrowserNetworkManager, BrowserPage, BrowserPerformance, BrowserPermissionManager, BrowserResultLimitError, BrowserRoute, BrowserScriptManager, BrowserSelectorError, BrowserSelectorManager, BrowserSnapshot, BrowserStorageManager, BrowserTouch, BrowserTracing, BrowserWebSocket, BrowserWorker, CDPClient, CDPConnectionError, CDPError, CDPTimeoutError, attributeOfBrowserNode, browserHARHeadersToRecord, browserHeadersToProtocol, browserPDFToParams, browserScreenshotToParams, bytesToText, compileActionabilityFunction, compileAttachedLocatorWaitExpression, compileAttachedWaitExpression, compileBrowserBindingCleanup, compileBrowserBindingResult, compileBrowserBindingSource, compileClickExpression, compileCodegenScript, compileDetachedLocatorWaitExpression, compileDetachedWaitExpression, compileFillExpression, compileFunctionWaitExpression, compileHiddenLocatorWaitExpression, compileHiddenWaitExpression, compileLocatorExpression, compileLocatorListExpression, compileScreenshotCleanupExpression, compileScreenshotPreparationExpression, compileSelectExpression, compileStorageClearExpression, compileStorageReadExpression, compileStorageRestoreExpression, compileVisibleLocatorWaitExpression, compileVisibleWaitExpression, computeBrowserButtons, computeBrowserModifiers, concatBytes, cookieToProtocol, createBrowserHAREntry, createBrowserSnapshot, createCDPClient, decodeBase64, decodeBrowserAttributes, decodeBrowserSnapshot, decodeRareBooleanData, decodeRareIntegerData, decodeRareStringData, encodeBase64, guardEvaluateExpression, isBrowserError, isBrowserNodeQuery, isBrowserNodeVisible, isBrowserResultLimitError, isBrowserSelectorError, isCDPConnectionError, isCDPError, isCDPTimeoutError, keyToBrowserInput, matchesBrowserCookieURL, matchesBrowserNode, matchesBrowserRoute, matchesBrowserURL, mediaToFeatures, normalizeCodegenActions, parseBrowserChord, parseCodegenActionPayload, readBrowserAXString, readBrowserAXValue, readBrowserAccessibility, readBrowserBindingCall, readBrowserConsoleMessage, readBrowserCookie, readBrowserCookiePartition, readBrowserCookies, readBrowserCoverageRanges, readBrowserDownloadProgress, readBrowserDownloadStart, readBrowserFrames, readBrowserHeaders, readBrowserMetrics, readBrowserPageError, readBrowserProfile, readBrowserProfileFrame, readBrowserQuad, readBrowserRect, readBrowserRemoteValue, readBrowserRequest, readBrowserRequestFailure, readBrowserResponse, readBrowserResponseRecord, readBrowserScriptCoverage, readBrowserScriptIdentifier, readBrowserSecurity, readBrowserStack, readBrowserStorageEntries, readBrowserStorageOrigin, readBrowserStreamChunk, readBrowserStyleCoverage, readBrowserTiming, readBrowserTimingRange, readBrowserWebSocketFrame, readCodegenNavigateAction, readEvaluationResult, readNumberArray, readSnapshotString, requireBrowserString, textToBytes, validateBrowserAccessibilityOptions, validateBrowserActionOptions, validateBrowserContextOptions, validateBrowserEmulationOptions, validateBrowserHAR, validateBrowserPoint, validateBrowserRange, validateBrowserTimeout, validateBrowserViewport };
|
|
7254
7128
|
|
|
7255
7129
|
//# sourceMappingURL=index.js.map
|