@orkestrel/browser 0.0.6 → 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.
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _orkestrel_contract = require("@orkestrel/contract");
3
3
  let _orkestrel_emitter = require("@orkestrel/emitter");
4
+ let _orkestrel_html = require("@orkestrel/html");
4
5
  //#region src/core/constants.ts
5
6
  /** Base64 alphabet, index-ordered, used to build {@link BASE64_LOOKUP}. */
6
7
  var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -75,7 +76,7 @@ var BROWSER_MOUSE_BUTTON_MASKS = Object.freeze({
75
76
  /** Tool identity embedded in HAR 1.2 documents. */
76
77
  var BROWSER_HAR_CREATOR = Object.freeze({
77
78
  name: "@orkestrel/browser",
78
- version: "0.0.5"
79
+ version: "0.0.8"
79
80
  });
80
81
  /** Attribute used to tag temporary screenshot styles and masks. */
81
82
  var BROWSER_SCREENSHOT_ATTRIBUTE = "data-orkestrel-screenshot";
@@ -559,10 +560,10 @@ function createBrowserHAREntry(pending, duration, body, error) {
559
560
  cookies: [],
560
561
  headers: requestHeaders,
561
562
  queryString: query,
562
- postData: post === void 0 ? void 0 : {
563
+ ...post !== void 0 ? { postData: {
563
564
  mimeType: requestMime,
564
565
  text: post
565
- },
566
+ } } : {},
566
567
  headersSize: -1,
567
568
  bodySize: post === void 0 ? 0 : textToBytes(post).byteLength
568
569
  },
@@ -575,8 +576,10 @@ function createBrowserHAREntry(pending, duration, body, error) {
575
576
  content: {
576
577
  size,
577
578
  mimeType: response?.mime ?? "application/octet-stream",
578
- text,
579
- encoding: text === void 0 ? void 0 : "base64"
579
+ ...text !== void 0 ? {
580
+ text,
581
+ encoding: "base64"
582
+ } : {}
580
583
  },
581
584
  redirectURL: redirect,
582
585
  headersSize: -1,
@@ -1388,7 +1391,7 @@ function readBrowserCookiePartition(value) {
1388
1391
  if (!(0, _orkestrel_contract.isRecord)(value) || !(0, _orkestrel_contract.isString)(value["topLevelSite"])) return void 0;
1389
1392
  return {
1390
1393
  site: value["topLevelSite"],
1391
- ancestor: (0, _orkestrel_contract.isBoolean)(value["hasCrossSiteAncestor"]) ? value["hasCrossSiteAncestor"] : void 0
1394
+ ...(0, _orkestrel_contract.isBoolean)(value["hasCrossSiteAncestor"]) ? { ancestor: value["hasCrossSiteAncestor"] } : {}
1392
1395
  };
1393
1396
  }
1394
1397
  /**
@@ -1610,7 +1613,7 @@ function readBrowserDownloadProgress(value) {
1610
1613
  status,
1611
1614
  received: value["receivedBytes"],
1612
1615
  total: value["totalBytes"],
1613
- path: (0, _orkestrel_contract.isString)(value["filePath"]) ? value["filePath"] : void 0
1616
+ ...(0, _orkestrel_contract.isString)(value["filePath"]) ? { path: value["filePath"] } : {}
1614
1617
  }];
1615
1618
  }
1616
1619
  /**
@@ -2821,326 +2824,6 @@ function decodeBrowserSnapshot(value, styles = [], limit = BROWSER_SNAPSHOT_NODE
2821
2824
  };
2822
2825
  }
2823
2826
  /**
2824
- * Lazily walk every captured browser node in structural depth-first order.
2825
- *
2826
- * @remarks
2827
- * Iframe content documents are entered at their owner element. Orphaned or
2828
- * cyclic protocol nodes are still yielded once through an identity guard.
2829
- *
2830
- * @param snapshot - Captured browser snapshot
2831
- * @returns A lazy node generator
2832
- */
2833
- function* walkBrowserSnapshot(snapshot) {
2834
- const seeds = [];
2835
- for (const document of snapshot.documents) seeds.push(...document.nodes.filter((node) => node.parent === void 0));
2836
- for (const document of snapshot.documents) seeds.push(...document.nodes);
2837
- const visited = /* @__PURE__ */ new Set();
2838
- for (const seed of seeds) {
2839
- const stack = [seed];
2840
- while (stack.length > 0) {
2841
- const node = stack.pop();
2842
- if (node === void 0) break;
2843
- const identity = `${node.document}:${node.index}`;
2844
- if (visited.has(identity)) continue;
2845
- visited.add(identity);
2846
- yield node;
2847
- const children = childrenOfBrowserNode(snapshot, node);
2848
- for (let index = children.length - 1; index >= 0; index -= 1) {
2849
- const child = children[index];
2850
- if (child !== void 0) stack.push(child);
2851
- }
2852
- }
2853
- }
2854
- }
2855
- /**
2856
- * Lazily walk every captured node in structural breadth-first order.
2857
- *
2858
- * @param snapshot - Captured browser snapshot
2859
- * @returns A lazy breadth-first generator
2860
- */
2861
- function* walkBrowserSnapshotBreadthFirst(snapshot) {
2862
- const seeds = [];
2863
- for (const document of snapshot.documents) seeds.push(...document.nodes.filter((node) => node.parent === void 0));
2864
- for (const document of snapshot.documents) seeds.push(...document.nodes);
2865
- const visited = /* @__PURE__ */ new Set();
2866
- for (const seed of seeds) {
2867
- const queue = [seed];
2868
- let cursor = 0;
2869
- while (cursor < queue.length) {
2870
- const node = queue[cursor];
2871
- cursor += 1;
2872
- if (node === void 0) continue;
2873
- const identity = `${node.document}:${node.index}`;
2874
- if (visited.has(identity)) continue;
2875
- visited.add(identity);
2876
- yield node;
2877
- queue.push(...childrenOfBrowserNode(snapshot, node));
2878
- }
2879
- }
2880
- }
2881
- /**
2882
- * Lazily walk one captured browser-node subtree in depth-first order.
2883
- *
2884
- * @param snapshot - Captured browser snapshot
2885
- * @param root - Root node, included as the first result
2886
- * @returns A lazy subtree generator
2887
- */
2888
- function* walkBrowserNode(snapshot, root) {
2889
- const stack = [root];
2890
- const visited = /* @__PURE__ */ new Set();
2891
- while (stack.length > 0) {
2892
- const node = stack.pop();
2893
- if (node === void 0) break;
2894
- const identity = `${node.document}:${node.index}`;
2895
- if (visited.has(identity)) continue;
2896
- visited.add(identity);
2897
- yield node;
2898
- const children = childrenOfBrowserNode(snapshot, node);
2899
- for (let index = children.length - 1; index >= 0; index -= 1) {
2900
- const child = children[index];
2901
- if (child !== void 0) stack.push(child);
2902
- }
2903
- }
2904
- }
2905
- /**
2906
- * Lazily walk one captured browser-node subtree in breadth-first order.
2907
- *
2908
- * @param snapshot - Captured browser snapshot
2909
- * @param root - Root node, included as the first result
2910
- * @returns A lazy breadth-first subtree generator
2911
- */
2912
- function* walkBrowserNodeBreadthFirst(snapshot, root) {
2913
- const queue = [root];
2914
- const visited = /* @__PURE__ */ new Set();
2915
- let cursor = 0;
2916
- while (cursor < queue.length) {
2917
- const node = queue[cursor];
2918
- cursor += 1;
2919
- if (node === void 0) continue;
2920
- const identity = `${node.document}:${node.index}`;
2921
- if (visited.has(identity)) continue;
2922
- visited.add(identity);
2923
- yield node;
2924
- queue.push(...childrenOfBrowserNode(snapshot, node));
2925
- }
2926
- }
2927
- /**
2928
- * Lazily walk descendants of one captured node, excluding the node itself.
2929
- *
2930
- * @param snapshot - Captured browser snapshot
2931
- * @param root - Root whose descendants are required
2932
- * @returns A lazy descendant generator
2933
- */
2934
- function* descendantsOfBrowserNode(snapshot, root) {
2935
- let first = true;
2936
- for (const node of walkBrowserNode(snapshot, root)) {
2937
- if (first) {
2938
- first = false;
2939
- continue;
2940
- }
2941
- yield node;
2942
- }
2943
- }
2944
- /**
2945
- * Resolve the document containing a browser node.
2946
- *
2947
- * @param snapshot - Captured browser snapshot
2948
- * @param node - Node whose document is required
2949
- * @returns The containing document, or undefined
2950
- */
2951
- function documentOfBrowserNode(snapshot, node) {
2952
- return snapshot.documents.find((document) => document.index === node.document);
2953
- }
2954
- /**
2955
- * Return direct children of a captured browser node.
2956
- *
2957
- * @remarks
2958
- * Includes the root nodes of an iframe's linked content document. Shadow DOM
2959
- * and template content already carry flattened parent indexes from CDP.
2960
- *
2961
- * @param snapshot - Captured browser snapshot
2962
- * @param node - Parent node
2963
- * @returns Direct children in snapshot order
2964
- */
2965
- function childrenOfBrowserNode(snapshot, node) {
2966
- const document = documentOfBrowserNode(snapshot, node);
2967
- if (document === void 0) return [];
2968
- const children = document.nodes.filter((candidate) => candidate.parent === node.index);
2969
- if (node.content === void 0) return children;
2970
- const content = snapshot.documents.find((candidate) => candidate.index === node.content);
2971
- if (content === void 0) return children;
2972
- return [...children, ...content.nodes.filter((candidate) => candidate.parent === void 0)];
2973
- }
2974
- /**
2975
- * Resolve the structural parent of a captured browser node.
2976
- *
2977
- * @remarks
2978
- * An iframe content-document root resolves to its owning iframe node.
2979
- *
2980
- * @param snapshot - Captured browser snapshot
2981
- * @param node - Child node
2982
- * @returns Parent node, or undefined for a top-level document root
2983
- */
2984
- function parentOfBrowserNode(snapshot, node) {
2985
- const document = documentOfBrowserNode(snapshot, node);
2986
- if (document === void 0) return void 0;
2987
- return node.parent === void 0 ? findBrowserNode(snapshot, (candidate) => candidate.content === node.document) : document.nodes[node.parent];
2988
- }
2989
- /**
2990
- * Return every structural sibling except the supplied browser node.
2991
- *
2992
- * @param snapshot - Captured browser snapshot
2993
- * @param node - Node whose siblings are required
2994
- * @returns Siblings in structural order
2995
- */
2996
- function siblingsOfBrowserNode(snapshot, node) {
2997
- const parent = parentOfBrowserNode(snapshot, node);
2998
- 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);
2999
- }
3000
- /**
3001
- * Return structural siblings before the supplied browser node.
3002
- *
3003
- * @param snapshot - Captured browser snapshot
3004
- * @param node - Reference node
3005
- * @returns Earlier siblings in structural order
3006
- */
3007
- function precedingSiblingsOfBrowserNode(snapshot, node) {
3008
- const parent = parentOfBrowserNode(snapshot, node);
3009
- const candidates = parent === void 0 ? documentOfBrowserNode(snapshot, node)?.nodes.filter((candidate) => candidate.parent === void 0) ?? [] : childrenOfBrowserNode(snapshot, parent);
3010
- const index = candidates.findIndex((candidate) => candidate.document === node.document && candidate.index === node.index);
3011
- return index < 0 ? [] : candidates.slice(0, index);
3012
- }
3013
- /**
3014
- * Return structural siblings after the supplied browser node.
3015
- *
3016
- * @param snapshot - Captured browser snapshot
3017
- * @param node - Reference node
3018
- * @returns Later siblings in structural order
3019
- */
3020
- function followingSiblingsOfBrowserNode(snapshot, node) {
3021
- const parent = parentOfBrowserNode(snapshot, node);
3022
- const candidates = parent === void 0 ? documentOfBrowserNode(snapshot, node)?.nodes.filter((candidate) => candidate.parent === void 0) ?? [] : childrenOfBrowserNode(snapshot, parent);
3023
- const index = candidates.findIndex((candidate) => candidate.document === node.document && candidate.index === node.index);
3024
- return index < 0 ? [] : candidates.slice(index + 1);
3025
- }
3026
- /**
3027
- * Return the nearest-first ancestor chain for a captured browser node.
3028
- *
3029
- * @param snapshot - Captured browser snapshot
3030
- * @param node - Descendant node
3031
- * @returns Nearest-first ancestor list
3032
- */
3033
- function ancestorsOfBrowserNode(snapshot, node) {
3034
- const ancestors = [];
3035
- const visited = /* @__PURE__ */ new Set();
3036
- let current = node;
3037
- while (true) {
3038
- const parent = parentOfBrowserNode(snapshot, current);
3039
- if (parent === void 0) break;
3040
- const identity = `${parent.document}:${parent.index}`;
3041
- if (visited.has(identity)) break;
3042
- visited.add(identity);
3043
- ancestors.push(parent);
3044
- current = parent;
3045
- }
3046
- return ancestors;
3047
- }
3048
- /**
3049
- * Test whether one captured node is below another across frame boundaries.
3050
- *
3051
- * @param snapshot - Captured browser snapshot
3052
- * @param node - Candidate descendant
3053
- * @param ancestor - Candidate ancestor
3054
- * @returns Whether the relationship exists
3055
- */
3056
- function isBrowserNodeDescendant(snapshot, node, ancestor) {
3057
- return ancestorsOfBrowserNode(snapshot, node).some((candidate) => candidate.document === ancestor.document && candidate.index === ancestor.index);
3058
- }
3059
- /**
3060
- * Resolve the nearest common ancestor of two captured browser nodes.
3061
- *
3062
- * @param snapshot - Captured browser snapshot
3063
- * @param first - First node
3064
- * @param second - Second node
3065
- * @returns Common ancestor, or undefined
3066
- */
3067
- function commonAncestorOfBrowserNodes(snapshot, first, second) {
3068
- const firstAncestors = [first, ...ancestorsOfBrowserNode(snapshot, first)];
3069
- const secondAncestors = [second, ...ancestorsOfBrowserNode(snapshot, second)];
3070
- const identities = new Set(firstAncestors.map((node) => `${node.document}:${node.index}`));
3071
- return secondAncestors.find((node) => identities.has(`${node.document}:${node.index}`));
3072
- }
3073
- /**
3074
- * Compute the edge count between two captured browser nodes.
3075
- *
3076
- * @param snapshot - Captured browser snapshot
3077
- * @param first - First node
3078
- * @param second - Second node
3079
- * @returns Tree distance, or undefined when no common ancestor exists
3080
- */
3081
- function computeBrowserNodeDistance(snapshot, first, second) {
3082
- const common = commonAncestorOfBrowserNodes(snapshot, first, second);
3083
- if (common === void 0) return void 0;
3084
- const firstPath = [first, ...ancestorsOfBrowserNode(snapshot, first)];
3085
- const secondPath = [second, ...ancestorsOfBrowserNode(snapshot, second)];
3086
- const firstDistance = firstPath.findIndex((node) => node.document === common.document && node.index === common.index);
3087
- const secondDistance = secondPath.findIndex((node) => node.document === common.document && node.index === common.index);
3088
- if (firstDistance < 0 || secondDistance < 0) return void 0;
3089
- return firstDistance + secondDistance;
3090
- }
3091
- /**
3092
- * Find the first captured node satisfying a predicate.
3093
- *
3094
- * @param snapshot - Captured browser snapshot
3095
- * @param predicate - Node predicate
3096
- * @returns First matching node, or undefined
3097
- */
3098
- function findBrowserNode(snapshot, predicate) {
3099
- for (const node of walkBrowserSnapshot(snapshot)) if (predicate(node)) return node;
3100
- }
3101
- /**
3102
- * Find captured nodes satisfying a predicate.
3103
- *
3104
- * @param snapshot - Captured browser snapshot
3105
- * @param predicate - Node predicate
3106
- * @param limit - Optional maximum result count
3107
- * @returns Matching nodes in snapshot order
3108
- */
3109
- function findBrowserNodes(snapshot, predicate, limit) {
3110
- if (limit !== void 0 && (!(0, _orkestrel_contract.isInteger)(limit) || limit < 0)) throw new BrowserError("Browser node result limit must be a non-negative integer", void 0, { limit });
3111
- if (limit === 0) return [];
3112
- const found = [];
3113
- for (const node of walkBrowserSnapshot(snapshot)) {
3114
- if (!predicate(node)) continue;
3115
- found.push(node);
3116
- if (limit !== void 0 && found.length >= limit) break;
3117
- }
3118
- return found;
3119
- }
3120
- /**
3121
- * Find the first matching descendant beneath one captured node.
3122
- *
3123
- * @param snapshot - Captured browser snapshot
3124
- * @param root - Root whose descendants are searched
3125
- * @param predicate - Node predicate
3126
- * @returns First matching descendant, or undefined
3127
- */
3128
- function findBrowserDescendant(snapshot, root, predicate) {
3129
- for (const node of descendantsOfBrowserNode(snapshot, root)) if (predicate(node)) return node;
3130
- }
3131
- /**
3132
- * Find the nearest matching node from the candidate through its ancestors.
3133
- *
3134
- * @param snapshot - Captured browser snapshot
3135
- * @param node - Starting node, included in the search
3136
- * @param predicate - Node predicate
3137
- * @returns Closest matching node, or undefined
3138
- */
3139
- function closestBrowserNode(snapshot, node, predicate) {
3140
- if (predicate(node)) return node;
3141
- return ancestorsOfBrowserNode(snapshot, node).find(predicate);
3142
- }
3143
- /**
3144
2827
  * Read one captured node attribute.
3145
2828
  *
3146
2829
  * @param node - Captured browser node
@@ -3151,6 +2834,23 @@ function attributeOfBrowserNode(node, name) {
3151
2834
  return node.attributes[name];
3152
2835
  }
3153
2836
  /**
2837
+ * Test whether a browser-node matcher is a declarative query.
2838
+ *
2839
+ * @param value - Browser-node query or predicate
2840
+ * @returns Whether the matcher is a declarative query
2841
+ *
2842
+ * @example
2843
+ * ```ts
2844
+ * import { isBrowserNodeQuery } from '@src/core'
2845
+ *
2846
+ * isBrowserNodeQuery({ name: 'main' }) // true
2847
+ * isBrowserNodeQuery(() => true) // false
2848
+ * ```
2849
+ */
2850
+ function isBrowserNodeQuery(value) {
2851
+ return !(0, _orkestrel_contract.isFunction)(value);
2852
+ }
2853
+ /**
3154
2854
  * Test a captured node against a declarative query.
3155
2855
  *
3156
2856
  * @param node - Captured browser node
@@ -3178,36 +2878,6 @@ function isBrowserNodeVisible(node) {
3178
2878
  const bounds = node.layout?.bounds;
3179
2879
  return bounds !== void 0 && bounds[2] > 0 && bounds[3] > 0;
3180
2880
  }
3181
- /**
3182
- * Build a deterministic, serializable path for a captured browser node.
3183
- *
3184
- * @param snapshot - Captured browser snapshot
3185
- * @param node - Captured browser node
3186
- * @returns Frame-qualified structural path
3187
- */
3188
- function nodeToPath(snapshot, node) {
3189
- const document = documentOfBrowserNode(snapshot, node);
3190
- if (document === void 0) return `document:${node.document} > node:${node.index}`;
3191
- const chain = [...ancestorsOfBrowserNode(snapshot, node)].reverse();
3192
- chain.push(node);
3193
- const segments = [];
3194
- for (const current of chain) {
3195
- if (current.type !== 1) {
3196
- segments.push(`${current.name.toLowerCase()}:${current.index}`);
3197
- continue;
3198
- }
3199
- let ordinal = 1;
3200
- if (current.parent !== void 0) {
3201
- const owner = documentOfBrowserNode(snapshot, current);
3202
- for (const sibling of owner?.nodes ?? []) {
3203
- if (sibling.index >= current.index) break;
3204
- if (sibling.parent === current.parent && sibling.name === current.name) ordinal += 1;
3205
- }
3206
- }
3207
- segments.push(`${current.name.toLowerCase()}:${ordinal}`);
3208
- }
3209
- return `frame(${JSON.stringify(document.frame)}) > ${segments.join(" > ")}`;
3210
- }
3211
2881
  //#endregion
3212
2882
  //#region src/core/CDPClient.ts
3213
2883
  /**
@@ -3510,7 +3180,7 @@ var BrowserClock = class {
3510
3180
  #installed = false;
3511
3181
  #advancing = false;
3512
3182
  #budgetResolve;
3513
- #budgetHandler = () => this.#budgetResolve?.();
3183
+ #budgetHandler = this.#handleBudget.bind(this);
3514
3184
  constructor(frame) {
3515
3185
  this.#frame = frame;
3516
3186
  }
@@ -3610,6 +3280,9 @@ var BrowserClock = class {
3610
3280
  #idle() {
3611
3281
  if (this.#advancing) throw new BrowserError("Browser clock advance is already active");
3612
3282
  }
3283
+ #handleBudget() {
3284
+ this.#budgetResolve?.();
3285
+ }
3613
3286
  };
3614
3287
  //#endregion
3615
3288
  //#region src/core/BrowserCoverage.ts
@@ -3807,8 +3480,8 @@ var BrowserCookieManager = class {
3807
3480
  expires: cookie.expires,
3808
3481
  http: cookie.http,
3809
3482
  secure: cookie.secure,
3810
- site: cookie.site,
3811
- partition: cookie.partition
3483
+ ...cookie.site !== void 0 ? { site: cookie.site } : {},
3484
+ ...cookie.partition !== void 0 ? { partition: cookie.partition } : {}
3812
3485
  })) };
3813
3486
  if (this.#context !== void 0) restore["browserContextId"] = this.#context;
3814
3487
  const result = await this.#client.send("Storage.setCookies", restore);
@@ -3952,12 +3625,14 @@ var BrowserCodegen = class {
3952
3625
  #stopping;
3953
3626
  #shutdown;
3954
3627
  #emitter;
3628
+ #onBindingCalled = this.#handleBindingCalled.bind(this);
3629
+ #onFrameNavigated = this.#handleFrameNavigated.bind(this);
3955
3630
  constructor(client, sessionId, options) {
3956
3631
  this.#client = client;
3957
3632
  this.#sessionId = sessionId;
3958
3633
  this.#emitter = new _orkestrel_emitter.Emitter({
3959
- on: options?.on,
3960
- error: options?.error
3634
+ ...options?.on !== void 0 ? { on: options.on } : {},
3635
+ ...options?.error !== void 0 ? { error: options.error } : {}
3961
3636
  });
3962
3637
  }
3963
3638
  get emitter() {
@@ -4052,19 +3727,19 @@ var BrowserCodegen = class {
4052
3727
  this.#actions = [];
4053
3728
  this.#emitter.destroy();
4054
3729
  }
4055
- #onBindingCalled = (params) => {
3730
+ #handleBindingCalled(params) {
4056
3731
  if (params["name"] !== "__orkestrelBrowserCodegen") return;
4057
3732
  const action = parseCodegenActionPayload(params["payload"]);
4058
3733
  if (action === void 0) return;
4059
3734
  this.#actions.push(action);
4060
3735
  this.#emitter.emit("action", action);
4061
- };
4062
- #onFrameNavigated = (params) => {
3736
+ }
3737
+ #handleFrameNavigated(params) {
4063
3738
  const action = readCodegenNavigateAction(params);
4064
3739
  if (action === void 0) return;
4065
3740
  this.#actions.push(action);
4066
3741
  this.#emitter.emit("action", action);
4067
- };
3742
+ }
4068
3743
  #unsubscribe() {
4069
3744
  this.#client.unsubscribe("Runtime.bindingCalled", this.#onBindingCalled, this.#sessionId);
4070
3745
  this.#client.unsubscribe("Page.frameNavigated", this.#onFrameNavigated, this.#sessionId);
@@ -4134,12 +3809,7 @@ var BrowserTracing = class {
4134
3809
  #active = false;
4135
3810
  #options;
4136
3811
  #completion;
4137
- #completeHandler = (params) => {
4138
- const completion = this.#completion;
4139
- if (completion === void 0) return;
4140
- if ((0, _orkestrel_contract.isString)(params["stream"])) completion.resolve(params["stream"]);
4141
- else completion.reject(new BrowserError("Browser trace did not return an IO stream"));
4142
- };
3812
+ #completeHandler = this.#handleComplete.bind(this);
4143
3813
  constructor(frame, writer) {
4144
3814
  this.#frame = frame;
4145
3815
  this.#writer = writer;
@@ -4215,6 +3885,12 @@ var BrowserTracing = class {
4215
3885
  clearTimeout(timer);
4216
3886
  }
4217
3887
  }
3888
+ #handleComplete(params) {
3889
+ const completion = this.#completion;
3890
+ if (completion === void 0) return;
3891
+ if ((0, _orkestrel_contract.isString)(params["stream"])) completion.resolve(params["stream"]);
3892
+ else completion.reject(new BrowserError("Browser trace did not return an IO stream"));
3893
+ }
4218
3894
  };
4219
3895
  //#endregion
4220
3896
  //#region src/core/BrowserDiagnostics.ts
@@ -4932,8 +4608,8 @@ var BrowserLocator = class BrowserLocator {
4932
4608
  let targetHandle;
4933
4609
  try {
4934
4610
  await target.wait({
4935
- timeout: options?.timeout,
4936
- strict: options?.strict,
4611
+ ...options?.timeout !== void 0 ? { timeout: options.timeout } : {},
4612
+ ...options?.strict !== void 0 ? { strict: options.strict } : {},
4937
4613
  state: "visible"
4938
4614
  });
4939
4615
  targetHandle = await target.frame.handle(compileLocatorExpression(target.query));
@@ -4956,8 +4632,8 @@ var BrowserLocator = class BrowserLocator {
4956
4632
  async #resolve(options, state) {
4957
4633
  validateBrowserActionOptions(options);
4958
4634
  await this.wait({
4959
- timeout: options?.timeout,
4960
- strict: options?.strict,
4635
+ ...options?.timeout !== void 0 ? { timeout: options.timeout } : {},
4636
+ ...options?.strict !== void 0 ? { strict: options.strict } : {},
4961
4637
  state
4962
4638
  });
4963
4639
  return await this.#frame.handle(compileLocatorExpression(this.#query));
@@ -4965,7 +4641,7 @@ var BrowserLocator = class BrowserLocator {
4965
4641
  async #point(handle, options, actionability) {
4966
4642
  if (options?.force !== true) await handle.call(compileActionabilityFunction({
4967
4643
  ...actionability,
4968
- position: options?.position
4644
+ ...options?.position !== void 0 ? { position: options.position } : {}
4969
4645
  }));
4970
4646
  const quad = await this.#quad(handle);
4971
4647
  if (options?.position === void 0) return quad.center;
@@ -5004,9 +4680,7 @@ var BrowserLocator = class BrowserLocator {
5004
4680
  case "enabled":
5005
4681
  expression = `(() => { const element = ${compileLocatorExpression(this.#query)}; return Boolean(element && !element.matches(':disabled')) })()`;
5006
4682
  break;
5007
- case "editable":
5008
- expression = `(() => { const element = ${compileLocatorExpression(this.#query)}; return Boolean(element && !element.matches(':disabled,[readonly]') && (element.isContentEditable || 'value' in element)) })()`;
5009
- break;
4683
+ case "editable": expression = `(() => { const element = ${compileLocatorExpression(this.#query)}; return Boolean(element && !element.matches(':disabled,[readonly]') && (element.isContentEditable || 'value' in element)) })()`;
5010
4684
  }
5011
4685
  return await this.#frame.evaluate(expression) === true;
5012
4686
  }
@@ -5039,29 +4713,29 @@ var BrowserSelectorManager = class {
5039
4713
  return new BrowserLocator(this.#frame, {
5040
4714
  selector: "role",
5041
4715
  value,
5042
- name: options?.name,
5043
- exact: options?.exact
4716
+ ...options?.name !== void 0 ? { name: options.name } : {},
4717
+ ...options?.exact !== void 0 ? { exact: options.exact } : {}
5044
4718
  });
5045
4719
  }
5046
4720
  text(value, options) {
5047
4721
  return new BrowserLocator(this.#frame, {
5048
4722
  selector: "text",
5049
4723
  value,
5050
- exact: options?.exact
4724
+ ...options?.exact !== void 0 ? { exact: options.exact } : {}
5051
4725
  });
5052
4726
  }
5053
4727
  label(value, options) {
5054
4728
  return new BrowserLocator(this.#frame, {
5055
4729
  selector: "label",
5056
4730
  value,
5057
- exact: options?.exact
4731
+ ...options?.exact !== void 0 ? { exact: options.exact } : {}
5058
4732
  });
5059
4733
  }
5060
4734
  placeholder(value, options) {
5061
4735
  return new BrowserLocator(this.#frame, {
5062
4736
  selector: "placeholder",
5063
4737
  value,
5064
- exact: options?.exact
4738
+ ...options?.exact !== void 0 ? { exact: options.exact } : {}
5065
4739
  });
5066
4740
  }
5067
4741
  test(value) {
@@ -5167,7 +4841,7 @@ var BrowserFrame = class {
5167
4841
  this.assert();
5168
4842
  const [title, html, text, currentUrl] = await Promise.all([
5169
4843
  this.#evaluate("document.title"),
5170
- this.#evaluate(guardEvaluateExpression("document.documentElement.outerHTML", BROWSER_RESULT_LIMIT)),
4844
+ this.#captureHTML(),
5171
4845
  this.#evaluate(guardEvaluateExpression("document.body ? document.body.innerText : \"\"", BROWSER_RESULT_LIMIT)),
5172
4846
  this.#evaluate("location.href")
5173
4847
  ]);
@@ -5180,6 +4854,11 @@ var BrowserFrame = class {
5180
4854
  text: requireBrowserString(text, "Document text")
5181
4855
  };
5182
4856
  }
4857
+ async article() {
4858
+ this.assert();
4859
+ const html = requireBrowserString(await this.#captureHTML(), "Document HTML");
4860
+ return (0, _orkestrel_html.renderText)((0, _orkestrel_html.createHTML)(html).distill().document);
4861
+ }
5183
4862
  async click(selector, options) {
5184
4863
  this.assert();
5185
4864
  await this.#selectors.css(selector).click(options);
@@ -5240,6 +4919,9 @@ var BrowserFrame = class {
5240
4919
  update(url) {
5241
4920
  this.#url = url;
5242
4921
  }
4922
+ async #captureHTML() {
4923
+ return await this.#evaluate(guardEvaluateExpression("document.documentElement.outerHTML", BROWSER_RESULT_LIMIT));
4924
+ }
5243
4925
  async #evaluate(expression, timeout) {
5244
4926
  const session = await this.#sessionId();
5245
4927
  const params = {
@@ -5331,45 +5013,11 @@ var BrowserHARManager = class {
5331
5013
  #failure;
5332
5014
  #archive;
5333
5015
  #fallback = false;
5334
- #requestHandler = (request) => {
5335
- if (!this.#recording) return;
5336
- this.#pending.set(request.id, {
5337
- request,
5338
- started: request.walltime === void 0 ? Date.now() : request.walltime * 1e3,
5339
- response: void 0
5340
- });
5341
- };
5342
- #responseHandler = (response) => {
5343
- const pending = this.#pending.get(response.id);
5344
- if (pending === void 0) return;
5345
- this.#pending.set(response.id, {
5346
- request: pending.request,
5347
- started: pending.started,
5348
- response
5349
- });
5350
- };
5351
- #failureHandler = (failure) => {
5352
- const pending = this.#pending.get(failure.id);
5353
- if (pending === void 0) return;
5354
- this.#pending.delete(failure.id);
5355
- this.#entries.push(createBrowserHAREntry(pending, Math.max(0, Date.now() - pending.started), void 0, failure.error));
5356
- };
5357
- #finishHandler = (id) => this.#track(this.#complete(id));
5358
- #replayHandler = async (route) => {
5359
- const entry = this.#archive?.log.entries.find((candidate) => candidate.request.url === route.request.url && candidate.request.method === route.request.method);
5360
- if (entry === void 0 || entry.response.status === 0) {
5361
- if (this.#fallback) await route.continue();
5362
- else await route.abort("Failed");
5363
- return;
5364
- }
5365
- const body = entry.response.content.text === void 0 ? void 0 : entry.response.content.encoding === "base64" ? decodeBase64(entry.response.content.text) : entry.response.content.text;
5366
- await route.fulfill({
5367
- status: entry.response.status,
5368
- phrase: entry.response.statusText,
5369
- headers: browserHARHeadersToRecord(entry.response.headers),
5370
- body
5371
- });
5372
- };
5016
+ #requestHandler = this.#handleRequest.bind(this);
5017
+ #responseHandler = this.#handleResponse.bind(this);
5018
+ #failureHandler = this.#handleFailure.bind(this);
5019
+ #finishHandler = this.#handleFinish.bind(this);
5020
+ #replayHandler = this.#handleReplay.bind(this);
5373
5021
  constructor(network, writer) {
5374
5022
  this.#network = network;
5375
5023
  this.#writer = writer;
@@ -5448,6 +5096,47 @@ var BrowserHARManager = class {
5448
5096
  this.#entries.length = 0;
5449
5097
  this.#pending.clear();
5450
5098
  }
5099
+ #handleRequest(request) {
5100
+ if (!this.#recording) return;
5101
+ this.#pending.set(request.id, {
5102
+ request,
5103
+ started: request.walltime === void 0 ? Date.now() : request.walltime * 1e3,
5104
+ response: void 0
5105
+ });
5106
+ }
5107
+ #handleResponse(response) {
5108
+ const pending = this.#pending.get(response.id);
5109
+ if (pending === void 0) return;
5110
+ this.#pending.set(response.id, {
5111
+ request: pending.request,
5112
+ started: pending.started,
5113
+ response
5114
+ });
5115
+ }
5116
+ #handleFailure(failure) {
5117
+ const pending = this.#pending.get(failure.id);
5118
+ if (pending === void 0) return;
5119
+ this.#pending.delete(failure.id);
5120
+ this.#entries.push(createBrowserHAREntry(pending, Math.max(0, Date.now() - pending.started), void 0, failure.error));
5121
+ }
5122
+ #handleFinish(id) {
5123
+ this.#track(this.#complete(id));
5124
+ }
5125
+ async #handleReplay(route) {
5126
+ const entry = this.#archive?.log.entries.find((candidate) => candidate.request.url === route.request.url && candidate.request.method === route.request.method);
5127
+ if (entry === void 0 || entry.response.status === 0) {
5128
+ if (this.#fallback) await route.continue();
5129
+ else await route.abort("Failed");
5130
+ return;
5131
+ }
5132
+ const body = entry.response.content.text === void 0 ? void 0 : entry.response.content.encoding === "base64" ? decodeBase64(entry.response.content.text) : entry.response.content.text;
5133
+ await route.fulfill({
5134
+ status: entry.response.status,
5135
+ phrase: entry.response.statusText,
5136
+ headers: browserHARHeadersToRecord(entry.response.headers),
5137
+ ...body !== void 0 ? { body } : {}
5138
+ });
5139
+ }
5451
5140
  async #complete(id) {
5452
5141
  const pending = this.#pending.get(id);
5453
5142
  if (pending === void 0) return;
@@ -5597,54 +5286,17 @@ var BrowserNetworkManager = class {
5597
5286
  #fetch = false;
5598
5287
  #destroyed = false;
5599
5288
  #starting;
5600
- #requestHandler = (params) => {
5601
- const request = readBrowserRequest(params);
5602
- if (request !== void 0) this.#emitter.emit("request", request);
5603
- };
5604
- #responseHandler = (params) => {
5605
- const response = readBrowserResponse(params);
5606
- if (response !== void 0) this.#emitter.emit("response", response);
5607
- };
5608
- #failureHandler = (params) => {
5609
- const failure = readBrowserRequestFailure(params);
5610
- if (failure !== void 0) this.#emitter.emit("failure", failure);
5611
- };
5612
- #finishHandler = (params) => {
5613
- if ((0, _orkestrel_contract.isString)(params["requestId"])) this.#emitter.emit("finish", params["requestId"]);
5614
- };
5615
- #pausedHandler = (params) => {
5616
- this.#route(params).catch(() => void 0);
5617
- };
5618
- #authHandler = (params) => {
5619
- this.#authenticate(params).catch(() => void 0);
5620
- };
5621
- #socketHandler = (params) => {
5622
- if (!(0, _orkestrel_contract.isString)(params["requestId"]) || !(0, _orkestrel_contract.isString)(params["url"])) return;
5623
- const socket = new BrowserWebSocket(params["requestId"], params["url"]);
5624
- this.#sockets.set(socket.id, socket);
5625
- this.#emitter.emit("socket", socket);
5626
- };
5627
- #socketReceiveHandler = (params) => {
5628
- const socket = (0, _orkestrel_contract.isString)(params["requestId"]) ? this.#sockets.get(params["requestId"]) : void 0;
5629
- const frame = readBrowserWebSocketFrame(params);
5630
- if (socket !== void 0 && frame !== void 0) socket.receive(frame);
5631
- };
5632
- #socketTransmitHandler = (params) => {
5633
- const socket = (0, _orkestrel_contract.isString)(params["requestId"]) ? this.#sockets.get(params["requestId"]) : void 0;
5634
- const frame = readBrowserWebSocketFrame(params);
5635
- if (socket !== void 0 && frame !== void 0) socket.transmit(frame);
5636
- };
5637
- #socketErrorHandler = (params) => {
5638
- const socket = (0, _orkestrel_contract.isString)(params["requestId"]) ? this.#sockets.get(params["requestId"]) : void 0;
5639
- if (socket !== void 0 && (0, _orkestrel_contract.isString)(params["errorMessage"])) socket.fail(params["errorMessage"]);
5640
- };
5641
- #socketCloseHandler = (params) => {
5642
- if (!(0, _orkestrel_contract.isString)(params["requestId"])) return;
5643
- const socket = this.#sockets.get(params["requestId"]);
5644
- if (socket === void 0) return;
5645
- this.#sockets.delete(params["requestId"]);
5646
- socket.close((0, _orkestrel_contract.isFiniteNumber)(params["timestamp"]) ? params["timestamp"] : Date.now() / 1e3);
5647
- };
5289
+ #requestHandler = this.#handleRequest.bind(this);
5290
+ #responseHandler = this.#handleResponse.bind(this);
5291
+ #failureHandler = this.#handleFailure.bind(this);
5292
+ #finishHandler = this.#handleFinish.bind(this);
5293
+ #pausedHandler = this.#handlePause.bind(this);
5294
+ #authHandler = this.#handleAuth.bind(this);
5295
+ #socketHandler = this.#handleSocket.bind(this);
5296
+ #socketReceiveHandler = this.#handleSocketReceive.bind(this);
5297
+ #socketTransmitHandler = this.#handleSocketTransmit.bind(this);
5298
+ #socketErrorHandler = this.#handleSocketError.bind(this);
5299
+ #socketCloseHandler = this.#handleSocketClose.bind(this);
5648
5300
  constructor(frame, writer) {
5649
5301
  this.#frame = frame;
5650
5302
  this.#emitter = new _orkestrel_emitter.Emitter();
@@ -5774,29 +5426,77 @@ var BrowserNetworkManager = class {
5774
5426
  this.#emitter.destroy();
5775
5427
  if (failed) throw failure;
5776
5428
  }
5777
- async #begin() {
5778
- try {
5779
- await this.#subscribe();
5780
- await this.#frame.send("Network.enable");
5781
- this.#started = true;
5782
- await this.#configure();
5783
- } catch (error) {
5784
- this.#started = false;
5785
- this.#fetch = false;
5786
- await this.#frame.send("Fetch.disable").catch(() => void 0);
5787
- await this.#frame.send("Network.disable").catch(() => void 0);
5788
- await this.#unsubscribe().catch(() => void 0);
5789
- throw error;
5790
- }
5429
+ #handleRequest(params) {
5430
+ const request = readBrowserRequest(params);
5431
+ if (request !== void 0) this.#emitter.emit("request", request);
5791
5432
  }
5792
- async #configure() {
5793
- if (!(this.#routes.length > 0 || this.#credentials !== void 0)) {
5794
- if (this.#fetch) {
5795
- await this.#frame.send("Fetch.disable");
5796
- this.#fetch = false;
5797
- }
5798
- return;
5799
- }
5433
+ #handleResponse(params) {
5434
+ const response = readBrowserResponse(params);
5435
+ if (response !== void 0) this.#emitter.emit("response", response);
5436
+ }
5437
+ #handleFailure(params) {
5438
+ const failure = readBrowserRequestFailure(params);
5439
+ if (failure !== void 0) this.#emitter.emit("failure", failure);
5440
+ }
5441
+ #handleFinish(params) {
5442
+ if ((0, _orkestrel_contract.isString)(params["requestId"])) this.#emitter.emit("finish", params["requestId"]);
5443
+ }
5444
+ #handlePause(params) {
5445
+ this.#route(params).catch(() => void 0);
5446
+ }
5447
+ #handleAuth(params) {
5448
+ this.#authenticate(params).catch(() => void 0);
5449
+ }
5450
+ #handleSocket(params) {
5451
+ if (!(0, _orkestrel_contract.isString)(params["requestId"]) || !(0, _orkestrel_contract.isString)(params["url"])) return;
5452
+ const socket = new BrowserWebSocket(params["requestId"], params["url"]);
5453
+ this.#sockets.set(socket.id, socket);
5454
+ this.#emitter.emit("socket", socket);
5455
+ }
5456
+ #handleSocketReceive(params) {
5457
+ const socket = (0, _orkestrel_contract.isString)(params["requestId"]) ? this.#sockets.get(params["requestId"]) : void 0;
5458
+ const frame = readBrowserWebSocketFrame(params);
5459
+ if (socket !== void 0 && frame !== void 0) socket.receive(frame);
5460
+ }
5461
+ #handleSocketTransmit(params) {
5462
+ const socket = (0, _orkestrel_contract.isString)(params["requestId"]) ? this.#sockets.get(params["requestId"]) : void 0;
5463
+ const frame = readBrowserWebSocketFrame(params);
5464
+ if (socket !== void 0 && frame !== void 0) socket.transmit(frame);
5465
+ }
5466
+ #handleSocketError(params) {
5467
+ const socket = (0, _orkestrel_contract.isString)(params["requestId"]) ? this.#sockets.get(params["requestId"]) : void 0;
5468
+ if (socket !== void 0 && (0, _orkestrel_contract.isString)(params["errorMessage"])) socket.fail(params["errorMessage"]);
5469
+ }
5470
+ #handleSocketClose(params) {
5471
+ if (!(0, _orkestrel_contract.isString)(params["requestId"])) return;
5472
+ const socket = this.#sockets.get(params["requestId"]);
5473
+ if (socket === void 0) return;
5474
+ this.#sockets.delete(params["requestId"]);
5475
+ socket.close((0, _orkestrel_contract.isFiniteNumber)(params["timestamp"]) ? params["timestamp"] : Date.now() / 1e3);
5476
+ }
5477
+ async #begin() {
5478
+ try {
5479
+ await this.#subscribe();
5480
+ await this.#frame.send("Network.enable");
5481
+ this.#started = true;
5482
+ await this.#configure();
5483
+ } catch (error) {
5484
+ this.#started = false;
5485
+ this.#fetch = false;
5486
+ await this.#frame.send("Fetch.disable").catch(() => void 0);
5487
+ await this.#frame.send("Network.disable").catch(() => void 0);
5488
+ await this.#unsubscribe().catch(() => void 0);
5489
+ throw error;
5490
+ }
5491
+ }
5492
+ async #configure() {
5493
+ if (!(this.#routes.length > 0 || this.#credentials !== void 0)) {
5494
+ if (this.#fetch) {
5495
+ await this.#frame.send("Fetch.disable");
5496
+ this.#fetch = false;
5497
+ }
5498
+ return;
5499
+ }
5800
5500
  await this.#frame.send("Fetch.enable", {
5801
5501
  patterns: this.#routes.map((definition) => ({
5802
5502
  urlPattern: definition.query.url ?? "*",
@@ -5877,21 +5577,8 @@ var BrowserNetworkManager = class {
5877
5577
  var BrowserNavigationManager = class {
5878
5578
  #page;
5879
5579
  #waits = /* @__PURE__ */ new Map();
5880
- #navigateHandler = (url) => {
5881
- for (const [id, wait] of this.#waits) {
5882
- if (!matchesBrowserURL(url, wait.pattern)) continue;
5883
- clearTimeout(wait.timer);
5884
- this.#waits.delete(id);
5885
- wait.resolve(url);
5886
- }
5887
- };
5888
- #closeHandler = () => {
5889
- for (const [id, wait] of this.#waits) {
5890
- clearTimeout(wait.timer);
5891
- this.#waits.delete(id);
5892
- wait.reject(new BrowserError("Browser URL wait ended because the page closed"));
5893
- }
5894
- };
5580
+ #navigateHandler = this.#handleNavigate.bind(this);
5581
+ #closeHandler = this.#handleClose.bind(this);
5895
5582
  constructor(page) {
5896
5583
  this.#page = page;
5897
5584
  this.#page.emitter.on("navigate", this.#navigateHandler);
@@ -5926,6 +5613,21 @@ var BrowserNavigationManager = class {
5926
5613
  timeout
5927
5614
  });
