@hira-core/sdk 1.0.7 → 1.0.9

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 CHANGED
@@ -188,17 +188,22 @@ const utils = new BrowserUtils(context);
188
188
 
189
189
  #### Navigation & Interaction
190
190
 
191
- | Method | Description |
192
- | ------------------------------------------ | ----------------------------- |
193
- | `utils.goto(url)` | Navigate to URL |
194
- | `utils.click(selector)` | Wait + scroll + click |
195
- | `utils.type(selector, text)` | Wait + clear + type |
196
- | `utils.getText(selector)` | Get text content |
197
- | `utils.exists(selector, timeout?)` | Check element exists |
198
- | `utils.waitForElement(selector, timeout?)` | Wait for element |
199
- | `utils.waitForNavigation()` | Wait for page navigation |
200
- | `utils.screenshot(path?)` | Take screenshot |
201
- | `utils.sleep(ms)` | Delay (respects abort signal) |
191
+ | Method | Description |
192
+ | ------------------------------------------ | -------------------------------------- |
193
+ | `utils.goto(url)` | Navigate to URL |
194
+ | `utils.click(selector)` | Wait + scroll + click |
195
+ | `utils.click({ x, y })` | Click at specific coordinates |
196
+ | `utils.type(selector, text)` | Wait + clear + type |
197
+ | `utils.select(selector, value)` | Select dropdown option (native `<select>`) |
198
+ | `utils.getText(selector)` | Get text content |
199
+ | `utils.getPosition(selector)` | Get element center coordinates |
200
+ | `utils.exists(selector, timeout?)` | Check element exists |
201
+ | `utils.waitForElement(selector, timeout?)` | Wait for element |
202
+ | `utils.waitForNavigation()` | Wait for page navigation |
203
+ | `utils.screenshot(path?)` | Take screenshot |
204
+ | `utils.sleep(ms)` | Delay (respects abort signal) |
205
+ | `utils.scroll({ deltaY })` | Smooth 60fps scroll (ease-in-out) |
206
+ | `utils.scroll({ deltaY, container })` | Smooth scroll inside overflow container|
202
207
 
203
208
  #### Tab Management
204
209
 
@@ -344,6 +349,22 @@ npx @hira-core/cli build
344
349
 
345
350
  ---
346
351
 
352
+ ## Virtual Cursor
353
+
354
+ The SDK includes a **virtual cursor** — a visible SVG cursor overlay that shows mouse movement, clicks, and scrolling in real-time. This is enabled by default and can be toggled via `IExecutionConfig.virtualCursor`:
355
+
356
+ ```typescript
357
+ // In flow runner params
358
+ execution: {
359
+ virtualCursor: true, // default — show cursor overlay
360
+ // virtualCursor: false, // disable cursor overlay
361
+ }
362
+ ```
363
+
364
+ When enabled, `BrowserUtils.click()`, `scroll()`, and other interaction methods will show realistic Bézier curve mouse movement with human-like timing.
365
+
366
+ ---
367
+
347
368
  ## License
348
369
 
349
370
  ISC
package/dist/index.d.ts CHANGED
@@ -237,6 +237,12 @@ interface IExecutionConfig {
237
237
  * Dev mode: dùng constructor `super(AntidetectProvider.GPM, ...)` trong flow class.
238
238
  */
239
239
  antidetectProvider?: 'gpm' | 'hidemium' | 'genlogin' | 'adspower';
240
+ /**
241
+ * Enable virtual cursor — SVG arrow with Bézier curve mouse movement.
242
+ * When true, click() and type() will move cursor naturally before interacting.
243
+ * Default: true
244
+ */
245
+ virtualCursor?: boolean;
240
246
  }
