@d-zero/puppeteer-scroll 3.1.18 → 4.0.1

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
@@ -1,23 +1,25 @@
1
1
  # `@d-zero/puppeteer-scroll`
2
2
 
3
- Puppeteerでスクロールするための関数を提供します。
3
+ `IntersectionObserver` や `loading="lazy"` を完了させるため、ページを上から下までスクロールするユーティリティ。スクロールジャック検出付き。
4
4
 
5
- `IntersectionObserver`や`loading="lazy"`などの機能を使っているサイトに対して表示や読み込みを完了させるために、スクロールを行います。
6
-
7
- ## インストール
5
+ ## Installation
8
6
 
9
7
  ```sh
10
- yarn install @d-zero/puppeteer-scroll
8
+ yarn add @d-zero/puppeteer-scroll
11
9
  ```
12
10
 
13
- ## 使い方
11
+ ## Usage
14
12
 
15
13
  ```ts
16
14
  import { scrollAllOver } from '@d-zero/puppeteer-scroll';
17
15
 
18
- const browser = await puppeteer.launch();
19
- const page = await browser.newPage();
20
- await page.goto('https://example.com');
21
-
22
16
  await scrollAllOver(page);
23
17
  ```
18
+
19
+ デフォルトは人間らしい揺らぎを付けたランダム挙動。テスト・レコーディング・VRT など**決定論的な挙動が必要な場合は `interval` と `distance` を明示指定**する:
20
+
21
+ ```ts
22
+ await scrollAllOver(page, { interval: 300, distance: 800 });
23
+ ```
24
+
25
+ オプション・デフォルト値・スクロールジャック検出ロジック・`distance ≤ 0` の丸めについては `src/scroll-all-over.ts` の JSDoc を参照。
@@ -0,0 +1,11 @@
1
+ import type { DelayOptions } from '@d-zero/shared/delay';
2
+ /**
3
+ * Synchronously resolves a `number | DelayOptions` to a concrete number.
4
+ *
5
+ * Why: `delay()` resolves the same shape but actually waits. For values
6
+ * that need to be passed into a browser `evaluate` call (e.g. scroll step
7
+ * distance), we need the sampled number without any time elapsing.
8
+ * @param value - The value to resolve.
9
+ * @returns A concrete number.
10
+ */
11
+ export declare function resolveValue(value: number | DelayOptions): number;
@@ -0,0 +1,20 @@
1
+ import { sampleDistribution } from '@d-zero/shared/sample-distribution';
2
+ /**
3
+ * Synchronously resolves a `number | DelayOptions` to a concrete number.
4
+ *
5
+ * Why: `delay()` resolves the same shape but actually waits. For values
6
+ * that need to be passed into a browser `evaluate` call (e.g. scroll step
7
+ * distance), we need the sampled number without any time elapsing.
8
+ * @param value - The value to resolve.
9
+ * @returns A concrete number.
10
+ */
11
+ export function resolveValue(value) {
12
+ if (typeof value === 'number') {
13
+ return value;
14
+ }
15
+ const random = value.random;
16
+ if (typeof random === 'number') {
17
+ return sampleDistribution(random);
18
+ }
19
+ return sampleDistribution({ min: random.min, max: random.max }, random.distribution);
20
+ }
@@ -1,7 +1,7 @@
1
1
  import type { Page } from 'puppeteer';
2
2
  import { type DelayOptions } from '@d-zero/shared/delay';
3
3
  export type Options = {
4
- distance?: number;
4
+ distance?: number | DelayOptions;
5
5
  interval?: number | DelayOptions;
6
6
  logger?: (scrollY: number, scrollHeight: number, message: string) => void;
7
7
  };