5928
5615
  }
5616
+ #handleNavigate(url) {
5617
+ for (const [id, wait] of this.#waits) {
5618
+ if (!matchesBrowserURL(url, wait.pattern)) continue;
5619
+ clearTimeout(wait.timer);
5620
+ this.#waits.delete(id);
5621
+ wait.resolve(url);
5622
+ }
5623
+ }
5624
+ #handleClose() {
5625
+ for (const [id, wait] of this.#waits) {
5626
+ clearTimeout(wait.timer);
5627
+ this.#waits.delete(id);
5628
+ wait.reject(new BrowserError("Browser URL wait ended because the page closed"));
5629
+ }
5630
+ }
5929
5631
  #timeout(options) {
5930
5632
  const timeout = options?.timeout ?? 3e4;
5931
5633
  validateBrowserTimeout(timeout);
@@ -5943,9 +5645,7 @@ var BrowserScriptManager = class {
5943
5645
  #scripts = /* @__PURE__ */ new Map();
5944
5646
  #subscribed = false;
5945
5647
  #destroyed = false;
5946
- #bindingHandler = (params) => {
5947
- this.#call(params).catch(() => void 0);
5948
- };
5648
+ #bindingHandler = this.#handleBinding.bind(this);
5949
5649
  constructor(frame) {
5950
5650
  this.#frame = frame;
5951
5651
  }