241
247
  interface IBrowserWindowConfig {
242
248
  width: number;
@@ -314,6 +320,8 @@ interface IScriptContext<TConfig extends IFlowConfig = IFlowConfig> {
314
320
  page: Page;
315
321
  profile: IAntidetectProfile;
316
322
  index: number;
323
+ /** Execution config — includes virtualCursor flag etc. */
324
+ execution: IExecutionConfig;
317
325
  globalInput: InferGlobalInput<TConfig>;
318
326
  profileInput: InferProfileInput<TConfig>;
319
327
  /**
@@ -616,13 +624,26 @@ declare class BrowserUtils<TConfig extends IFlowConfig = IFlowConfig> {
616
624
  private activePage;
617
625
  /** Currently active iframe — null means main frame. Changed via activeIframe() */
618
626
  private activeFrame;
627
+ /** Virtual cursor — Bézier movement + SVG visualization */
628
+ private readonly hiraCursor;
619
629
  constructor(context: IScriptContext<TConfig>);
620
630
  /**
621
- * CDP trick — Chromium nghĩ tab luôn focused, không bị throttle
622
- * khi user chuyển sang tab khác hoặc click vào trình duyệt.
631
+ * CDP trick — Chromium thinks tab is always focused, not throttled
632
+ * when user switches to another tab or clicks elsewhere.
623
633
  */
624
634
  private enableFocusEmulation;
635
+ /**
636
+ * Apply stealth scripts to remove automation fingerprints.
637
+ * Uses evaluateOnNewDocument — runs BEFORE any page scripts on every navigation.
638
+ * Persists across navigations on the same page instance.
639
+ */
640
+ private applyStealthScripts;
625
641
  private checkAbort;
642
+ /**
643
+ * Scroll element vào viewport mượt mà — segments + delay.
644
+ * Chỉ scroll nếu element nằm ngoài viewport.
645
+ */
646
+ private smoothScrollToElement;
626
647
  /**
627
648
  * Pause execution for the given duration.
628
649
  *
@@ -650,10 +671,11 @@ declare class BrowserUtils<TConfig extends IFlowConfig = IFlowConfig> {
650
671
  */
651
672
  private resolveElement;
652
673
  /**
653
- * Click on an element. Scrolls the element into view before clicking.
674
+ * Click on an element or at specific coordinates.
675
+ * Scrolls the element into view before clicking.
654
676
  * Throws if the element is not found.
655
677
  *
656
- * @param target - CSS selector, XPath, or an existing ElementHandle
678
+ * @param target - CSS selector, XPath, ElementHandle, or `{ x, y }` coordinates
657
679
  * @param options.delay - Delay in ms before clicking (default: 1000)
658
680
  * @param options.waitTimeout - Max wait time for element to appear (default: 2000)
659
681
  * @param options.frame - Optional Frame to search within
@@ -662,9 +684,58 @@ declare class BrowserUtils<TConfig extends IFlowConfig = IFlowConfig> {
662
684
  * @example
663
685
  * await utils.click("#submit-btn");
664
686
  * await utils.click("#btn", { delay: 0 }); // click immediately
665
- * await utils.click("#btn", { waitTimeout: 8000 }); // wait longer
687
+ * await utils.click({ x: 500, y: 300 }); // click at coordinates
688
+ * const pos = await utils.getPosition("#btn", { randomXY: true });
689
+ * await utils.click(pos!); // click random point inside element
666
690
  */
667
- click(target: string | ElementHandle, options?: {
691
+ click(target: string | ElementHandle | {
692
+ x: number;
693
+ y: number;
694
+ }, options?: {
695
+ delay?: number;
696
+ waitTimeout?: number;
697
+ frame?: Frame;
698
+ }): Promise<boolean>;
699
+ /**
700
+ * Get the position (x, y) of an element.
701
+ * Returns center point by default, or a random point within bounds with `randomXY`.
702
+ *
703
+ * @param selector - CSS selector or XPath
704
+ * @param options.randomXY - If true, returns random point within element (10% margin from edges)
705
+ * @param options.waitTimeout - Max wait time for element (default: 2000ms)
706
+ * @param options.frame - Optional Frame to search within
707
+ * @returns `{ x, y }` or null if element not found / no bounding box
708
+ *
709
+ * @example
710
+ * const center = await utils.getPosition("#btn");
711
+ * const rand = await utils.getPosition("#btn", { randomXY: true });
712
+ * if (rand) await utils.click(rand);
713
+ */
714
+ getPosition(selector: string, options?: {
715
+ randomXY?: boolean;
716
+ waitTimeout?: number;
717
+ frame?: Frame;
718
+ }): Promise<{
719
+ x: number;
720
+ y: number;
721
+ } | null>;
722
+ /**
723
+ * Select a value from a `<select>` dropdown.
724
+ * Human-like flow: cursor moves to select → click to open → page.select() → close.
725
+ *
726
+ * NOTE: Native `<option>` elements CAN NOT be clicked via Puppeteer
727
+ * ("Node is either not clickable or not an Element").
728
+ * `page.select()` is the ONLY reliable method.
729
+ *
730
+ * @param selector - CSS selector or XPath of the `<select>` element
731
+ * @param value - The `value` attribute of the option to select
732
+ * @param options.delay - Delay before clicking (default: 500ms)
733
+ * @param options.waitTimeout - Max wait for element (default: 2000ms)
734
+ *
735
+ * @example
736
+ * await utils.select("#country", "vn");
737
+ */
738
+ select(selector: string, value: string, options?: {
668
739
  delay?: number;
669
740
  waitTimeout?: number;
670
741
  frame?: Frame;
@@ -741,6 +812,78 @@ declare class BrowserUtils<TConfig extends IFlowConfig = IFlowConfig> {
741
812
  goto(url: string, options?: {
742
813
  waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2";
743
814
  }): Promise<boolean>;
815
+ /**
816
+ * Scroll the page or a container by a specific amount.
817
+ * Uses CDP mouseWheel events (human-like, not JS scrollBy).
818
+ *
819
+ * @param direction - "up" or "down"
820
+ * @param amount - Pixels to scroll
821
+ * @param options.container - CSS selector of scroll container
822
+ * @param options.speed - Scroll speed 1-100 (default: 50)
823
+ *
824
+ * @example
825
+ * await utils.scroll("down", 500);
826
+ * await utils.scroll("up", 200, { container: "#chat-messages" });
827
+ */
828
+ scroll(direction: "up" | "down", amount: number, options?: {
829
+ container?: string;
830
+ speed?: number;
831
+ }): Promise<void>;
832
+ /**
833
+ * Scroll to the top or bottom of the page.
834
+ * Scrolls in segments with pauses — giống người lướt dần đến đích.
835
+ *
836
+ * @param position - "top" or "bottom"
837
+ * @param options.speed - Scroll speed 1-100 (default: 50)
838
+ *
839
+ * @example
840
+ * await utils.scrollTo("bottom");
841
+ * await utils.scrollTo("top");
842
+ */
843
+ scrollTo(position: "top" | "bottom", options?: {
844
+ speed?: number;
845
+ }): Promise<void>;
846
+ /**
847
+ * Scroll an element into the viewport.
848
+ * Scrolls in segments with pauses — không nhảy tới đích.
849
+ *
850
+ * @param selector - CSS selector or XPath of the element
851
+ * @param options.speed - Scroll speed 1-100 (default: 50)
852
+ * @returns true if element found and scrolled
853
+ *
854
+ * @example
855
+ * await utils.scrollToElement("#footer");
856
+ * await utils.scrollToElement("//button[text()='Load more']");
857
+ */
858
+ scrollToElement(selector: string, options?: {
859
+ speed?: number;
860
+ }): Promise<boolean>;
861
+ /**
862
+ * Simulate natural browsing scroll — cuộn xuống xen kẽ lướt ngược, delay random.
863
+ * Giống người đang đọc/lướt web tự nhiên.
864
+ *
865
+ * @param options.duration - Thời gian scroll tính bằng giây (default: 5)
866
+ * @param options.direction - Main direction (default: "down")
867
+ * @param options.backChance - Chance to scroll back 0-1 (default: 0.15)
868
+ * @param options.backAmount - [min, max] px to scroll back (default: [50, 150])
869
+ * @param options.stepSize - [min, max] px per step (default: [200, 400])
870
+ * @param options.stepDelay - [min, max] ms delay between steps (default: [300, 800])
871
+ * @param options.container - CSS selector of scroll container
872
+ *
873
+ * @example
874
+ * await utils.randomScroll(); // 5s lướt xuống
875
+ * await utils.randomScroll({ duration: 10 }); // 10s
876
+ * await utils.randomScroll({ backChance: 0.3, container: "#feed" });
877
+ */
878
+ randomScroll(options?: {
879
+ duration?: number;
880
+ direction?: "down" | "up";
881
+ backChance?: number;
882
+ backAmount?: [number, number];
883
+ stepSize?: [number, number];
884
+ stepDelay?: [number, number];
885
+ container?: string;
886
+ }): Promise<void>;
744
887
  /**
745
888
  * Wait for the current page to complete a navigation (e.g. after a form submit).
746
889
  * Throws on timeout.