browserscale-ts 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
  // implementation detail and is NOT exported from the package entrypoints.
3
3
  import { create } from "@bufbuild/protobuf";
4
4
  import { HeaderModificationSchema, CookiePartitionKeySchema, CookieParamSchema, HeaderSchema, StorageItemSchema, StorageOriginEntrySchema, } from "../gen/wrc_pb.js";
5
+ import { ClickError, DragError, FillError, MoveError, ScrollError, SelectOptionError, WaitError, } from "../errors.js";
5
6
  import { pickFrame } from "../locator.js";
6
7
  export function elementFields(target, optsInFrame) {
7
8
  const out = {};
@@ -28,22 +29,95 @@ export function rectFromProto(r) {
28
29
  return { x: 0, y: 0, width: 0, height: 0 };
29
30
  return { x: r.x, y: r.y, width: r.width, height: r.height };
30
31
  }
31
- // ──────────────────────────────────────────────────────────────────────
32
- // Element / Drag / Wait results
33
- // ──────────────────────────────────────────────────────────────────────
34
- export function elementResultFromProto(r) {
32
+ function elementResult(r, isVisible, bounds, rootX, rootY) {
35
33
  return {
36
34
  success: r.success,
37
35
  frameId: r.frameId,
38
36
  backendNodeId: r.backendNodeId,
39
- isVisible: r.isVisible,
40
- bounds: rectFromProto(r.bounds),
41
- rootX: r.rootX,
42
- rootY: r.rootY,
37
+ isVisible,
38
+ bounds,
39
+ rootX,
40
+ rootY,
43
41
  };
44
42
  }
45
- export function dragResultFromProto(r) {
43
+ function occluderFromProto(o) {
44
+ if (!o)
45
+ return undefined;
46
46
  return {
47
+ backendNodeId: o.backendNodeId,
48
+ frameId: o.frameId,
49
+ tagName: o.tagName,
50
+ id: o.id ?? "",
51
+ className: o.className ?? "",
52
+ text: o.text ?? "",
53
+ bounds: rectFromProto(o.bounds),
54
+ pointerEvents: o.pointerEvents ?? "",
55
+ visibility: o.visibility ?? "",
56
+ opacity: o.opacity ?? 0,
57
+ zIndex: o.zIndex ?? "",
58
+ hittableWhileInvisible: o.hittableWhileInvisible ?? false,
59
+ };
60
+ }
61
+ // Nested click-core detail (no standalone result) surfaced inside a FillError
62
+ // or DragError as the underlying click failure that blocked the action.
63
+ function clickDetailFromProto(e) {
64
+ if (!e)
65
+ return undefined;
66
+ return new ClickError({
67
+ code: e.code,
68
+ message: e.message,
69
+ occluder: occluderFromProto(e.occluder),
70
+ evadeAttempted: e.evadeAttempted ?? false,
71
+ });
72
+ }
73
+ /** Maps a ClickResult to ElementResult; throws {@link ClickError} when the click did not land. */
74
+ export function unwrapClick(r) {
75
+ const res = elementResult(r, r.isVisible, rectFromProto(r.bounds), r.rootX, r.rootY);
76
+ if (!r.success && r.error) {
77
+ throw new ClickError({
78
+ code: r.error.code,
79
+ message: r.error.message,
80
+ occluder: occluderFromProto(r.error.occluder),
81
+ evadeAttempted: r.error.evadeAttempted ?? false,
82
+ result: res,
83
+ });
84
+ }
85
+ return res;
86
+ }
87
+ /** Maps a FillResult to ElementResult; throws {@link FillError} when the fill failed. */
88
+ export function unwrapFill(r) {
89
+ // Fill omits is_visible/bounds from its contract; they stay zero.
90
+ const res = elementResult(r, false, rectFromProto(undefined), r.rootX, r.rootY);
91
+ if (!r.success && r.error) {
92
+ throw new FillError({
93
+ code: r.error.code,
94
+ message: r.error.message,
95
+ clickError: clickDetailFromProto(r.error.clickError),
96
+ result: res,
97
+ });
98
+ }
99
+ return res;
100
+ }
101
+ /** Maps a MoveResult to ElementResult; throws {@link MoveError} when the target was not found. */
102
+ export function unwrapMove(r) {
103
+ const res = elementResult(r, r.isVisible, rectFromProto(r.bounds), r.rootX, r.rootY);
104
+ if (!r.success && r.error) {
105
+ throw new MoveError({ code: r.error.code, message: r.error.message, result: res });
106
+ }
107
+ return res;
108
+ }
109
+ /** Maps a ScrollResult to ElementResult; throws {@link ScrollError} when the target could not be scrolled. */
110
+ export function unwrapScroll(r) {
111
+ // scrollTo returns no root_x/root_y; they stay zero.
112
+ const res = elementResult(r, r.isVisible, rectFromProto(r.bounds), 0, 0);
113
+ if (!r.success && r.error) {
114
+ throw new ScrollError({ code: r.error.code, message: r.error.message, result: res });
115
+ }
116
+ return res;
117
+ }
118
+ /** Maps a DragResult; throws {@link DragError} when the source pickup failed. */
119
+ export function unwrapDrag(r) {
120
+ const res = {
47
121
  success: r.success,
48
122
  frameId: r.frameId,
49
123
  backendNodeId: r.backendNodeId,
@@ -52,15 +126,57 @@ export function dragResultFromProto(r) {
52
126
  endX: r.endX,
53
127
  endY: r.endY,
54
128
  };
129
+ if (!r.success && r.error) {
130
+ throw new DragError({
131
+ code: r.error.code,
132
+ message: r.error.message,
133
+ clickError: clickDetailFromProto(r.error.clickError),
134
+ result: res,
135
+ });
136
+ }
137
+ return res;
55
138
  }
56
- export function waitResultFromProto(r) {
139
+ /** Maps a SelectOptionResult; throws {@link SelectOptionError} when no option was selected. */
140
+ export function unwrapSelect(r) {
141
+ const res = {
142
+ selectedIndex: r.selectedIndex,
143
+ selectedValue: r.selectedValue,
144
+ selectedText: r.selectedText,
145
+ };
146
+ if (!r.success && r.error) {
147
+ throw new SelectOptionError({ code: r.error.code, message: r.error.message, result: res });
148
+ }
149
+ return res;
150
+ }
151
+ function waitConditionStatusFromProto(c) {
57
152
  return {
153
+ index: c.index,
154
+ state: c.state,
155
+ backendNodeId: c.backendNodeId,
156
+ frameId: c.frameId,
157
+ isVisible: c.isVisible,
158
+ bounds: c.bounds ? rectFromProto(c.bounds) : undefined,
159
+ occluder: occluderFromProto(c.occluder),
160
+ };
161
+ }
162
+ /** Maps a WaitResult; throws {@link WaitError} when no condition matched before the deadline. */
163
+ export function unwrapWait(r) {
164
+ const res = {
58
165
  index: r.index,
59
166
  frameId: r.frameId,
60
167
  backendNodeId: r.backendNodeId,
61
168
  isVisible: r.isVisible,
62
169
  bounds: rectFromProto(r.bounds),
63
170
  };
171
+ if (r.error) {
172
+ throw new WaitError({
173
+ code: r.error.code,
174
+ message: r.error.message,
175
+ conditions: r.error.conditions.map(waitConditionStatusFromProto),
176
+ result: res,
177
+ });
178
+ }
179
+ return res;
64
180
  }
65
181
  // ──────────────────────────────────────────────────────────────────────
66
182
  // FrameInfo / PageInfo
package/dist/types.d.ts CHANGED
@@ -64,6 +64,67 @@ export interface WaitResult {
64
64
  isVisible: boolean;
65
65
  bounds: Rect;
66
66
  }
67
+ /**
68
+ * OccluderInfo describes the element that intercepted a click — the element
69
+ * sitting on top of the target at the intended click point. Coordinates are in
70
+ * root-viewport CSS pixels. Populated on {@link ClickError} for occlusion
71
+ * failures so the caller can locate and clear the blocker (e.g. find its close
72
+ * button).
73
+ */
74
+ export interface OccluderInfo {
75
+ backendNodeId: number;
76
+ frameId: string;
77
+ tagName: string;
78
+ id: string;
79
+ className: string;
80
+ text: string;
81
+ bounds: Rect;
82
+ /**
83
+ * Computed pointer-events keyword (e.g. "auto", "none", "all"). Lets you tell
84
+ * an invisible pass-through layer from one that genuinely swallows the click.
85
+ */
86
+ pointerEvents: string;
87
+ /** Computed visibility keyword ("visible", "hidden", "collapse"). */
88
+ visibility: string;
89
+ /**
90
+ * Computed opacity (0..1). 0 means visually invisible but it may still
91
+ * intercept clicks depending on pointerEvents.
92
+ */
93
+ opacity: number;
94
+ /** Computed effective z-index as a string ("0" when auto / not stacked). */
95
+ zIndex: string;
96
+ /**
97
+ * True when the blocker intercepts clicks even while invisible (computed
98
+ * pointer-events in {all, painted, fill, stroke}): a real click is swallowed
99
+ * even at visibility:hidden / opacity:0. When false and the element is
100
+ * invisible, a real click would fall through.
101
+ */
102
+ hittableWhileInvisible: boolean;
103
+ }
104
+ /**
105
+ * WaitConditionStatus is the per-condition diagnostic carried by
106
+ * {@link WaitError} when a {@link CloudBrowser.wait} times out: one entry per
107
+ * condition (in the order they were passed) explaining why it never matched.
108
+ */
109
+ export interface WaitConditionStatus {
110
+ /** Index into the condition list this entry describes. */
111
+ index: number;
112
+ /**
113
+ * Last observed state: `"not_found"`, `"found_hidden"`, `"found_occluded"`
114
+ * (only when the condition required visibility), or `"pending_steady"`.
115
+ */
116
+ state: string;
117
+ /** backendNodeId last seen for this condition (0 if never found). */
118
+ backendNodeId: number;
119
+ /** frameId where it was last seen (empty if never found). */
120
+ frameId: string;
121
+ /** Whether it was CSS-visible at the last observation. */
122
+ isVisible: boolean;
123
+ /** Last known rect in root-viewport coordinates (undefined if never found). */
124
+ bounds?: Rect;
125
+ /** The intercepting element, present iff state === `"found_occluded"`. */
126
+ occluder?: OccluderInfo;
127
+ }
67
128
  /** NavigateResult reports where a {@link CloudBrowser.navigate} call ended up after redirects. */
68
129
  export interface NavigateResult {
69
130
  frameId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browserscale-ts",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Official SDK for browserscale — undetected stealth browsers in the cloud. Rent real Chromium sessions with unique fingerprints, built-in proxies, captcha solving, human-like input and live video — scale web scraping and automation without running a single browser yourself.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",