@@ -6041,11 +5741,197 @@ var BrowserScriptManager = class {
6041
5741
  });
6042
5742
  }
6043
5743
  }
5744
+ #handleBinding(params) {
5745
+ this.#call(params).catch(() => void 0);
5746
+ }
6044
5747
  #assert() {
6045
5748
  if (this.#destroyed) throw new BrowserError("Browser script manager is destroyed");
6046
5749
  }
6047
5750
  };
6048
5751
  //#endregion
5752
+ //#region src/core/BrowserSnapshot.ts
5753
+ /** A navigable, serializable browser DOM snapshot. */
5754
+ var BrowserSnapshot = class {
5755
+ #owners;
5756
+ documents;
5757
+ styles;
5758
+ constructor(input) {
5759
+ this.documents = Object.freeze([...input.documents]);
5760
+ this.styles = Object.freeze([...input.styles]);
5761
+ }
5762
+ walk(options) {
5763
+ return options?.order === "breadth" ? this.#breadth(options.root) : this.#depth(options?.root);
5764
+ }
5765
+ *descendants(node) {
5766
+ let first = true;
5767
+ for (const descendant of this.#depth(node)) {
5768
+ if (first) {
5769
+ first = false;
5770
+ continue;
5771
+ }
5772
+ yield descendant;
5773
+ }
5774
+ }
5775
+ document(node) {
5776
+ return this.documents.find((document) => document.index === node.document);
5777
+ }
5778
+ children(node) {
5779
+ const document = this.document(node);
5780
+ if (document === void 0) return [];
5781
+ const children = document.nodes.filter((candidate) => candidate.parent === node.index);
5782
+ if (node.content === void 0) return children;
5783
+ const content = this.documents.find((candidate) => candidate.index === node.content);
5784
+ if (content === void 0) return children;
5785
+ return [...children, ...content.nodes.filter((candidate) => candidate.parent === void 0)];
5786
+ }
5787
+ parent(node) {
5788
+ const document = this.document(node);
5789
+ if (document === void 0) return void 0;
5790
+ return node.parent === void 0 ? this.#owner(node.document) : document.nodes[node.parent];
5791
+ }
5792
+ siblings(node, relation) {
5793
+ const candidates = this.#candidates(node);
5794
+ const index = candidates.findIndex((candidate) => candidate.document === node.document && candidate.index === node.index);
5795
+ if (relation === "preceding") return index < 0 ? [] : candidates.slice(0, index);
5796
+ if (relation === "following") return index < 0 ? [] : candidates.slice(index + 1);
5797
+ return candidates.filter((candidate) => candidate.document !== node.document || candidate.index !== node.index);
5798
+ }
5799
+ ancestors(node) {
5800
+ const ancestors = [];
5801
+ const visited = /* @__PURE__ */ new Set();
5802
+ let current = node;
5803
+ while (true) {
5804
+ const parent = this.parent(current);
5805
+ if (parent === void 0) break;
5806
+ const identity = this.#identity(parent);
5807
+ if (visited.has(identity)) break;
5808
+ visited.add(identity);
5809
+ ancestors.push(parent);
5810
+ current = parent;
5811
+ }
5812
+ return ancestors;
5813
+ }
5814
+ common(first, second) {
5815
+ const firstAncestors = this.#chain(first);
5816
+ const secondAncestors = this.#chain(second);
5817
+ const identities = new Set(firstAncestors.map((node) => this.#identity(node)));
5818
+ return secondAncestors.find((node) => identities.has(this.#identity(node)));
5819
+ }
5820
+ distance(first, second) {
5821
+ const common = this.common(first, second);
5822
+ if (common === void 0) return void 0;
5823
+ const identity = this.#identity(common);
5824
+ const firstDistance = this.#chain(first).findIndex((node) => this.#identity(node) === identity);
5825
+ const secondDistance = this.#chain(second).findIndex((node) => this.#identity(node) === identity);
5826
+ if (firstDistance < 0 || secondDistance < 0) return void 0;
5827
+ return firstDistance + secondDistance;
5828
+ }
5829
+ find(query) {
5830
+ for (const node of this.walk()) if (this.#match(node, query)) return node;
5831
+ }
5832
+ filter(query, limit) {
5833
+ if (limit !== void 0 && (!(0, _orkestrel_contract.isInteger)(limit) || limit < 0)) throw new BrowserError("Browser node result limit must be a non-negative integer", void 0, { limit });
5834
+ if (limit === 0) return [];
5835
+ const found = [];
5836
+ for (const node of this.walk()) {
5837
+ if (!this.#match(node, query)) continue;
5838
+ found.push(node);
5839
+ if (limit !== void 0 && found.length >= limit) break;
5840
+ }
5841
+ return found;
5842
+ }
5843
+ closest(node, query) {
5844
+ if (this.#match(node, query)) return node;
5845
+ for (const ancestor of this.ancestors(node)) if (this.#match(ancestor, query)) return ancestor;
5846
+ }
5847
+ path(node) {
5848
+ const document = this.document(node);
5849
+ if (document === void 0) return `document:${node.document} > node:${node.index}`;
5850
+ const chain = [...this.ancestors(node)].reverse();
5851
+ chain.push(node);
5852
+ const segments = [];
5853
+ for (const current of chain) {
5854
+ if (current.type !== 1) {
5855
+ segments.push(`${current.name.toLowerCase()}:${current.index}`);
5856
+ continue;
5857
+ }
5858
+ let ordinal = 1;
5859
+ if (current.parent !== void 0) {
5860
+ const owner = this.document(current);
5861
+ for (const sibling of owner?.nodes ?? []) {
5862
+ if (sibling.index >= current.index) break;
5863
+ if (sibling.parent === current.parent && sibling.name === current.name) ordinal += 1;
5864
+ }
5865
+ }
5866
+ segments.push(`${current.name.toLowerCase()}:${ordinal}`);
5867
+ }
5868
+ return `frame(${JSON.stringify(document.frame)}) > ${segments.join(" > ")}`;
5869
+ }
5870
+ #roots() {
5871
+ const roots = [];
5872
+ for (const document of this.documents) roots.push(...document.nodes.filter((node) => node.parent === void 0));
5873
+ for (const document of this.documents) roots.push(...document.nodes);
5874
+ return roots;
5875
+ }
5876
+ #owner(document) {
5877
+ const owners = this.#owners;
5878
+ if (owners !== void 0) return owners.get(document);
5879
+ const index = /* @__PURE__ */ new Map();
5880
+ for (const node of this.walk()) if (node.content !== void 0 && !index.has(node.content)) index.set(node.content, node);
5881
+ this.#owners = index;
5882
+ return index.get(document);
5883
+ }
5884
+ *#depth(root) {
5885
+ const visited = /* @__PURE__ */ new Set();
5886
+ for (const seed of root === void 0 ? this.#roots() : [root]) {
5887
+ const stack = [seed];
5888
+ while (stack.length > 0) {
5889
+ const node = stack.pop();
5890
+ if (node === void 0) break;
5891
+ const identity = this.#identity(node);
5892
+ if (visited.has(identity)) continue;
5893
+ visited.add(identity);
5894
+ yield node;
5895
+ const children = this.children(node);
5896
+ for (let index = children.length - 1; index >= 0; index -= 1) {
5897
+ const child = children[index];
5898
+ if (child !== void 0) stack.push(child);
5899
+ }
5900
+ }
5901
+ }
5902
+ }
5903
+ *#breadth(root) {
5904
+ const visited = /* @__PURE__ */ new Set();
5905
+ for (const seed of root === void 0 ? this.#roots() : [root]) {
5906
+ const queue = [seed];
5907
+ let cursor = 0;
5908
+ while (cursor < queue.length) {
5909
+ const node = queue[cursor];
5910
+ cursor += 1;
5911
+ if (node === void 0) continue;
5912
+ const identity = this.#identity(node);
5913
+ if (visited.has(identity)) continue;
5914
+ visited.add(identity);
5915
+ yield node;
5916
+ queue.push(...this.children(node));
5917
+ }
5918
+ }
5919
+ }
5920
+ #candidates(node) {
5921
+ const parent = this.parent(node);
5922
+ return parent === void 0 ? this.document(node)?.nodes.filter((candidate) => candidate.parent === void 0) ?? [] : this.children(parent);
5923
+ }
5924
+ #chain(node) {
5925
+ return [node, ...this.ancestors(node)];
5926
+ }
5927
+ #match(node, query) {
5928
+ return isBrowserNodeQuery(query) ? matchesBrowserNode(node, query) : query(node);
5929
+ }
5930
+ #identity(node) {
5931
+ return `${node.document}:${node.index}`;
5932
+ }
5933
+ };
5934
+ //#endregion
6049
5935
  //#region src/core/BrowserWorker.ts