@@ -14,8 +14,12 @@ export type Options = {
14
14
  * progress.
15
15
  * @param page - The Puppeteer page object.
16
16
  * @param options - Optional parameters for scrolling.
17
- * @param options.distance - The distance to scroll on each iteration (default: 100).
18
- * @param options.interval - The interval between each scroll iteration in milliseconds (default: 300).
17
+ * @param options.distance - The distance to scroll on each iteration in pixels.
18
+ * Accepts a fixed number or a random range via `{ random: ... }`. When omitted,
19
+ * each step uses `clientHeight × random(0.5, 1.0)` so it adapts to the viewport.
20
+ * @param options.interval - The interval between each scroll iteration in
21
+ * milliseconds. Accepts a fixed number or a random range via `{ random: ... }`.
22
+ * When omitted, defaults to a random range of 200–500 ms.
19
23
  * @param options.logger - A function that logs messages.
20
24
  */
21
25
  export declare function scrollAllOver(page: Page, options?: Options): Promise<void>;
@@ -1,13 +1,27 @@
1
1
  import { delay } from '@d-zero/shared/delay';
2
+ import { resolveValue } from './resolve-value.js';
2
3
  /**
3
4
  * Number of consecutive iterations without scroll progress before bailing out.
4
5
  *
5
6
  * Scroll-jacking libraries (e.g. fullpage.js) can block `scrollBy` while
6
7
  * `body.scrollHeight` remains larger than the viewport, causing an infinite
7
- * loop. Three stuck iterations (≈ 900 ms at the default interval) is enough
8
- * to confirm that scrolling is genuinely blocked.
8
+ * loop. Three stuck iterations (≈ 1.05 s at the default interval mean) is
9
+ * enough to confirm that scrolling is genuinely blocked.
9
10
  */
10
11
  const MAX_STUCK_ITERATIONS = 3;
12
+ /**
13
+ * Default interval range (ms) used when `options.interval` is omitted.
14
+ * Randomized to mimic human-like reading pauses while staying close to the
15
+ * historical 300 ms fixed default.
16
+ */
17
+ const DEFAULT_INTERVAL = { random: { min: 200, max: 500 } };
18
+ /**
19
+ * Default scroll-step ratio range applied to `clientHeight` when
20
+ * `options.distance` is omitted. Sampled in the browser context per iteration
21
+ * so it adapts to the current viewport height.
22
+ */
23
+ const DEFAULT_DISTANCE_RATIO_MIN = 0.5;
24
+ const DEFAULT_DISTANCE_RATIO_MAX = 1;
11
25
  /**
12
26
  * Scrolls the page vertically until the end or a maximum height is reached.
13
27
  *
@@ -17,26 +31,37 @@ const MAX_STUCK_ITERATIONS = 3;
17
31
  * progress.
18
32
  * @param page - The Puppeteer page object.
19
33
  * @param options - Optional parameters for scrolling.
20
- * @param options.distance - The distance to scroll on each iteration (default: 100).
21
- * @param options.interval - The interval between each scroll iteration in milliseconds (default: 300).
34
+ * @param options.distance - The distance to scroll on each iteration in pixels.
35
+ * Accepts a fixed number or a random range via `{ random: ... }`. When omitted,
36
+ * each step uses `clientHeight × random(0.5, 1.0)` so it adapts to the viewport.
37
+ * @param options.interval - The interval between each scroll iteration in
38
+ * milliseconds. Accepts a fixed number or a random range via `{ random: ... }`.
39
+ * When omitted, defaults to a random range of 200–500 ms.
22
40
  * @param options.logger - A function that logs messages.
23
41
  */
24
42
  export async function scrollAllOver(page, options) {
25
- const interval = options?.interval ?? 300;
43
+ const interval = options?.interval ?? DEFAULT_INTERVAL;
44
+ const distance = options?.distance;
26
45
  let currentScrollY = 0;
27
46
  let scrollHeight = await page.evaluate(() => document.body.scrollHeight);
28
47
  let prevScrollY = -1;
29
48
  let stuckCount = 0;
30
49
  while (Math.ceil(currentScrollY) < Math.ceil(scrollHeight)) {
31
- [currentScrollY, scrollHeight] = await page.evaluate(() => {
32
- // Move the scroll position to the bottom of the page.
33
- globalThis.scrollBy(0, document.documentElement.clientHeight);
34
- // Return the current scroll position.
50
+ // Force a minimum of 1 px so a user-supplied 0/negative distance
51
+ // cannot stall the loop into the stuck-detection bail out.
52
+ const stepDistance = distance === undefined ? null : Math.max(1, resolveValue(distance));
53
+ [currentScrollY, scrollHeight] = await page.evaluate((step, ratioMin, ratioMax) => {
54
+ // When step is null, sample a random fraction of the viewport
55
+ // height so each scroll feels less mechanical.
56
+ const actualStep = step ??
57
+ Math.max(1, Math.floor(document.documentElement.clientHeight *
58
+ (ratioMin + Math.random() * (ratioMax - ratioMin))));
59
+ globalThis.scrollBy(0, actualStep);
35
60
  return [
36
61
  Math.ceil(globalThis.scrollY + globalThis.innerHeight),
37
62
  Math.ceil(document.body.scrollHeight),
38
63
  ];
39
- });
64
+ }, stepDistance, DEFAULT_DISTANCE_RATIO_MIN, DEFAULT_DISTANCE_RATIO_MAX);
40
65
  options?.logger?.(currentScrollY, scrollHeight, 'Scrolling');
41
66
  if (currentScrollY === prevScrollY) {
42
67
  stuckCount++;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d-zero/puppeteer-scroll",
3
- "version": "3.1.18",
3
+ "version": "4.0.1",
4
4
  "description": "Scroll function for puppeteer",
5
5
  "author": "D-ZERO",
6
6
  "license": "MIT",
@@ -23,7 +23,7 @@
23
23
  "clean": "tsc --build --clean"
24
24
  },
25
25
  "dependencies": {
26
- "@d-zero/shared": "0.21.3"
26
+ "@d-zero/shared": "0.22.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "puppeteer": "24.37.5"
@@ -36,5 +36,5 @@
36
36
  "url": "https://github.com/d-zero-dev/tools.git",
37
37
  "directory": "packages/@d-zero/puppeteer-scroll"
38
38
  },
39
- "gitHead": "611614bdfcdfe3ef872efd01a1fd31ebedffdbb2"
39
+ "gitHead": "25b4043dcd70cf3490ddcefd76a88b22c60f7712"
40
40
  }