6050
5936
  /**
6051
5937
  * Dedicated, shared, or service worker attached through a flattened target session.
@@ -6136,129 +6022,21 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
6136
6022
  #loadResolve;
6137
6023
  #loadReject;
6138
6024
  #responses;
6139
- #navigationResponseHandler = (response) => {
6140
- if (response.frame === this.id) this.#responses?.push(response);
6141
- };
6142
- #destroyHandler = (params) => {
6143
- if (!(0, _orkestrel_contract.isString)(params["targetId"]) || params["targetId"] !== this.#targetId) return;
6144
- this.#closed = true;
6145
- this.#release().catch(() => void 0);
6146
- };
6147
- #loadHandler = (params) => {
6148
- if (this.#loadEvents.includes("Page.navigatedWithinDocument")) {
6149
- const frame = params["frameId"];
6150
- if ((0, _orkestrel_contract.isString)(frame) && frame === this.id) {
6151
- this.#sameDocument = true;
6152
- this.#resolveLoad();
6153
- return;
6154
- }
6155
- }
6156
- if (this.#loadEvents.includes("Page.frameNavigated")) {
6157
- const frame = params["frame"];
6158
- if ((0, _orkestrel_contract.isRecord)(frame) && frame["id"] === this.id) this.#resolveLoad();
6159
- return;
6160
- }
6161
- if (this.#loadEvents.includes("Page.loadEventFired") || this.#loadEvents.includes("Page.domContentEventFired")) this.#resolveLoad();
6162
- };
6163
- #frameAttachedHandler = (params) => {
6164
- const frame = params["frameId"];
6165
- const parent = params["parentFrameId"];
6166
- if (!(0, _orkestrel_contract.isString)(frame)) return;
6167
- this.#emitter.emit("attach", new BrowserFrame(this.#client, (id) => this.#resolveFrameSession(id), frame, "about:blank", (0, _orkestrel_contract.isString)(parent) ? parent : void 0));
6168
- };
6169
- #frameNavigatedHandler = (params) => {
6170
- const frame = params["frame"];
6171
- if (!(0, _orkestrel_contract.isRecord)(frame) || !(0, _orkestrel_contract.isString)(frame["id"]) || !(0, _orkestrel_contract.isString)(frame["url"])) return;
6172
- if (frame["id"] === this.id) {
6173
- this.update(frame["url"]);
6174
- this.#emitter.emit("navigate", frame["url"]);
6175
- }
6176
- };
6177
- #frameDetachedHandler = (params) => {
6178
- const frame = params["frameId"];
6179
- if (!(0, _orkestrel_contract.isString)(frame)) return;
6180
- this.#frameSessions.delete(frame);
6181
- this.#emitter.emit("detach", frame);
6182
- };
6183
- #dialogHandler = (params) => {
6184
- const category = params["type"];
6185
- if (category !== "alert" && category !== "confirm" && category !== "prompt" && category !== "beforeunload" || !(0, _orkestrel_contract.isString)(params["message"])) return;
6186
- this.#emitter.emit("dialog", new BrowserDialog(this, category, params["message"], (0, _orkestrel_contract.isString)(params["defaultPrompt"]) ? params["defaultPrompt"] : ""));
6187
- };
6188
- #chooserHandler = (params) => {
6189
- const backend = params["backendNodeId"];
6190
- const mode = params["mode"];
6191
- if (!(0, _orkestrel_contract.isInteger)(backend)) return;
6192
- this.#emitter.emit("chooser", new BrowserFileChooser(this, backend, mode === "selectMultiple"));
6193
- };
6194
- #consoleHandler = (params) => {
6195
- const message = readBrowserConsoleMessage(params);
6196
- if (message !== void 0) this.#emitter.emit("console", message);
6197
- };
6198
- #errorHandler = (params) => {
6199
- const error = readBrowserPageError(params);
6200
- if (error !== void 0) this.#emitter.emit("error", error);
6201
- };
6202
- #crashHandler = () => this.#emitter.emit("crash");
6203
- #downloadHandler = (params) => {
6204
- const start = readBrowserDownloadStart(params);
6205
- if (start === void 0 || start.frame !== this.id && !this.#frameSessions.has(start.frame)) return;
6206
- const download = new BrowserDownload(this.#client, start.id, start.url, start.name, this.#contextId);
6207
- this.#downloads.set(start.id, download);
6208
- this.#emitter.emit("download", download);
6209
- };
6210
- #downloadProgressHandler = (params) => {
6211
- const decoded = readBrowserDownloadProgress(params);
6212
- if (decoded === void 0) return;
6213
- const [id, progress] = decoded;
6214
- const download = this.#downloads.get(id);
6215
- if (download === void 0) return;
6216
- download.update(progress);
6217
- if (progress.status !== "pending") this.#downloads.delete(id);
6218
- };
6219
- #attachedHandler = (params) => {
6220
- const target = params["targetInfo"];
6221
- const session = params["sessionId"];
6222
- if (!(0, _orkestrel_contract.isRecord)(target) || !(0, _orkestrel_contract.isString)(target["targetId"]) || !(0, _orkestrel_contract.isString)(session)) return;
6223
- const category = target["type"];
6224
- if (category === "worker" || category === "service_worker" || category === "shared_worker") {
6225
- this.#attachWorker(session, target["targetId"], (0, _orkestrel_contract.isString)(target["url"]) ? target["url"] : "", category);
6226
- return;
6227
- }
6228
- if (category === "page") {
6229
- this.#attachPopup(session, target["targetId"], (0, _orkestrel_contract.isString)(target["url"]) ? target["url"] : "about:blank");
6230
- return;
6231
- }
6232
- if (category !== "iframe") return;
6233
- const frame = target["targetId"];
6234
- const attempt = this.#enableFrameSession(session);
6235
- this.#frameSessions.set(frame, attempt);
6236
- this.#frameIds.set(session, frame);
6237
- attempt.catch(() => {
6238
- if (this.#frameSessions.get(frame) === attempt) this.#frameSessions.delete(frame);
6239
- if (this.#frameIds.get(session) === frame) this.#frameIds.delete(session);
6240
- this.#detachChild(session);
6241
- });
6242
- };
6243
- #detachedHandler = (params) => {
6244
- const session = params["sessionId"];
6245
- const target = params["targetId"];
6246
- if ((0, _orkestrel_contract.isString)(target)) {
6247
- const worker = this.#workers.get(target);
6248
- if (worker !== void 0) {
6249
- worker.detach();
6250
- this.#workers.delete(target);
6251
- }
6252
- const popup = this.#popups.get(target);
6253
- if (popup !== void 0) {
6254
- this.#popups.delete(target);
6255
- popup.destroy().catch(() => void 0);
6256
- }
6257
- }
6258
- const frame = (0, _orkestrel_contract.isString)(target) ? target : (0, _orkestrel_contract.isString)(session) ? this.#frameIds.get(session) : void 0;
6259
- if (frame !== void 0) this.#frameSessions.delete(frame);
6260
- if ((0, _orkestrel_contract.isString)(session)) this.#frameIds.delete(session);
6261
- };
6025
+ #navigationResponseHandler = this.#handleNavigationResponse.bind(this);
6026
+ #destroyHandler = this.#handleDestroy.bind(this);
6027
+ #loadHandler = this.#handleLoad.bind(this);
6028
+ #frameAttachedHandler = this.#handleFrameAttached.bind(this);
6029
+ #frameNavigatedHandler = this.#handleFrameNavigated.bind(this);
6030
+ #frameDetachedHandler = this.#handleFrameDetached.bind(this);
6031
+ #dialogHandler = this.#handleDialog.bind(this);
6032
+ #chooserHandler = this.#handleChooser.bind(this);
6033
+ #consoleHandler = this.#handleConsole.bind(this);
6034
+ #errorHandler = this.#handleError.bind(this);
6035
+ #crashHandler = this.#handleCrash.bind(this);
6036
+ #downloadHandler = this.#handleDownload.bind(this);
6037
+ #downloadProgressHandler = this.#handleDownloadProgress.bind(this);
6038
+ #attachedHandler = this.#handleAttached.bind(this);
6039
+ #detachedHandler = this.#handleDetached.bind(this);
6262
6040
  constructor(client, targetId, sessionId, writer, url, frameId, contextId, opener) {
6263
6041
  super(client, sessionId, frameId ?? targetId, url ?? "about:blank", void 0, void 0, false);
6264
6042
  this.#client = client;
@@ -6442,11 +6220,11 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
6442
6220
  async snapshot(options) {
6443
6221
  this.assert();
6444
6222
  const styles = options?.styles ?? [];
6445
- return decodeBrowserSnapshot(await this.request("DOMSnapshot.captureSnapshot", {
6223
+ return new BrowserSnapshot(decodeBrowserSnapshot(await this.request("DOMSnapshot.captureSnapshot", {
6446
6224
  computedStyles: [...styles],
6447
6225
  includePaintOrder: options?.paint ?? false,
6448
6226
  includeDOMRects: options?.rects ?? false
6449
- }), styles, options?.limit ?? 1e5);
6227
+ }), styles, options?.limit ?? 1e5));
6450
6228
  }
6451
6229
  async codegen(options) {
6452
6230
  this.assert();
@@ -6718,6 +6496,131 @@ var BrowserPage = class BrowserPage extends BrowserFrame {
6718
6496
  await this.request("Page.stopLoading", void 0, timeout);
6719
6497
  } catch {}
6720
6498
  }
6499
+ #handleNavigationResponse(response) {
6500
+ if (response.frame === this.id) this.#responses?.push(response);
6501
+ }
6502
+ #handleDestroy(params) {
6503
+ if (!(0, _orkestrel_contract.isString)(params["targetId"]) || params["targetId"] !== this.#targetId) return;
6504
+ this.#closed = true;
6505
+ this.#release().catch(() => void 0);
6506
+ }
6507
+ #handleLoad(params) {
6508
+ if (this.#loadEvents.includes("Page.navigatedWithinDocument")) {
6509
+ const frame = params["frameId"];
6510
+ if ((0, _orkestrel_contract.isString)(frame) && frame === this.id) {
6511
+ this.#sameDocument = true;
6512
+ this.#resolveLoad();
6513
+ return;
6514
+ }
6515
+ }
6516
+ if (this.#loadEvents.includes("Page.frameNavigated")) {
6517
+ const frame = params["frame"];
6518
+ if ((0, _orkestrel_contract.isRecord)(frame) && frame["id"] === this.id) this.#resolveLoad();
6519
+ return;
6520
+ }
6521
+ if (this.#loadEvents.includes("Page.loadEventFired") || this.#loadEvents.includes("Page.domContentEventFired")) this.#resolveLoad();
6522
+ }
6523
+ #handleFrameAttached(params) {
6524
+ const frame = params["frameId"];
6525
+ const parent = params["parentFrameId"];
6526
+ if (!(0, _orkestrel_contract.isString)(frame)) return;
6527
+ this.#emitter.emit("attach", new BrowserFrame(this.#client, (id) => this.#resolveFrameSession(id), frame, "about:blank", (0, _orkestrel_contract.isString)(parent) ? parent : void 0));
6528
+ }
6529
+ #handleFrameNavigated(params) {
6530
+ const frame = params["frame"];
6531
+ if (!(0, _orkestrel_contract.isRecord)(frame) || !(0, _orkestrel_contract.isString)(frame["id"]) || !(0, _orkestrel_contract.isString)(frame["url"])) return;
6532
+ if (frame["id"] === this.id) {
6533
+ this.update(frame["url"]);
6534
+ this.#emitter.emit("navigate", frame["url"]);
6535
+ }
6536
+ }
6537
+ #handleFrameDetached(params) {
6538
+ const frame = params["frameId"];
6539
+ if (!(0, _orkestrel_contract.isString)(frame)) return;
6540
+ this.#frameSessions.delete(frame);
6541
+ this.#emitter.emit("detach", frame);
6542
+ }
6543
+ #handleDialog(params) {
6544
+ const category = params["type"];
6545
+ if (category !== "alert" && category !== "confirm" && category !== "prompt" && category !== "beforeunload" || !(0, _orkestrel_contract.isString)(params["message"])) return;
6546
+ this.#emitter.emit("dialog", new BrowserDialog(this, category, params["message"], (0, _orkestrel_contract.isString)(params["defaultPrompt"]) ? params["defaultPrompt"] : ""));
6547
+ }
6548
+ #handleChooser(params) {
6549
+ const backend = params["backendNodeId"];
6550
+ const mode = params["mode"];
6551
+ if (!(0, _orkestrel_contract.isInteger)(backend)) return;
6552
+ this.#emitter.emit("chooser", new BrowserFileChooser(this, backend, mode === "selectMultiple"));
6553
+ }
6554
+ #handleConsole(params) {
6555
+ const message = readBrowserConsoleMessage(params);
6556
+ if (message !== void 0) this.#emitter.emit("console", message);
6557
+ }
6558
+ #handleError(params) {
6559
+ const error = readBrowserPageError(params);
6560
+ if (error !== void 0) this.#emitter.emit("error", error);
6561
+ }
6562
+ #handleCrash() {
6563
+ this.#emitter.emit("crash");
6564
+ }
6565
+ #handleDownload(params) {
6566
+ const start = readBrowserDownloadStart(params);
6567
+ if (start === void 0 || start.frame !== this.id && !this.#frameSessions.has(start.frame)) return;
6568
+ const download = new BrowserDownload(this.#client, start.id, start.url, start.name, this.#contextId);
6569
+ this.#downloads.set(start.id, download);
6570
+ this.#emitter.emit("download", download);
6571
+ }
6572
+ #handleDownloadProgress(params) {
6573
+ const decoded = readBrowserDownloadProgress(params);
6574
+ if (decoded === void 0) return;
6575
+ const [id, progress] = decoded;
6576
+ const download = this.#downloads.get(id);
6577
+ if (download === void 0) return;
6578
+ download.update(progress);
6579
+ if (progress.status !== "pending") this.#downloads.delete(id);
6580
+ }
6581
+ #handleAttached(params) {
6582
+ const target = params["targetInfo"];
6583
+ const session = params["sessionId"];
6584
+ if (!(0, _orkestrel_contract.isRecord)(target) || !(0, _orkestrel_contract.isString)(target["targetId"]) || !(0, _orkestrel_contract.isString)(session)) return;
6585
+ const category = target["type"];
6586
+ if (category === "worker" || category === "service_worker" || category === "shared_worker") {
6587
+ this.#attachWorker(session, target["targetId"], (0, _orkestrel_contract.isString)(target["url"]) ? target["url"] : "", category);
6588
+ return;
6589
+ }
6590
+ if (category === "page") {
6591
+ this.#attachPopup(session, target["targetId"], (0, _orkestrel_contract.isString)(target["url"]) ? target["url"] : "about:blank");
6592
+ return;
6593
+ }
6594
+ if (category !== "iframe") return;
6595
+ const frame = target["targetId"];
6596
+ const attempt = this.#enableFrameSession(session);
6597
+ this.#frameSessions.set(frame, attempt);
6598
+ this.#frameIds.set(session, frame);
6599
+ attempt.catch(() => {
6600
+ if (this.#frameSessions.get(frame) === attempt) this.#frameSessions.delete(frame);
6601
+ if (this.#frameIds.get(session) === frame) this.#frameIds.delete(session);
6602
+ this.#detachChild(session);
6603
+ });
6604
+ }
6605
+ #handleDetached(params) {
6606
+ const session = params["sessionId"];
6607
+ const target = params["targetId"];
6608
+ if ((0, _orkestrel_contract.isString)(target)) {
6609
+ const worker = this.#workers.get(target);
6610
+ if (worker !== void 0) {
6611
+ worker.detach();
6612
+ this.#workers.delete(target);
6613
+ }
6614
+ const popup = this.#popups.get(target);
6615
+ if (popup !== void 0) {
6616
+ this.#popups.delete(target);
6617
+ popup.destroy().catch(() => void 0);
6618
+ }
6619
+ }
6620
+ const frame = (0, _orkestrel_contract.isString)(target) ? target : (0, _orkestrel_contract.isString)(session) ? this.#frameIds.get(session) : void 0;
6621
+ if (frame !== void 0) this.#frameSessions.delete(frame);
6622
+ if ((0, _orkestrel_contract.isString)(session)) this.#frameIds.delete(session);
6623
+ }
6721
6624
  #waitForLoadEvent(condition, timeout) {
6722
6625
  const eventName = condition === "commit" ? "Page.frameNavigated" : condition === "domcontentloaded" ? "Page.domContentEventFired" : "Page.loadEventFired";
6723
6626
  const deferred = Promise.withResolvers();
@@ -6819,8 +6722,8 @@ var BrowserStorageManager = class {
6819
6722
  expires: cookie.expires,
6820
6723
  http: cookie.http,
6821
6724
  secure: cookie.secure,
6822
- site: cookie.site,
6823
- partition: cookie.partition
6725
+ ...cookie.site !== void 0 ? { site: cookie.site } : {},
6726
+ ...cookie.partition !== void 0 ? { partition: cookie.partition } : {}
6824
6727
  })),
6825
6728
  origins: storage
6826
6729
  };
@@ -6968,7 +6871,7 @@ var BrowserContext = class {
6968
6871
  try {
6969
6872
  const viewport = options?.viewport ?? this.#viewport;
6970
6873
  page = await this.#attach(targetId, options?.url ?? "about:blank", viewport);
6971
- if (options?.url !== void 0 && options.url !== "about:blank") await page.navigate(options.url, { timeout: options.timeout });
6874
+ if (options?.url !== void 0 && options.url !== "about:blank") await page.navigate(options.url, { ...options.timeout !== void 0 ? { timeout: options.timeout } : {} });
6972
6875
  if (this.#closed) throw new BrowserError("Browser context closed during page creation");
6973
6876
  this.#pages.set(targetId, page);
6974
6877
  this.#emitter.emit("page", page);
@@ -7204,6 +7107,23 @@ var BrowserContext = class {
7204
7107
  function createCDPClient(options) {
7205
7108
  return new CDPClient(options);
7206
7109
  }
7110
+ /**
7111
+ * Create a navigable browser snapshot from decoded serializable data.
7112
+ *
7113
+ * @param input - Captured documents and computed-style names
7114
+ * @returns A {@link BrowserSnapshotInterface}
7115
+ *
7116
+ * @example
7117
+ * ```ts
7118
+ * import { createBrowserSnapshot, decodeBrowserSnapshot } from '@src/core'
7119
+ *
7120
+ * const snapshot = createBrowserSnapshot(decodeBrowserSnapshot(captured, ['display']))
7121
+ * snapshot.find({ name: 'main' })
7122
+ * ```
7123
+ */
7124
+ function createBrowserSnapshot(input) {
7125
+ return new BrowserSnapshot(input);
7126
+ }
7207
7127
  //#endregion
7208
7128
  exports.BASE64_CHARS = BASE64_CHARS;
7209
7129
  exports.BASE64_LOOKUP = BASE64_LOOKUP;
@@ -7253,6 +7173,7 @@ exports.BrowserRoute = BrowserRoute;
7253
7173
  exports.BrowserScriptManager = BrowserScriptManager;
7254
7174
  exports.BrowserSelectorError = BrowserSelectorError;
7255
7175
  exports.BrowserSelectorManager = BrowserSelectorManager;
7176
+ exports.BrowserSnapshot = BrowserSnapshot;
7256
7177
  exports.BrowserStorageManager = BrowserStorageManager;
7257
7178
  exports.BrowserTouch = BrowserTouch;
7258
7179
  exports.BrowserTracing = BrowserTracing;
@@ -7262,16 +7183,12 @@ exports.CDPClient = CDPClient;
7262
7183
  exports.CDPConnectionError = CDPConnectionError;
7263
7184
  exports.CDPError = CDPError;
7264
7185
  exports.CDPTimeoutError = CDPTimeoutError;
7265
- exports.ancestorsOfBrowserNode = ancestorsOfBrowserNode;
7266
7186
  exports.attributeOfBrowserNode = attributeOfBrowserNode;
7267
7187
  exports.browserHARHeadersToRecord = browserHARHeadersToRecord;
7268
7188
  exports.browserHeadersToProtocol = browserHeadersToProtocol;
7269
7189
  exports.browserPDFToParams = browserPDFToParams;
7270
7190
  exports.browserScreenshotToParams = browserScreenshotToParams;
7271
7191
  exports.bytesToText = bytesToText;
7272
- exports.childrenOfBrowserNode = childrenOfBrowserNode;
7273
- exports.closestBrowserNode = closestBrowserNode;
7274
- exports.commonAncestorOfBrowserNodes = commonAncestorOfBrowserNodes;
7275
7192
  exports.compileActionabilityFunction = compileActionabilityFunction;
7276
7193
  exports.compileAttachedLocatorWaitExpression = compileAttachedLocatorWaitExpression;
7277
7194
  exports.compileAttachedWaitExpression = compileAttachedWaitExpression;
@@ -7298,10 +7215,10 @@ exports.compileVisibleLocatorWaitExpression = compileVisibleLocatorWaitExpressio
7298
7215
  exports.compileVisibleWaitExpression = compileVisibleWaitExpression;
7299
7216
  exports.computeBrowserButtons = computeBrowserButtons;
7300
7217
  exports.computeBrowserModifiers = computeBrowserModifiers;
7301
- exports.computeBrowserNodeDistance = computeBrowserNodeDistance;
7302
7218
  exports.concatBytes = concatBytes;
7303
7219
  exports.cookieToProtocol = cookieToProtocol;
7304
7220
  exports.createBrowserHAREntry = createBrowserHAREntry;
7221
+ exports.createBrowserSnapshot = createBrowserSnapshot;
7305
7222
  exports.createCDPClient = createCDPClient;
7306
7223
  exports.decodeBase64 = decodeBase64;
7307
7224
  exports.decodeBrowserAttributes = decodeBrowserAttributes;
@@ -7309,16 +7226,10 @@ exports.decodeBrowserSnapshot = decodeBrowserSnapshot;
7309
7226
  exports.decodeRareBooleanData = decodeRareBooleanData;
7310
7227
  exports.decodeRareIntegerData = decodeRareIntegerData;
7311
7228
  exports.decodeRareStringData = decodeRareStringData;
7312
- exports.descendantsOfBrowserNode = descendantsOfBrowserNode;
7313
- exports.documentOfBrowserNode = documentOfBrowserNode;
7314
7229
  exports.encodeBase64 = encodeBase64;
7315
- exports.findBrowserDescendant = findBrowserDescendant;
7316
- exports.findBrowserNode = findBrowserNode;
7317
- exports.findBrowserNodes = findBrowserNodes;
7318
- exports.followingSiblingsOfBrowserNode = followingSiblingsOfBrowserNode;
7319
7230
  exports.guardEvaluateExpression = guardEvaluateExpression;
7320
7231
  exports.isBrowserError = isBrowserError;
7321
- exports.isBrowserNodeDescendant = isBrowserNodeDescendant;
7232
+ exports.isBrowserNodeQuery = isBrowserNodeQuery;
7322
7233
  exports.isBrowserNodeVisible = isBrowserNodeVisible;
7323
7234
  exports.isBrowserResultLimitError = isBrowserResultLimitError;
7324
7235
  exports.isBrowserSelectorError = isBrowserSelectorError;
@@ -7331,12 +7242,9 @@ exports.matchesBrowserNode = matchesBrowserNode;
7331
7242
  exports.matchesBrowserRoute = matchesBrowserRoute;
7332
7243
  exports.matchesBrowserURL = matchesBrowserURL;
7333
7244
  exports.mediaToFeatures = mediaToFeatures;
7334
- exports.nodeToPath = nodeToPath;
7335
7245
  exports.normalizeCodegenActions = normalizeCodegenActions;
7336
- exports.parentOfBrowserNode = parentOfBrowserNode;
7337
7246
  exports.parseBrowserChord = parseBrowserChord;
7338
7247
  exports.parseCodegenActionPayload = parseCodegenActionPayload;
7339
- exports.precedingSiblingsOfBrowserNode = precedingSiblingsOfBrowserNode;
7340
7248
  exports.readBrowserAXString = readBrowserAXString;
7341
7249
  exports.readBrowserAXValue = readBrowserAXValue;
7342
7250
  exports.readBrowserAccessibility = readBrowserAccessibility;
@@ -7377,7 +7285,6 @@ exports.readEvaluationResult = readEvaluationResult;
7377
7285
  exports.readNumberArray = readNumberArray;
7378
7286
  exports.readSnapshotString = readSnapshotString;
7379
7287
  exports.requireBrowserString = requireBrowserString;
7380
- exports.siblingsOfBrowserNode = siblingsOfBrowserNode;
7381
7288
  exports.textToBytes = textToBytes;
7382
7289
  exports.validateBrowserAccessibilityOptions = validateBrowserAccessibilityOptions;
7383
7290
  exports.validateBrowserActionOptions = validateBrowserActionOptions;
@@ -7388,9 +7295,5 @@ exports.validateBrowserPoint = validateBrowserPoint;
7388
7295
  exports.validateBrowserRange = validateBrowserRange;
7389
7296
  exports.validateBrowserTimeout = validateBrowserTimeout;
7390
7297
  exports.validateBrowserViewport = validateBrowserViewport;
7391
- exports.walkBrowserNode = walkBrowserNode;
7392
- exports.walkBrowserNodeBreadthFirst = walkBrowserNodeBreadthFirst;
7393
- exports.walkBrowserSnapshot = walkBrowserSnapshot;
7394
- exports.walkBrowserSnapshotBreadthFirst = walkBrowserSnapshotBreadthFirst;
7395
7298
 
7396
7299
  //# sourceMappingURL=index.cjs.map