@humanjs/playwright 0.3.0 → 0.4.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.
- package/README.md +91 -5
- package/dist/index.cjs +501 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +243 -30
- package/dist/index.d.ts +243 -30
- package/dist/index.js +483 -12
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/timing.ts","../src/keyboard/index.ts","../src/internal/mouse-walk.ts","../src/mouse/index.ts","../src/reading/index.ts","../src/scroll/index.ts","../src/mouse-helper/index.ts","../src/index.ts"],"names":["planTypeKeystrokes","box","bezierPath","humanizePath","countWords","computeReadingDwellMs","planReadingScan","clamp","planScroll","resolvePersonality","createRng","options"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Personality, Rng } from '@humanjs/core';\nimport type { Speed } from '..';\n\n/**\n * Sleeps for `ms` milliseconds. Resolves immediately if `ms <= 0` so callers\n * can hand the result of `computeDwellTime` straight through without guards.\n */\nexport function sleep(ms: number): Promise<void> {\n return ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();\n}\n\n/**\n * Per-mode timing multiplier applied on top of personality.speed.\n * - `'human'`: 1 — full humanization\n * - `'fast'`: 0.5 — accelerated humanization (still humanized, just brisk)\n * - `'instant'`: 0 — no delays (CI / snapshot mode)\n */\nexport function speedModeFactor(speed: Speed): number {\n switch (speed) {\n case 'fast':\n return 0.5;\n case 'instant':\n return 0;\n default:\n return 1;\n }\n}\n\n/**\n * Computes a dwell time in ms with deterministic jitter and personality scaling.\n *\n * base = meanMs\n * jitter = base × jitterFraction × rand[-1, 1]\n * total = (base + jitter) × personality.speed × speedModeFactor(speed)\n *\n * Returns 0 for non-positive inputs so callers can skip the sleep entirely.\n */\nexport function computeDwellTime(\n meanMs: number,\n jitter: number,\n personality: Personality,\n speed: Speed,\n rng: Rng,\n): number {\n if (meanMs <= 0) return 0;\n const jitterMag = meanMs * jitter;\n const offset = rng.nextFloat(-jitterMag, jitterMag);\n return Math.max(0, (meanMs + offset) * personality.speed * speedModeFactor(speed));\n}\n","import { type Personality, planTypeKeystrokes, type Rng } from '@humanjs/core';\nimport type { Locator, Page } from 'playwright';\nimport type { Speed } from '../index';\nimport { sleep, speedModeFactor } from '../internal/timing';\n\n/** Runtime dependencies for a humanized keyboard action. */\nexport interface KeyboardContext {\n readonly page: Page;\n readonly personality: Personality;\n readonly rng: Rng;\n readonly speed: Speed;\n}\n\n/** Result of a typing action, returned to the caller for observability. */\nexport interface TypeResult {\n /** Number of characters in the input string. */\n readonly characters: number;\n /** Number of typos injected (with or without correction). */\n readonly typos: number;\n /** Number of typo corrections via Backspace. */\n readonly corrections: number;\n}\n\n/**\n * Executes a humanized typing pass over `value` on `target`.\n *\n * Planning (which keys to press, with what delays, in what order) is delegated\n * to `@humanjs/core`'s `planTypeKeystrokes`. This module is the thin Playwright\n * dispatcher: it focuses the target, walks the plan, and chooses the best\n * Playwright API per key:\n *\n * - Named keys (`Enter`, `Backspace`, `Tab`) and ASCII chars: `keyboard.press`\n * — fires keydown/press/up, so handlers (autocomplete, validation) run.\n * - Non-ASCII characters: `keyboard.insertText` — fires `input` events but\n * not keyboard events, since `keyboard.press` is keyboard-layout-aware\n * and can't reliably synthesize characters like `é` or `🎉` on every layout.\n *\n * In `speed: 'instant'`, the whole humanized loop is bypassed in favor of\n * `locator.pressSequentially(value, { delay: 0 })` — events still fire,\n * humanization is skipped.\n */\nexport async function executeType(\n target: Locator | string,\n value: string,\n ctx: KeyboardContext,\n): Promise<TypeResult> {\n const locator = typeof target === 'string' ? ctx.page.locator(target) : target;\n\n if (value.length === 0) {\n return { characters: 0, typos: 0, corrections: 0 };\n }\n\n if (ctx.speed === 'instant') {\n await locator.pressSequentially(value, { delay: 0 });\n return { characters: value.length, typos: 0, corrections: 0 };\n }\n\n await locator.focus();\n\n const plan = planTypeKeystrokes(value, ctx.personality.typing, ctx.rng, {\n personalitySpeed: ctx.personality.speed,\n speedFactor: speedModeFactor(ctx.speed),\n });\n\n let typos = 0;\n let corrections = 0;\n\n for (const step of plan) {\n if (step.delayBeforeMs > 0) await sleep(step.delayBeforeMs);\n await dispatchKey(ctx.page, step.key);\n if (step.isTypo) typos++;\n if (step.isCorrection) corrections++;\n }\n\n return { characters: value.length, typos, corrections };\n}\n\n/**\n * Dispatches a single planned key. Multi-char strings are treated as named\n * keys (`Enter`, `Backspace`). Single ASCII chars use the layout-aware\n * `keyboard.press`. Single non-ASCII chars fall back to `insertText` so the\n * adapter works on any keyboard layout, at the cost of skipping per-key events.\n */\nasync function dispatchKey(page: Page, key: string): Promise<void> {\n if (key.length > 1 || key.charCodeAt(0) < 128) {\n await page.keyboard.press(key);\n } else {\n await page.keyboard.insertText(key);\n }\n}\n","import type { Point } from '@humanjs/core';\nimport type { Page } from 'playwright';\nimport { sleep } from './timing';\n\n/**\n * Walks the mouse along `path` over `durationMs`, evenly spacing the steps.\n * Generic helper used by both the click executor (which derives `durationMs`\n * from `personality.mouse.travelTimeMs`) and the read executor (which uses\n * the reading dwell duration so the cursor finishes its scan exactly as the\n * dwell elapses).\n *\n * - Empty path is a no-op.\n * - `durationMs <= 0` jumps through the path without inter-step delays\n * (matches `speed: 'instant'` behavior).\n */\nexport async function walkMouseAlongPath(\n page: Page,\n path: readonly Point[],\n durationMs: number,\n): Promise<void> {\n if (path.length === 0) return;\n const stepDelayMs = path.length > 1 && durationMs > 0 ? durationMs / (path.length - 1) : 0;\n\n for (let i = 0; i < path.length; i++) {\n const point = path[i];\n if (!point) continue;\n await page.mouse.move(point.x, point.y);\n if (i < path.length - 1 && stepDelayMs > 0) {\n await sleep(stepDelayMs);\n }\n }\n}\n","import { bezierPath, humanizePath, type Personality, type Point, type Rng } from '@humanjs/core';\nimport type { Locator, Page } from 'playwright';\nimport type { Speed } from '../index';\nimport { walkMouseAlongPath } from '../internal/mouse-walk';\nimport { computeDwellTime, sleep, speedModeFactor } from '../internal/timing';\n\n/** Runtime dependencies for a humanized mouse action. */\nexport interface MouseContext {\n readonly page: Page;\n readonly personality: Personality;\n readonly rng: Rng;\n readonly speed: Speed;\n /** Last known mouse position — used as the path start. */\n readonly getMousePosition: () => Point;\n /** Updates the last known mouse position after a move/click. */\n readonly setMousePosition: (point: Point) => void;\n}\n\n/** Result of a click action, returned to the caller for observability. */\nexport interface ClickResult {\n /** Coordinates the click landed at. */\n readonly target: Point;\n}\n\n/**\n * Executes a humanized click on a target locator.\n *\n * Steps:\n * 1. Resolve the target's bounding box.\n * 2. Pick a point inside it — Gaussian-centered so we never click dead-center,\n * which is itself a bot signal.\n * 3. Generate a Bezier path from the current mouse position to the target.\n * 4. Apply velocity profile + micro-jitter via `humanizePath`.\n * 5. Walk the mouse along the path with timing scaled by personality + speed.\n * 6. Click at the target coordinates.\n *\n * In `speed: 'instant'`, all humanization is bypassed and Playwright's\n * native `locator.click()` is used directly.\n */\nexport async function executeClick(\n target: Locator | string,\n ctx: MouseContext,\n): Promise<ClickResult> {\n const locator = typeof target === 'string' ? ctx.page.locator(target) : target;\n\n if (ctx.speed === 'instant') {\n // Read the bounding box BEFORE the click — the click may navigate away\n // or remove the element, after which `boundingBox()` returns null.\n const box = await locator.boundingBox();\n await locator.click();\n const center = box\n ? { x: box.x + box.width / 2, y: box.y + box.height / 2 }\n : ctx.getMousePosition();\n ctx.setMousePosition(center);\n return { target: center };\n }\n\n const box = await locator.boundingBox();\n if (!box) {\n throw new Error(\n `Cannot click: element not found or has no bounding box (target: ${describeTarget(target)})`,\n );\n }\n\n const targetPoint = pickClickPoint(box, ctx.rng);\n const startPoint = ctx.getMousePosition();\n\n const rawPath = bezierPath(startPoint, targetPoint, ctx.rng, {\n curvature: ctx.personality.mouse.curvature,\n });\n const path = humanizePath(rawPath, ctx.rng);\n\n const travelMs = computeTravelTime(path, ctx.personality, ctx.speed, ctx.rng);\n await walkMouseAlongPath(ctx.page, path, travelMs);\n\n // Hover dwell — a real user briefly settles on the target before clicking.\n const preClickMs = computeDwellTime(\n ctx.personality.dwell.preClickMs,\n ctx.personality.dwell.preClickJitter,\n ctx.personality,\n ctx.speed,\n ctx.rng,\n );\n if (preClickMs > 0) await sleep(preClickMs);\n\n // Commit the new position BEFORE the click side-effect. If the click throws\n // (page closed, target removed mid-flight), the next action still starts\n // from the correct mouse position.\n ctx.setMousePosition(targetPoint);\n await ctx.page.mouse.click(targetPoint.x, targetPoint.y);\n\n // Post-action dwell — a beat after the click before the next action.\n const postActionMs = computeDwellTime(\n ctx.personality.dwell.postActionMs,\n ctx.personality.dwell.postActionJitter,\n ctx.personality,\n ctx.speed,\n ctx.rng,\n );\n if (postActionMs > 0) await sleep(postActionMs);\n\n return { target: targetPoint };\n}\n\n/** Bounding box returned by Playwright's `Locator.boundingBox()`. */\ninterface BoundingBox {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Picks a click point inside the bounding box. Gaussian-centered so the\n * majority of clicks land near the visual center but with natural spread.\n * Clamped to never fall outside the element.\n */\nfunction pickClickPoint(box: BoundingBox, rng: Rng): Point {\n const cx = box.x + box.width / 2;\n const cy = box.y + box.height / 2;\n // Spread is 1/8 of each dimension — most clicks fall within the middle\n // half of the element, but we never click dead-center deterministically.\n const x = clamp(cx + rng.nextGaussian(0, box.width / 8), box.x, box.x + box.width);\n const y = clamp(cy + rng.nextGaussian(0, box.height / 8), box.y, box.y + box.height);\n return { x, y };\n}\n\n/**\n * Travel time in ms for the given path.\n *\n * base = (totalDistance / 1000) * Personality.mouse.travelTimeMs\n * jitter = base * Personality.mouse.travelTimeJitter * rand[-1, 1]\n * total = (base + jitter) * Personality.speed * speedModeFactor\n */\nfunction computeTravelTime(\n path: readonly Point[],\n personality: Personality,\n speed: Speed,\n rng: Rng,\n): number {\n let distance = 0;\n for (let i = 1; i < path.length; i++) {\n const prev = path[i - 1];\n const curr = path[i];\n if (!prev || !curr) continue;\n distance += Math.hypot(curr.x - prev.x, curr.y - prev.y);\n }\n\n const baseTime = (distance / 1000) * personality.mouse.travelTimeMs;\n const jitterMag = baseTime * personality.mouse.travelTimeJitter;\n const jitter = rng.nextFloat(-jitterMag, jitterMag);\n const total = (baseTime + jitter) * personality.speed * speedModeFactor(speed);\n return Math.max(0, total);\n}\n\nfunction describeTarget(target: Locator | string): string {\n return typeof target === 'string' ? target : (target.toString?.() ?? 'locator');\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return value < min ? min : value > max ? max : value;\n}\n","import {\n computeReadingDwellMs,\n countWords,\n type Personality,\n type Point,\n planReadingScan,\n type ReadKind,\n type Rng,\n} from '@humanjs/core';\nimport type { Locator, Page } from 'playwright';\nimport type { Speed } from '../index';\nimport { walkMouseAlongPath } from '../internal/mouse-walk';\nimport { sleep, speedModeFactor } from '../internal/timing';\n\n/** Runtime dependencies for a humanized reading dwell. */\nexport interface ReadingContext {\n readonly page: Page;\n readonly personality: Personality;\n readonly rng: Rng;\n readonly speed: Speed;\n /**\n * Last known cursor position. Used as the starting point for an eye-scan\n * when `withMotion: true`. Required even when motion is off — the read\n * adapter needs to know where the cursor *would* start if motion is later\n * enabled mid-session.\n */\n readonly getMousePosition: () => Point;\n /** Updates the tracked cursor position after a scan completes. */\n readonly setMousePosition: (point: Point) => void;\n}\n\n/**\n * What to read:\n * - `string`: a Playwright-compatible selector (matches `click()` / `type()`).\n * The element's `innerText` is resolved and word-counted.\n * - `Locator`: same, but you already have a Locator handle.\n * - `{ text }`: literal text (you have the string in hand, no selector).\n * - `{ words }`: pre-counted (bypass extraction entirely).\n */\nexport type ReadTarget = string | Locator | { readonly text: string } | { readonly words: number };\n\nexport interface ReadOptions {\n /**\n * Reading mode. Built-in multipliers on top of `personality.reading.wpm`:\n * - `'prose'`: 1.0× (default for non-code targets)\n * - `'code'`: 0.4× (default when target is a `<pre>` or `<code>` element)\n * - `'scan'`: 1.8× (skim mode, must be explicit)\n *\n * **Smart defaults:** when `kind` is omitted AND the target is a Locator/\n * selector, the adapter inspects the resolved element's tag — `<pre>` and\n * `<code>` auto-detect as `'code'`; everything else as `'prose'`. An\n * explicit `kind` always wins over auto-detection.\n */\n readonly kind?: ReadKind;\n /** Override the effective WPM multiplier directly. Wins over `kind`. */\n readonly wpmMultiplier?: number;\n /**\n * For selector/Locator targets: scroll the element into the viewport\n * before the dwell. A user can't read what isn't on screen. Defaults to\n * `false` — in most flows the caller has already scrolled there.\n */\n readonly scrollIntoView?: boolean;\n /**\n * For selector/Locator targets: trace a humanized cursor path through the\n * target's bounding box while the dwell elapses, like an eye tracking\n * lines of text. Off by default — opt in when you want the humanization\n * signal (or when reading-time mouseover behavior matters, e.g. tooltips\n * that activate while a real user's cursor moves over the content).\n *\n * The scan path is generated by `planReadingScan` from `@humanjs/core`\n * (same deterministic-by-seed contract as the rest of the library) and\n * walked over the computed dwell duration, so motion finishes exactly as\n * the read does. The cursor's tracked position is updated to the scan's\n * endpoint so the next click starts from where the eye landed.\n *\n * Ignored when the target is `{ text }` or `{ words }` (no bounding box).\n */\n readonly withMotion?: boolean;\n}\n\n/** Outcome of a read, returned to the caller for observability. */\nexport interface ReadResult {\n /** Number of words counted (after whitespace splitting / from caller's `{ words }`). */\n readonly words: number;\n /** Sleep duration in ms. Zero in `speed: 'instant'` or for zero-word inputs. */\n readonly durationMs: number;\n /** Final reading kind used (after auto-detection + explicit-override resolution). */\n readonly kind: ReadKind;\n}\n\n/**\n * Executes a humanized reading dwell.\n *\n * Flow:\n * 1. Resolve target → words + (optional) Locator\n * 2. Optionally scroll into view\n * 3. Resolve `kind` (explicit → auto-detected from tag → `'prose'`)\n * 4. Compute dwell via `computeReadingDwellMs` (jittered + scaled)\n * 5. Sleep\n *\n * Eye-scan cursor motion during the dwell (`withMotion`) is planned for a\n * follow-up release — see CLAUDE.md \"v1 must-have features.\" The base\n * dwell ships now because it completes the brand's third pillar.\n */\nexport async function executeRead(\n target: ReadTarget,\n ctx: ReadingContext,\n options: ReadOptions = {},\n): Promise<ReadResult> {\n // ── Resolve target → words (+ optional Locator for tag detection / scroll) ─\n let words = 0;\n let locator: Locator | undefined;\n\n if (typeof target === 'string') {\n locator = ctx.page.locator(target);\n } else if ('words' in target) {\n words = target.words;\n } else if ('text' in target) {\n words = countWords(target.text);\n } else {\n // Only Locator remains in the union after the discriminator checks above.\n locator = target;\n }\n\n let autoDetectedKind: ReadKind | undefined;\n\n if (locator) {\n if (options.scrollIntoView) {\n await locator.scrollIntoViewIfNeeded();\n }\n const text = await locator.innerText().catch(() => '');\n words = countWords(text);\n\n // Auto-detect kind from tag only when caller didn't specify one.\n if (options.kind === undefined) {\n autoDetectedKind = await detectKindFromTag(locator);\n }\n }\n\n const kind = options.kind ?? autoDetectedKind ?? 'prose';\n\n const durationMs = computeReadingDwellMs(words, ctx.personality.reading, ctx.rng, {\n kind,\n wpmMultiplier: options.wpmMultiplier,\n personalitySpeed: ctx.personality.speed,\n speedFactor: speedModeFactor(ctx.speed),\n });\n\n if (options.withMotion && locator && durationMs > 0) {\n // Walk a humanized scan path across the rendered text over the dwell\n // duration. `walkMouseAlongPath` paces the steps so motion ends exactly\n // as the read does — no extra sleep needed.\n const box = await locator.boundingBox().catch(() => null);\n if (box) {\n // Per-line rects make sweeps land on actual text edges, not the\n // element's full width — important for `<pre>` / `<code>` blocks\n // where individual lines are far narrower than the container.\n const lineRects = await getLineRects(locator).catch(() => []);\n const path = planReadingScan(box, ctx.rng, {\n start: ctx.getMousePosition(),\n lineRects: lineRects.length > 0 ? lineRects : undefined,\n });\n await walkMouseAlongPath(ctx.page, path, durationMs);\n const final = path[path.length - 1];\n if (final) ctx.setMousePosition(final);\n return { words, durationMs, kind };\n }\n // No box → fall through to a plain sleep (rare but possible if the\n // element disappeared between innerText() and boundingBox()).\n }\n\n if (durationMs > 0) await sleep(durationMs);\n\n return { words, durationMs, kind };\n}\n\n// ────────── helpers ──────────\n\n/**\n * Inspects the element's tag for the smart default. Only the narrowest\n * heuristic (literal `<pre>` / `<code>` tagName) — CSS class sniffing would\n * be too magical. Returns undefined for anything else, so the caller's\n * resolution chain falls back to `'prose'`.\n */\n/**\n * Returns one rect per visible line of *text* inside the element, in\n * viewport coordinates (same space as `locator.boundingBox()`).\n *\n * Walks text-node descendants and collects each text node's line rects via\n * `Range.getClientRects()`. This is the only way to get tight per-line\n * geometry when the target contains block-level descendants — calling\n * `selectNodeContents` over a `<ul>` or `<div>` would return the block's\n * content box (full container width), not the rendered text edges.\n *\n * Rects on the same visual line (e.g. text broken by `<strong>` / `<em>`)\n * are merged so the planner sweeps each line as a single L→R stroke\n * rather than zigzagging through inline siblings.\n */\nasync function getLineRects(\n locator: Locator,\n): Promise<Array<{ x: number; y: number; width: number; height: number }>> {\n const result = await locator.evaluate((el) => {\n type Rect = { x: number; y: number; width: number; height: number };\n // `4` === `NodeFilter.SHOW_TEXT`. Hardcoded so we don't depend on the\n // global `NodeFilter` being in the Node-side TS lib.\n const walker = el.ownerDocument.createTreeWalker(el, 4);\n const rects: Rect[] = [];\n let node = walker.nextNode();\n while (node) {\n const text = node.textContent ?? '';\n if (text.trim().length > 0) {\n const range = el.ownerDocument.createRange();\n range.selectNodeContents(node);\n for (const r of Array.from(range.getClientRects())) {\n if (r.width > 0 && r.height > 0) {\n rects.push({ x: r.x, y: r.y, width: r.width, height: r.height });\n }\n }\n }\n node = walker.nextNode();\n }\n\n // Merge rects that sit on the same visual line — text broken by inline\n // siblings (\"hello <strong>bold</strong> world\") produces three rects\n // with the same y. Merged, that becomes a single L→R stroke across the\n // full line of text.\n rects.sort((a, b) => a.y - b.y || a.x - b.x);\n const merged: Rect[] = [];\n for (const r of rects) {\n const last = merged[merged.length - 1];\n if (last && Math.abs(last.y - r.y) < 1 && r.x - (last.x + last.width) < 6) {\n const right = Math.max(last.x + last.width, r.x + r.width);\n const bottom = Math.max(last.y + last.height, r.y + r.height);\n last.width = right - last.x;\n last.height = bottom - last.y;\n } else {\n merged.push({ ...r });\n }\n }\n return merged;\n });\n // Defensive: validate the evaluate result is an array of rect-shaped\n // objects before handing it to the planner. Cheap, and keeps the planner\n // from ever seeing junk from a mocked evaluate or an exotic test harness.\n if (!Array.isArray(result)) return [];\n return result.filter(\n (r): r is { x: number; y: number; width: number; height: number } =>\n r != null &&\n typeof r === 'object' &&\n typeof (r as { x?: unknown }).x === 'number' &&\n typeof (r as { y?: unknown }).y === 'number' &&\n typeof (r as { width?: unknown }).width === 'number' &&\n typeof (r as { height?: unknown }).height === 'number',\n );\n}\n\nasync function detectKindFromTag(locator: Locator): Promise<ReadKind | undefined> {\n // `evaluate` runs the function in the browser context; Playwright's types\n // give the callback a DOM Element here without needing the lib.dom ref\n // on the Node-side tsconfig.\n const tag = await locator.evaluate((el) => el.tagName?.toLowerCase() ?? '').catch(() => '');\n if (tag === 'pre' || tag === 'code') return 'code';\n return undefined;\n}\n","import { type Personality, planScroll, type Rng, type ScrollSegment } from '@humanjs/core';\nimport type { Locator, Page } from 'playwright';\nimport type { Speed } from '../index';\nimport { sleep, speedModeFactor } from '../internal/timing';\n\n/** Runtime dependencies for a humanized scroll. */\nexport interface ScrollContext {\n readonly page: Page;\n readonly personality: Personality;\n readonly rng: Rng;\n readonly speed: Speed;\n}\n\n/**\n * What / where to scroll. The chosen axis (`'y'` by default, `'x'` via\n * `options.axis`) decides whether targets resolve along the vertical or\n * horizontal axis.\n *\n * - `'natural'`: scroll one viewport along the chosen axis (default).\n * - `'end'`: scroll to the far edge (bottom for `'y'`, right for `'x'`).\n * - `'top'`: scroll to the origin (top for `'y'`, left for `'x'`).\n * - `string`: a Playwright-compatible selector — scroll until in view.\n * - `Locator`: same, but you already have a Locator handle.\n * - `{ by: number }`: relative pixel delta (negative = up / left).\n * - `{ to: number }`: absolute scroll position on the chosen axis.\n */\nexport type ScrollTarget =\n | 'natural'\n | 'end'\n | 'top'\n | string\n | Locator\n | { readonly by: number }\n | { readonly to: number };\n\nexport interface ScrollOptions {\n /**\n * Force an overshoot + correction even if the personality wouldn't choose\n * one. Useful when you want the humanization signal regardless of\n * personality (e.g. demos).\n */\n readonly overshoot?: boolean;\n /**\n * Disable mid-scroll micro-pauses. Defaults to `true` (pauses enabled).\n * Set `false` for the smoothest possible motion.\n */\n readonly withPauses?: boolean;\n /**\n * For element targets only: where to align the element along the chosen\n * axis when the scroll ends. Mirrors `scrollIntoView({ block })`.\n * Defaults to `'start'`.\n *\n * - `'start'`: element's leading edge aligns with the viewport's leading\n * edge (top for `axis: 'y'`, left for `axis: 'x'`)\n * - `'center'`: element centers in the viewport\n * - `'end'`: element's trailing edge aligns with the viewport's trailing\n * edge (bottom for `'y'`, right for `'x'`)\n * - `'nearest'`: do the minimum scroll — stay put if the element is\n * already fully visible, otherwise scroll to the closest edge\n */\n readonly block?: 'start' | 'center' | 'end' | 'nearest';\n /**\n * Scroll inside a scrollable container instead of the page. Accepts a\n * Playwright-compatible selector or a built `Locator`. Every scroll\n * semantic (`'natural'`, `'top'`, `'end'`, `{ by }`, `{ to }`, element\n * targets, `block` alignment) applies relative to the container.\n *\n * In humanized speed modes, the cursor parks over the container's\n * center so the visible cursor reads as \"human hand on the wheel\"\n * (when paired with `installMouseHelper`), and each planned segment\n * advances the container's `scrollLeft` / `scrollTop` directly. Direct\n * property assignment is more reliable than dispatching wheel events\n * into nested overflow containers — Playwright's synthetic wheel\n * events don't always route past the cursor element. In `'instant'`\n * mode, the container's scroll position is set with a single\n * `scrollTo` call.\n *\n * Common use: chat threads, modal bodies, infinite-scroll feeds, any\n * `<div style=\"overflow-y: auto\">` that owns its own scrollbar.\n */\n readonly within?: string | Locator;\n /**\n * Which axis to scroll along. Defaults to `'y'` (vertical).\n *\n * Set `'x'` for horizontal scrolling — carousels, kanban boards,\n * sideways galleries. Every target shape still works: `'natural'`\n * scrolls one viewport-width right, `'top'` jumps to scrollLeft 0,\n * `'end'` to (scrollWidth - clientWidth), `{ by }` / `{ to }` apply to\n * the X-axis, and element targets scroll until the element is visible\n * horizontally.\n */\n readonly axis?: 'x' | 'y';\n}\n\n/** Outcome of a scroll, returned to the caller for observability. */\nexport interface ScrollResult {\n /** Starting scroll position along the chosen axis (scrollY or scrollX). */\n readonly from: number;\n /** Final scroll position the scroll aimed for, along the chosen axis. */\n readonly to: number;\n /** Signed pixel distance (`to - from`). */\n readonly distance: number;\n /** Total elapsed dwell + motion time in ms. Zero in `speed: 'instant'`. */\n readonly durationMs: number;\n}\n\n/** Reserved string targets that select a behavior rather than an element. */\nconst RESERVED_TARGETS = new Set(['natural', 'end', 'top']);\n\n/**\n * Generic axis description — the planner doesn't care whether it's driving\n * `window.scrollY` or an element's `scrollTop`. Both look like the same\n * 1-D problem: a current position, a viewport length, and a max scrollable\n * extent.\n */\ninterface ScrollGeometry {\n /** Current scroll position. */\n readonly current: number;\n /** Visible length (viewport height for window, clientHeight for container). */\n readonly viewport: number;\n /** Total scrollable extent (docHeight for window, scrollHeight for container). */\n readonly total: number;\n /**\n * For container scrolls: viewport-relative center coordinates the cursor\n * should park at so an `installMouseHelper` overlay reads as \"human hand\n * on the wheel\" during the scroll. Undefined for window scrolls (no\n * cursor parking — the page scrolls without a per-target hover).\n */\n readonly hover?: { x: number; y: number };\n}\n\n/**\n * Executes a humanized scroll on either the page or a scrollable container,\n * along the chosen axis.\n *\n * Flow:\n * 1. Resolve the scope: page (default) or a container if `within` is set.\n * 2. Read current position + viewport + total geometry for the chosen axis.\n * 3. Resolve target → final position on that axis.\n * 4. Plan segments via `planScroll`.\n * 5. Walk the segments — for page scrolls, dispatch `page.mouse.wheel`\n * events on the chosen axis. For containers, advance the element's\n * `scrollLeft` / `scrollTop` directly (wheel events don't always\n * route into nested overflow containers). Containers also park the\n * cursor at their center so an `installMouseHelper` overlay reads\n * as \"hand on the wheel.\"\n *\n * In `speed: 'instant'`, all humanization is bypassed:\n * - Page scrolls call `window.scrollTo(...)` with the new position on the\n * chosen axis and the existing position on the other.\n * - Container scrolls evaluate `el.scrollTo(...)` with the same shape.\n */\nexport async function executeScroll(\n target: ScrollTarget | undefined,\n ctx: ScrollContext,\n options: ScrollOptions = {},\n): Promise<ScrollResult> {\n const { page, personality, rng, speed } = ctx;\n const speedFactor = speedModeFactor(speed);\n const axis: 'x' | 'y' = options.axis ?? 'y';\n\n const container = resolveWithin(options.within, ctx);\n const geom = container\n ? await readContainerGeometry(container, axis)\n : await readWindowGeometry(page, axis);\n if (!geom) {\n // Container not found / not scrollable — nothing to do.\n return { from: 0, to: 0, distance: 0, durationMs: 0 };\n }\n\n const from = geom.current;\n const targetPos = await resolveTarget(target, ctx, geom, container, axis, options.block);\n const to = clamp(targetPos, 0, Math.max(0, geom.total - geom.viewport));\n const distance = to - from;\n\n if (distance === 0) {\n return { from, to, distance: 0, durationMs: 0 };\n }\n\n if (speed === 'instant') {\n if (container) {\n await container.evaluate(\n (el, args) => {\n const a = args as { axis: 'x' | 'y'; pos: number };\n if (a.axis === 'x') el.scrollTo(a.pos, el.scrollTop);\n else el.scrollTo(el.scrollLeft, a.pos);\n },\n { axis, pos: to },\n );\n } else {\n await page.evaluate(\n (args) => {\n if (args.axis === 'x') window.scrollTo(args.pos, window.scrollY);\n else window.scrollTo(window.scrollX, args.pos);\n },\n { axis, pos: to },\n );\n }\n return { from, to, distance, durationMs: 0 };\n }\n\n const segments = planScroll(from, to, personality.scroll, rng, {\n forceOvershoot: options.overshoot,\n withPauses: options.withPauses,\n personalitySpeed: personality.speed,\n speedFactor,\n });\n\n // For container scrolls, park the cursor over the container so the\n // visible cursor (when `installMouseHelper` is in use) reads as \"human\n // hand on the wheel.\" Actual scrolling goes through direct\n // `scrollLeft` / `scrollTop` assignment in `walkSegments` — Playwright's\n // synthetic wheel events don't reliably route into nested overflow\n // containers.\n if (container && geom.hover) {\n await page.mouse.move(geom.hover.x, geom.hover.y);\n }\n\n const startedAt = Date.now();\n await walkSegments(page, segments, axis, container);\n const durationMs = Date.now() - startedAt;\n\n return { from, to, distance, durationMs };\n}\n\n/** Coerces the `within` option into a Locator (or null when unset). */\nfunction resolveWithin(within: ScrollOptions['within'], ctx: ScrollContext): Locator | null {\n if (!within) return null;\n return typeof within === 'string' ? ctx.page.locator(within) : within;\n}\n\n/** Reads the page's current scroll geometry for the chosen axis. */\nasync function readWindowGeometry(page: Page, axis: 'x' | 'y'): Promise<ScrollGeometry> {\n const g = await page.evaluate((a: 'x' | 'y') => {\n if (a === 'x') {\n return {\n current: window.scrollX,\n viewport: window.innerWidth,\n total: Math.max(document.documentElement.scrollWidth, document.body?.scrollWidth ?? 0),\n };\n }\n return {\n current: window.scrollY,\n viewport: window.innerHeight,\n total: Math.max(document.documentElement.scrollHeight, document.body?.scrollHeight ?? 0),\n };\n }, axis);\n return { current: g.current, viewport: g.viewport, total: g.total };\n}\n\n/**\n * Reads container scroll geometry for the chosen axis plus the center of\n * its viewport-relative bounding box (for cursor parking). Returns `null`\n * if the element resolves to nothing.\n */\nasync function readContainerGeometry(\n container: Locator,\n axis: 'x' | 'y',\n): Promise<ScrollGeometry | null> {\n return container\n .evaluate((el, a: 'x' | 'y') => {\n const rect = el.getBoundingClientRect();\n const isX = a === 'x';\n return {\n current: isX ? el.scrollLeft : el.scrollTop,\n viewport: isX ? el.clientWidth : el.clientHeight,\n total: isX ? el.scrollWidth : el.scrollHeight,\n hover: {\n x: rect.left + rect.width / 2,\n y: rect.top + rect.height / 2,\n },\n } as const;\n }, axis)\n .catch(() => null);\n}\n\n/**\n * Resolves a `ScrollTarget` to an absolute scroll position on the active\n * axis (window scroll or container scroll, vertical or horizontal).\n */\nasync function resolveTarget(\n target: ScrollTarget | undefined,\n ctx: ScrollContext,\n geom: ScrollGeometry,\n container: Locator | null,\n axis: 'x' | 'y',\n block: 'start' | 'center' | 'end' | 'nearest' = 'start',\n): Promise<number> {\n if (target === undefined || target === 'natural') return geom.current + geom.viewport;\n if (target === 'end') return geom.total;\n if (target === 'top') return 0;\n if (typeof target === 'object' && 'by' in target) return geom.current + target.by;\n if (typeof target === 'object' && 'to' in target) return target.to;\n\n // Element target — selector string or Locator.\n const elementLocator =\n typeof target === 'string' && !RESERVED_TARGETS.has(target)\n ? ctx.page.locator(target)\n : typeof target === 'string'\n ? null\n : target;\n if (!elementLocator) return geom.current + geom.viewport;\n\n return container\n ? resolveElementWithinContainer(elementLocator, container, geom, axis, block)\n : resolveElementInWindow(elementLocator, geom, axis, block);\n}\n\n/**\n * Computes the scroll position needed to align a window-level element per\n * `block` along the chosen axis. `rect.x` / `rect.y` are viewport-relative;\n * absolute position is `geom.current + rect.{axis}`.\n */\nasync function resolveElementInWindow(\n elementLocator: Locator,\n geom: ScrollGeometry,\n axis: 'x' | 'y',\n block: 'start' | 'center' | 'end' | 'nearest',\n): Promise<number> {\n const rect = await elementLocator.boundingBox().catch(() => null);\n if (!rect) return geom.current;\n const relStart = axis === 'x' ? rect.x : rect.y;\n const length = axis === 'x' ? rect.width : rect.height;\n const absoluteStart = geom.current + relStart;\n const absoluteEnd = absoluteStart + length;\n if (block === 'start') return absoluteStart;\n if (block === 'end') return absoluteEnd - geom.viewport;\n if (block === 'nearest') {\n if (relStart >= 0 && relStart + length <= geom.viewport) return geom.current;\n if (relStart < 0) return absoluteStart;\n return absoluteEnd - geom.viewport;\n }\n return absoluteStart - (geom.viewport - length) / 2;\n}\n\n/**\n * Computes the container's `scrollTop` / `scrollLeft` needed to align an\n * element per `block` along the chosen axis. Element offset from the\n * container's content origin =\n * `(element.rect.{axis} - container.rect.{axis}) + container.scroll{axis}`\n * regardless of whether the container is the element's positioning ancestor.\n */\nasync function resolveElementWithinContainer(\n elementLocator: Locator,\n container: Locator,\n geom: ScrollGeometry,\n axis: 'x' | 'y',\n block: 'start' | 'center' | 'end' | 'nearest',\n): Promise<number> {\n const rects = await container\n .evaluate(\n (containerEl, args: { sel: string | null; axis: 'x' | 'y' }) => {\n const elementEl = args.sel ? document.querySelector(args.sel) : null;\n // The element may not be a child of the container — handle that too.\n const targetEl = elementEl ?? (containerEl.querySelector(':scope > *') as Element | null);\n if (!targetEl) return null;\n const cRect = containerEl.getBoundingClientRect();\n const eRect = targetEl.getBoundingClientRect();\n return args.axis === 'x'\n ? { relStart: eRect.left - cRect.left, length: eRect.width }\n : { relStart: eRect.top - cRect.top, length: eRect.height };\n },\n { sel: await locatorSelector(elementLocator), axis },\n )\n .catch(() => null);\n if (!rects) return geom.current;\n // Offset from container's content origin = (visual delta) + scroll position.\n const offsetStart = rects.relStart + geom.current;\n const offsetEnd = offsetStart + rects.length;\n if (block === 'start') return offsetStart;\n if (block === 'end') return offsetEnd - geom.viewport;\n if (block === 'nearest') {\n if (rects.relStart >= 0 && rects.relStart + rects.length <= geom.viewport) {\n return geom.current;\n }\n if (rects.relStart < 0) return offsetStart;\n return offsetEnd - geom.viewport;\n }\n return offsetStart - (geom.viewport - rects.length) / 2;\n}\n\n/**\n * Best-effort recovery of the selector string a Locator wraps. Playwright's\n * `Locator.toString()` returns something like `locator('css=#foo')` — we\n * strip the wrapper to get the inner selector. Falls back to `null` when\n * the format isn't recognized; the caller treats that as \"no element.\"\n *\n * Fragile by design: depends on Playwright's internal `toString()` shape,\n * which isn't a stable public API. Used only when resolving element\n * targets *inside* a `within` container, since the executor needs to\n * `querySelector` from the container's perspective rather than the\n * document's. For chained Locators (e.g. `page.locator('foo').first()`)\n * the regex grabs the first quoted selector and ignores chained\n * refinements — good enough for the common case, but worth a richer\n * approach if Playwright ever changes the shape.\n */\nasync function locatorSelector(locator: Locator): Promise<string | null> {\n const s = locator.toString?.();\n if (typeof s !== 'string') return null;\n const match = /locator\\(['\"](.+?)['\"]/.exec(s);\n if (!match) return null;\n const raw = match[1] ?? '';\n // Strip an \"engine=\" prefix when present (Playwright sometimes prepends one).\n const eq = raw.indexOf('=');\n return eq > 0 && /^[a-z]+$/.test(raw.slice(0, eq)) ? raw.slice(eq + 1) : raw;\n}\n\n/**\n * Walks the planned segments. For page scrolls, dispatches wheel events\n * on the chosen axis — real wheel events trigger every page-level wheel\n * handler, which is part of HumanJS's \"dispatch what a human dispatches\"\n * promise.\n *\n * For container scrolls, advances `scrollLeft` / `scrollTop` directly via\n * `container.evaluate`. Playwright's `page.mouse.wheel` doesn't reliably\n * route into nested overflow containers (the event hits the element under\n * the cursor but may scroll a parent or no element instead). Direct\n * property assignment is deterministic — it always scrolls the target —\n * and the brand promise stays intact because the planner still owns the\n * bell-curve cadence, mid-scroll pauses, and overshoot.\n */\nasync function walkSegments(\n page: Page,\n segments: readonly ScrollSegment[],\n axis: 'x' | 'y',\n container: Locator | null,\n): Promise<void> {\n for (const segment of segments) {\n if (segment.delayBeforeMs > 0) await sleep(segment.delayBeforeMs);\n if (segment.delta === 0) continue;\n if (container) {\n await container.evaluate(\n (el, args) => {\n const a = args as { axis: 'x' | 'y'; delta: number };\n // Direct property assignment is more reliable than `scrollBy()`\n // — some flex/grid layouts have edge cases where scrollBy\n // becomes a no-op despite the element being scrollable.\n if (a.axis === 'x') el.scrollLeft += a.delta;\n else el.scrollTop += a.delta;\n },\n { axis, delta: segment.delta },\n );\n } else if (axis === 'x') {\n await page.mouse.wheel(segment.delta, 0);\n } else {\n await page.mouse.wheel(0, segment.delta);\n }\n }\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return value < min ? min : value > max ? max : value;\n}\n","/// <reference lib=\"dom\" />\n\nimport type { BrowserContext, Page } from 'playwright';\n\n/**\n * Canonical HumanJS cursor path — kept in lockstep with the SVG used by the\n * web app's `HumanCursorIcon`. Inlined here so the playwright package has no\n * dependency on `@humanjs/web` or DOM assets.\n */\nconst CURSOR_PATH = 'M 0 0 L 16 6 L 8 9.5 L 5 19 Z';\n\n/** Options for {@link installMouseHelper}. */\nexport interface InstallMouseHelperOptions {\n /** Cursor fill color. Defaults to the HumanJS amber `#f5a55c`. */\n readonly color?: string;\n /** Cursor visual size in pixels. Defaults to 22. */\n readonly size?: number;\n /**\n * Render a ripple at each `mousedown` position so clicks read on video.\n * Defaults to `true`.\n */\n readonly showClicks?: boolean;\n /** Halo opacity behind the cursor. Defaults to `0.18`. */\n readonly haloOpacity?: number;\n}\n\n/**\n * Installs a visual cursor overlay that follows every `mousemove` on each\n * page in the target. Real synthetic motion from Playwright (e.g.\n * `human.click()`, `human.read(..., { withMotion: true })`) is *already*\n * happening — the page just doesn't render a system cursor for it. This\n * helper injects a HumanJS-styled SVG cursor that listens to mousemove\n * events and follows them, making the motion visible in headed demos and\n * screen recordings.\n *\n * Re-runs on every navigation via `addInitScript`, so the overlay survives\n * page reloads. Idempotent — guard flag on `window` prevents double-install.\n *\n * Accepts either a `Page` (overlay applies to that page) or a\n * `BrowserContext` (overlay applies to every page in the context, including\n * pages opened later).\n *\n * @example\n * ```ts\n * const browser = await chromium.launch({ headless: false });\n * const context = await browser.newContext();\n * await installMouseHelper(context);\n * const page = await context.newPage();\n * // human-driven actions are now visible in the page\n * ```\n */\nexport async function installMouseHelper(\n target: BrowserContext | Page,\n options: InstallMouseHelperOptions = {},\n): Promise<void> {\n const config: HelperConfig = {\n color: options.color ?? '#f5a55c',\n stroke: '#020203',\n size: options.size ?? 22,\n showClicks: options.showClicks ?? true,\n haloOpacity: options.haloOpacity ?? 0.18,\n path: CURSOR_PATH,\n };\n\n // Register for every future navigation. Survives goto, reload, and frame\n // attachment without needing to re-install.\n await target.addInitScript(installScript, config);\n\n // ALSO inject into any pages that already exist. Without this, a page that\n // has already loaded (via goto or setContent) won't get the overlay until\n // the next navigation — and `page.setContent()` in particular replaces the\n // document, wiping anything the initial init-script run appended. Pages\n // opened later still get the overlay via the registered init script.\n const pages: Page[] = 'pages' in target ? target.pages() : [target];\n await Promise.all(\n pages.map((page) => page.evaluate(installScript, config).catch(() => undefined)),\n );\n}\n\ninterface HelperConfig {\n color: string;\n stroke: string;\n size: number;\n showClicks: boolean;\n haloOpacity: number;\n path: string;\n}\n\n/**\n * Serialized into the page and re-run on every navigation. No closures —\n * everything threads through `config`. `window` and `document` are the\n * page's globals at runtime, not the Node-side types.\n */\nfunction installScript(config: HelperConfig) {\n const guardKey = '__humanjsMouseHelperInstalled';\n const w = window as Window & { [guardKey]?: boolean };\n if (w[guardKey]) return;\n w[guardKey] = true;\n\n const attach = () => {\n const cursor = document.createElement('div');\n cursor.setAttribute('aria-hidden', 'true');\n cursor.setAttribute('data-humanjs-cursor', 'true');\n cursor.style.cssText = [\n 'position: fixed',\n 'left: 0',\n 'top: 0',\n `width: ${config.size}px`,\n `height: ${config.size + 4}px`,\n 'pointer-events: none',\n 'z-index: 2147483647',\n // Start visible at (0, 0) so the cursor is on screen from the moment\n // the page loads — without this the helper looks like nothing happened\n // until the first mousemove arrives.\n 'opacity: 1',\n 'transform: translate(0px, 0px)',\n // CSS interpolates between successive `mousemove` updates so the\n // cursor reads as continuous motion instead of discrete hops. Slightly\n // longer than the path-walker's typical step interval (~30–80ms) so\n // each tween is still settling when the next move lands → no pauses.\n 'transition: transform 110ms ease-out, opacity 0.18s ease-out',\n 'will-change: transform',\n ].join('; ');\n const haloRadius = Math.round(config.size * 0.6);\n cursor.innerHTML = `\n <svg width=\"${config.size}\" height=\"${config.size + 4}\" viewBox=\"0 0 22 24\" style=\"overflow: visible;\">\n <circle cx=\"0\" cy=\"0\" r=\"${haloRadius}\" fill=\"${config.color}\" opacity=\"${config.haloOpacity}\" />\n <path d=\"${config.path}\" fill=\"${config.color}\" stroke=\"${config.stroke}\" stroke-width=\"0.7\" stroke-linejoin=\"round\" />\n </svg>\n `;\n document.body.appendChild(cursor);\n\n let lastX = 0;\n let lastY = 0;\n const onMove = (e: MouseEvent) => {\n lastX = e.clientX;\n lastY = e.clientY;\n cursor.style.transform = `translate(${lastX}px, ${lastY}px)`;\n cursor.style.opacity = '1';\n };\n // Listen on both window and document so we catch synthetic mouse events\n // dispatched at either level (Playwright sends them via CDP, and the\n // target frame can vary by event source).\n window.addEventListener('mousemove', onMove, { capture: true, passive: true });\n document.addEventListener('mousemove', onMove, { capture: true, passive: true });\n document.addEventListener(\n 'mouseleave',\n () => {\n cursor.style.opacity = '0';\n },\n { capture: true, passive: true },\n );\n\n if (config.showClicks) {\n const styleEl = document.createElement('style');\n styleEl.textContent =\n '@keyframes humanjs-ripple { 0% { transform: translate(-50%, -50%) scale(0.4); opacity: 0.9; } 100% { transform: translate(-50%, -50%) scale(2); opacity: 0; } }';\n document.head.appendChild(styleEl);\n\n window.addEventListener(\n 'mousedown',\n () => {\n const ripple = document.createElement('div');\n ripple.style.cssText = [\n 'position: fixed',\n `left: ${lastX}px`,\n `top: ${lastY}px`,\n 'width: 28px',\n 'height: 28px',\n 'border-radius: 50%',\n `border: 1.5px solid ${config.color}`,\n 'pointer-events: none',\n 'z-index: 2147483646',\n 'animation: humanjs-ripple 0.45s ease-out forwards',\n ].join('; ');\n document.body.appendChild(ripple);\n window.setTimeout(() => ripple.remove(), 500);\n },\n { capture: true, passive: true },\n );\n }\n };\n\n if (document.body) attach();\n else document.addEventListener('DOMContentLoaded', attach, { once: true });\n}\n","import {\n type ActionResult,\n createRng,\n type HumanAction,\n type HumanPlugin,\n type Personality,\n type PersonalityConfig,\n type PluginContext,\n type Point,\n resolvePersonality,\n} from '@humanjs/core';\nimport type { Locator, Page } from 'playwright';\nimport { executeType } from './keyboard';\nimport { executeClick } from './mouse';\nimport { executeRead, type ReadOptions, type ReadResult, type ReadTarget } from './reading';\nimport { executeScroll, type ScrollOptions, type ScrollResult, type ScrollTarget } from './scroll';\n\nexport type {\n ActionResult,\n ActionType,\n BezierPathOptions,\n ComputeReadingDwellOptions,\n DwellProfile,\n HumanAction,\n HumanizePathOptions,\n HumanPlugin,\n Keystroke,\n KnownActionType,\n MouseProfile,\n Personality,\n PersonalityConfig,\n PersonalityExtension,\n PlanTypingOptions,\n PluginContext,\n Point,\n PresetName,\n ReadingProfile,\n ReadKind,\n Rng,\n ScrollProfile,\n ScrollSegment,\n TypingProfile,\n} from '@humanjs/core';\n// Re-exports of the public core API so consumers have one import surface.\nexport {\n applyMicroJitter,\n applyVelocityProfile,\n bezierPath,\n blend,\n careful,\n computeReadingDwellMs,\n countWords,\n createRng,\n distracted,\n fast,\n humanizePath,\n planScroll,\n planTypeKeystrokes,\n precise,\n resolvePersonality,\n} from '@humanjs/core';\nexport type { InstallMouseHelperOptions } from './mouse-helper';\nexport { installMouseHelper } from './mouse-helper';\nexport type { ReadOptions, ReadResult, ReadTarget } from './reading';\nexport type { ScrollOptions, ScrollResult, ScrollTarget } from './scroll';\n\n/**\n * How fast the humanized session runs.\n * - `'human'` — full humanization (default)\n * - `'fast'` — humanized but accelerated\n * - `'instant'` — bypass all humanization, straight Playwright\n */\nexport type Speed = 'fast' | 'human' | 'instant';\n\n/** Options for {@link createHuman}. */\nexport interface CreateHumanOptions {\n /** Personality preset, extension, or fully built personality. Defaults to `'careful'`. */\n readonly personality?: PersonalityConfig;\n /** Seed for the session's PRNG. Same seed produces identical trajectories. */\n readonly seed?: number | string;\n /** Speed mode. Defaults to `'human'`. */\n readonly speed?: Speed;\n /** Plugins installed on this session, invoked in registration order. */\n readonly plugins?: readonly HumanPlugin[];\n /**\n * Starting cursor position used as the origin of the first humanized path.\n * Defaults to `{ x: 0, y: 0 }`. Set this if you've already moved the cursor\n * (e.g. via `page.mouse.move`) before creating the session, so the first\n * click's path starts from the correct location.\n */\n readonly initialMousePosition?: Point;\n}\n\n/** A humanized Playwright session bound to a single `Page`. */\nexport interface Human {\n /** The resolved personality this session is using. */\n readonly personality: Personality;\n /** The speed mode this session was created with. */\n readonly speed: Speed;\n /**\n * Navigate to `url`. Plugins observe the action via `'goto'`; the underlying\n * `page.goto(url)` is awaited unchanged.\n */\n goto(url: string): Promise<void>;\n /**\n * Move the mouse along a humanized Bezier path to `target` and click.\n *\n * `target` accepts either a Playwright-compatible selector string (e.g.\n * `'button:has-text(\"Buy now\")'`) or a built `Locator`. The click point\n * inside the element is Gaussian-distributed around the center.\n *\n * In `speed: 'instant'`, all humanization is skipped and Playwright's\n * native `locator.click()` is used directly.\n */\n click(target: Locator | string): Promise<void>;\n /**\n * Type `value` into `target` with humanized per-key timing, optional typo\n * injection (with backspace recovery), and occasional think-pauses.\n *\n * Per-key `keydown`/`press`/`up` events fire for each character, so\n * handlers like autocomplete dropdowns still receive every keystroke.\n *\n * In `speed: 'instant'`, falls back to `locator.pressSequentially` with\n * zero inter-key delay — events still fire, but humanization is skipped.\n */\n type(target: Locator | string, value: string): Promise<void>;\n /**\n * Dwell as if reading `target` — the third pillar of humanization after\n * the cursor and the keyboard. Real users pause to read; HumanJS models\n * that pause from word count + the personality's reading WPM (+ jitter).\n *\n * **Targets:**\n * - `string`: a Playwright-compatible selector (matches `click()`/`type()`).\n * - `Locator`: a pre-built Locator.\n * - `{ text }`: literal text in hand (no DOM lookup).\n * - `{ words }`: pre-counted — skip text extraction entirely.\n *\n * **Smart defaults** (only when the caller doesn't override):\n * - `kind` auto-detects as `'code'` for `<pre>` and `<code>` tags;\n * everything else falls back to `'prose'`. Explicit `kind` always wins.\n * - `scrollIntoView: false` — most flows already scrolled to the content.\n *\n * Plugins observe `'read'` actions with `{ target, words, kind }` in params.\n * The text content itself is never echoed — passwords, tokens, and other\n * sensitive strings stay out of telemetry by default.\n *\n * In `speed: 'instant'`, dwell collapses to 0 ms but the action still fires\n * so observability stays consistent.\n *\n * Returns a {@link ReadResult} with the word count, final kind (after\n * auto-detection), and total dwell duration — useful for assertions in\n * tests or for surfacing reading metadata to a UI.\n */\n read(target: ReadTarget, options?: ReadOptions): Promise<ReadResult>;\n /**\n * Scroll the page (or a scrollable container) humanly. Multi-segment\n * motion with a bell-curve velocity profile, optional mid-scroll\n * micro-pauses, and (for the `distracted` personality) the occasional\n * overshoot + correction.\n *\n * **Targets:**\n * - `'natural'` (default): scroll one viewport in the chosen axis\n * - `'end'` / `'top'`: jump to the document/container edges, humanized\n * - `string`: a Playwright-compatible selector — scroll until in view\n * - `Locator`: same, but with a pre-built handle\n * - `{ by: n }`: relative pixel delta (negative = up / left)\n * - `{ to: n }`: absolute scroll position on the chosen axis\n *\n * **Options:** `axis: 'x' | 'y'` (default `'y'`) picks the direction;\n * `within: selector | Locator` scopes the scroll to a scrollable\n * container; `block: 'start' | 'center' | 'end' | 'nearest'` aligns\n * element targets; `overshoot` / `withPauses` toggle individual\n * humanization signals.\n *\n * Plugins observe `'scroll'` actions with `{ target }` in\n * `beforeAction`'s params (a human-readable description of the target).\n * The full {@link ScrollResult} (`from` / `to` / `distance` /\n * `durationMs`) is available via `afterAction`'s `result` argument.\n *\n * In `speed: 'instant'`, the page (or container) is moved with a single\n * `scrollTo` call. No wheel events, no segments — but the action still\n * fires for observability.\n *\n * Returns a {@link ScrollResult} for assertions in tests.\n */\n scroll(target?: ScrollTarget, options?: ScrollOptions): Promise<ScrollResult>;\n}\n\n/**\n * Creates a humanized session bound to a Playwright `Page`.\n *\n * @example\n * ```ts\n * import { chromium } from 'playwright';\n * import { createHuman } from '@humanjs/playwright';\n *\n * const browser = await chromium.launch();\n * const page = await browser.newPage();\n *\n * const human = await createHuman(page, {\n * personality: 'careful',\n * seed: 'session-42',\n * });\n *\n * await human.goto('https://example.com');\n * ```\n */\nexport async function createHuman(page: Page, options: CreateHumanOptions = {}): Promise<Human> {\n const personality = resolvePersonality(options.personality ?? 'careful');\n const rng = createRng(options.seed);\n const speed = options.speed ?? 'human';\n const plugins = options.plugins ?? [];\n\n const context: PluginContext = { personality, rng };\n for (const plugin of plugins) {\n await plugin.install?.(context);\n }\n\n async function performAction<T>(action: HumanAction, actionFn: () => Promise<T>): Promise<T> {\n for (const plugin of plugins) {\n await plugin.beforeAction?.(action);\n }\n const startedAt = Date.now();\n try {\n const value = await actionFn();\n const result: ActionResult = {\n type: action.type,\n durationMs: Date.now() - startedAt,\n };\n for (const plugin of plugins) {\n await plugin.afterAction?.(action, result);\n }\n return value;\n } catch (error) {\n for (const plugin of plugins) {\n await plugin.onError?.(action, error);\n }\n throw error;\n }\n }\n\n let lastMousePosition: Point = options.initialMousePosition ?? { x: 0, y: 0 };\n\n return {\n personality,\n speed,\n async goto(url) {\n await performAction({ type: 'goto', params: { url } }, async () => {\n await page.goto(url);\n });\n },\n async click(target) {\n const description = typeof target === 'string' ? target : (target.toString?.() ?? 'locator');\n await performAction({ type: 'click', params: { target: description } }, async () => {\n await executeClick(target, {\n page,\n personality,\n rng,\n speed,\n getMousePosition: () => lastMousePosition,\n setMousePosition: (point) => {\n lastMousePosition = point;\n },\n });\n });\n },\n async type(target, value) {\n const description = typeof target === 'string' ? target : (target.toString?.() ?? 'locator');\n // `value` itself is intentionally not echoed into params — typed input may\n // be sensitive (passwords, tokens). Expose length only for observability.\n await performAction(\n { type: 'type', params: { target: description, length: value.length } },\n async () => {\n await executeType(target, value, { page, personality, rng, speed });\n },\n );\n },\n async read(target, options) {\n const description = describeReadTarget(target);\n // Same privacy posture as `type`: never echo arbitrary content into\n // action params. `target` description, words (when known up front), and\n // kind are inert metadata; the text itself never lands here.\n return performAction(\n {\n type: 'read',\n params: {\n target: description,\n kind: options?.kind,\n },\n },\n () =>\n executeRead(\n target,\n {\n page,\n personality,\n rng,\n speed,\n // Read shares the session's tracked cursor position so an eye\n // scan starts from where the last click left off, and the next\n // click starts from where the scan ended.\n getMousePosition: () => lastMousePosition,\n setMousePosition: (point) => {\n lastMousePosition = point;\n },\n },\n options,\n ),\n );\n },\n async scroll(target, options) {\n const description = describeScrollTarget(target);\n return performAction(\n {\n type: 'scroll',\n params: { target: description },\n },\n () => executeScroll(target, { page, personality, rng, speed }, options),\n );\n },\n };\n}\n\n/**\n * Human-readable description of a scroll target for action params. Strings\n * (presets + selectors) pass through; objects describe their shape; the\n * Locator branch falls back to `toString()`.\n */\nfunction describeScrollTarget(target: ScrollTarget | undefined): string {\n if (target === undefined) return 'natural';\n if (typeof target === 'string') return target;\n if ('by' in target) return `by:${target.by}`;\n if ('to' in target) return `to:${target.to}`;\n return target.toString?.() ?? 'locator';\n}\n\n/**\n * Human-readable description of a read target for action params. Echoes\n * selectors and `{ words }` (both inert); abbreviates literal text to its\n * length so we never expose content even via accidental logging.\n */\nfunction describeReadTarget(target: ReadTarget): string {\n if (typeof target === 'string') return target;\n if ('words' in target && typeof target.words === 'number') return `${target.words} words`;\n if ('text' in target && typeof target.text === 'string') {\n return `text:${target.text.length} chars`;\n }\n return target.toString?.() ?? 'locator';\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/timing.ts","../src/keyboard/index.ts","../src/internal/mouse-walk.ts","../src/mouse/index.ts","../src/reading/index.ts","../src/recording/index.ts","../src/recording/capture.ts","../src/scroll/index.ts","../src/mouse-helper/index.ts","../src/index.ts"],"names":["planTypeKeystrokes","box","bezierPath","humanizePath","countWords","computeReadingDwellMs","planReadingScan","rmSync","ffmpegStatic","mkdir","dirname","extname","writeFile","spawn","mkdtemp","join","tmpdir","path","sleep","rm","clamp","planScroll","resolvePersonality","createRng","options"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Personality, Rng } from '@humanjs/core';\nimport type { Speed } from '..';\n\n/**\n * Sleeps for `ms` milliseconds. Resolves immediately if `ms <= 0` so callers\n * can hand the result of `computeDwellTime` straight through without guards.\n */\nexport function sleep(ms: number): Promise<void> {\n return ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();\n}\n\n/**\n * Per-mode timing multiplier applied on top of personality.speed.\n * - `'human'`: 1 — full humanization\n * - `'fast'`: 0.5 — accelerated humanization (still humanized, just brisk)\n * - `'instant'`: 0 — no delays (CI / snapshot mode)\n */\nexport function speedModeFactor(speed: Speed): number {\n switch (speed) {\n case 'fast':\n return 0.5;\n case 'instant':\n return 0;\n default:\n return 1;\n }\n}\n\n/**\n * Computes a dwell time in ms with deterministic jitter and personality scaling.\n *\n * base = meanMs\n * jitter = base × jitterFraction × rand[-1, 1]\n * total = (base + jitter) × personality.speed × speedModeFactor(speed)\n *\n * Returns 0 for non-positive inputs so callers can skip the sleep entirely.\n */\nexport function computeDwellTime(\n meanMs: number,\n jitter: number,\n personality: Personality,\n speed: Speed,\n rng: Rng,\n): number {\n if (meanMs <= 0) return 0;\n const jitterMag = meanMs * jitter;\n const offset = rng.nextFloat(-jitterMag, jitterMag);\n return Math.max(0, (meanMs + offset) * personality.speed * speedModeFactor(speed));\n}\n","import { type Personality, planTypeKeystrokes, type Rng } from '@humanjs/core';\nimport type { Locator, Page } from 'playwright';\nimport type { Speed } from '../index';\nimport { sleep, speedModeFactor } from '../internal/timing';\n\n/** Runtime dependencies for a humanized keyboard action. */\nexport interface KeyboardContext {\n readonly page: Page;\n readonly personality: Personality;\n readonly rng: Rng;\n readonly speed: Speed;\n}\n\n/** Result of a typing action, returned to the caller for observability. */\nexport interface TypeResult {\n /** Number of characters in the input string. */\n readonly characters: number;\n /** Number of typos injected (with or without correction). */\n readonly typos: number;\n /** Number of typo corrections via Backspace. */\n readonly corrections: number;\n}\n\n/**\n * Executes a humanized typing pass over `value` on `target`.\n *\n * Planning (which keys to press, with what delays, in what order) is delegated\n * to `@humanjs/core`'s `planTypeKeystrokes`. This module is the thin Playwright\n * dispatcher: it focuses the target, walks the plan, and chooses the best\n * Playwright API per key:\n *\n * - Named keys (`Enter`, `Backspace`, `Tab`) and ASCII chars: `keyboard.press`\n * — fires keydown/press/up, so handlers (autocomplete, validation) run.\n * - Non-ASCII characters: `keyboard.insertText` — fires `input` events but\n * not keyboard events, since `keyboard.press` is keyboard-layout-aware\n * and can't reliably synthesize characters like `é` or `🎉` on every layout.\n *\n * In `speed: 'instant'`, the whole humanized loop is bypassed in favor of\n * `locator.pressSequentially(value, { delay: 0 })` — events still fire,\n * humanization is skipped.\n */\nexport async function executeType(\n target: Locator | string,\n value: string,\n ctx: KeyboardContext,\n): Promise<TypeResult> {\n const locator = typeof target === 'string' ? ctx.page.locator(target) : target;\n\n if (value.length === 0) {\n return { characters: 0, typos: 0, corrections: 0 };\n }\n\n if (ctx.speed === 'instant') {\n await locator.pressSequentially(value, { delay: 0 });\n return { characters: value.length, typos: 0, corrections: 0 };\n }\n\n await locator.focus();\n\n const plan = planTypeKeystrokes(value, ctx.personality.typing, ctx.rng, {\n personalitySpeed: ctx.personality.speed,\n speedFactor: speedModeFactor(ctx.speed),\n });\n\n let typos = 0;\n let corrections = 0;\n\n for (const step of plan) {\n if (step.delayBeforeMs > 0) await sleep(step.delayBeforeMs);\n await dispatchKey(ctx.page, step.key);\n if (step.isTypo) typos++;\n if (step.isCorrection) corrections++;\n }\n\n return { characters: value.length, typos, corrections };\n}\n\n/**\n * Dispatches a single planned key. Multi-char strings are treated as named\n * keys (`Enter`, `Backspace`). Single ASCII chars use the layout-aware\n * `keyboard.press`. Single non-ASCII chars fall back to `insertText` so the\n * adapter works on any keyboard layout, at the cost of skipping per-key events.\n */\nasync function dispatchKey(page: Page, key: string): Promise<void> {\n if (key.length > 1 || key.charCodeAt(0) < 128) {\n await page.keyboard.press(key);\n } else {\n await page.keyboard.insertText(key);\n }\n}\n","import type { Point } from '@humanjs/core';\nimport type { Page } from 'playwright';\nimport { sleep } from './timing';\n\n/**\n * Walks the mouse along `path` over `durationMs`, evenly spacing the steps.\n * Generic helper used by both the click executor (which derives `durationMs`\n * from `personality.mouse.travelTimeMs`) and the read executor (which uses\n * the reading dwell duration so the cursor finishes its scan exactly as the\n * dwell elapses).\n *\n * - Empty path is a no-op.\n * - `durationMs <= 0` jumps through the path without inter-step delays\n * (matches `speed: 'instant'` behavior).\n */\nexport async function walkMouseAlongPath(\n page: Page,\n path: readonly Point[],\n durationMs: number,\n): Promise<void> {\n if (path.length === 0) return;\n const stepDelayMs = path.length > 1 && durationMs > 0 ? durationMs / (path.length - 1) : 0;\n\n for (let i = 0; i < path.length; i++) {\n const point = path[i];\n if (!point) continue;\n await page.mouse.move(point.x, point.y);\n if (i < path.length - 1 && stepDelayMs > 0) {\n await sleep(stepDelayMs);\n }\n }\n}\n","import { bezierPath, humanizePath, type Personality, type Point, type Rng } from '@humanjs/core';\nimport type { Locator, Page } from 'playwright';\nimport type { Speed } from '../index';\nimport { walkMouseAlongPath } from '../internal/mouse-walk';\nimport { computeDwellTime, sleep, speedModeFactor } from '../internal/timing';\n\n/** Runtime dependencies for a humanized mouse action. */\nexport interface MouseContext {\n readonly page: Page;\n readonly personality: Personality;\n readonly rng: Rng;\n readonly speed: Speed;\n /** Last known mouse position — used as the path start. */\n readonly getMousePosition: () => Point;\n /** Updates the last known mouse position after a move/click. */\n readonly setMousePosition: (point: Point) => void;\n}\n\n/** Result of a click action, returned to the caller for observability. */\nexport interface ClickResult {\n /** Coordinates the click landed at. */\n readonly target: Point;\n}\n\n/**\n * Executes a humanized click on a target locator.\n *\n * Steps:\n * 1. Resolve the target's bounding box.\n * 2. Pick a point inside it — Gaussian-centered so we never click dead-center,\n * which is itself a bot signal.\n * 3. Generate a Bezier path from the current mouse position to the target.\n * 4. Apply velocity profile + micro-jitter via `humanizePath`.\n * 5. Walk the mouse along the path with timing scaled by personality + speed.\n * 6. Click at the target coordinates.\n *\n * In `speed: 'instant'`, all humanization is bypassed and Playwright's\n * native `locator.click()` is used directly.\n */\nexport async function executeClick(\n target: Locator | string,\n ctx: MouseContext,\n): Promise<ClickResult> {\n const locator = typeof target === 'string' ? ctx.page.locator(target) : target;\n\n if (ctx.speed === 'instant') {\n // Read the bounding box BEFORE the click — the click may navigate away\n // or remove the element, after which `boundingBox()` returns null.\n const box = await locator.boundingBox();\n await locator.click();\n const center = box\n ? { x: box.x + box.width / 2, y: box.y + box.height / 2 }\n : ctx.getMousePosition();\n ctx.setMousePosition(center);\n return { target: center };\n }\n\n const box = await locator.boundingBox();\n if (!box) {\n throw new Error(\n `Cannot click: element not found or has no bounding box (target: ${describeTarget(target)})`,\n );\n }\n\n const targetPoint = pickClickPoint(box, ctx.rng);\n const startPoint = ctx.getMousePosition();\n\n const rawPath = bezierPath(startPoint, targetPoint, ctx.rng, {\n curvature: ctx.personality.mouse.curvature,\n });\n const path = humanizePath(rawPath, ctx.rng);\n\n const travelMs = computeTravelTime(path, ctx.personality, ctx.speed, ctx.rng);\n await walkMouseAlongPath(ctx.page, path, travelMs);\n\n // Hover dwell — a real user briefly settles on the target before clicking.\n const preClickMs = computeDwellTime(\n ctx.personality.dwell.preClickMs,\n ctx.personality.dwell.preClickJitter,\n ctx.personality,\n ctx.speed,\n ctx.rng,\n );\n if (preClickMs > 0) await sleep(preClickMs);\n\n // Commit the new position BEFORE the click side-effect. If the click throws\n // (page closed, target removed mid-flight), the next action still starts\n // from the correct mouse position.\n ctx.setMousePosition(targetPoint);\n await ctx.page.mouse.click(targetPoint.x, targetPoint.y);\n\n // Post-action dwell — a beat after the click before the next action.\n const postActionMs = computeDwellTime(\n ctx.personality.dwell.postActionMs,\n ctx.personality.dwell.postActionJitter,\n ctx.personality,\n ctx.speed,\n ctx.rng,\n );\n if (postActionMs > 0) await sleep(postActionMs);\n\n return { target: targetPoint };\n}\n\n/** Bounding box returned by Playwright's `Locator.boundingBox()`. */\ninterface BoundingBox {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Picks a click point inside the bounding box. Gaussian-centered so the\n * majority of clicks land near the visual center but with natural spread.\n * Clamped to never fall outside the element.\n */\nfunction pickClickPoint(box: BoundingBox, rng: Rng): Point {\n const cx = box.x + box.width / 2;\n const cy = box.y + box.height / 2;\n // Spread is 1/8 of each dimension — most clicks fall within the middle\n // half of the element, but we never click dead-center deterministically.\n const x = clamp(cx + rng.nextGaussian(0, box.width / 8), box.x, box.x + box.width);\n const y = clamp(cy + rng.nextGaussian(0, box.height / 8), box.y, box.y + box.height);\n return { x, y };\n}\n\n/**\n * Travel time in ms for the given path.\n *\n * base = (totalDistance / 1000) * Personality.mouse.travelTimeMs\n * jitter = base * Personality.mouse.travelTimeJitter * rand[-1, 1]\n * total = (base + jitter) * Personality.speed * speedModeFactor\n */\nfunction computeTravelTime(\n path: readonly Point[],\n personality: Personality,\n speed: Speed,\n rng: Rng,\n): number {\n let distance = 0;\n for (let i = 1; i < path.length; i++) {\n const prev = path[i - 1];\n const curr = path[i];\n if (!prev || !curr) continue;\n distance += Math.hypot(curr.x - prev.x, curr.y - prev.y);\n }\n\n const baseTime = (distance / 1000) * personality.mouse.travelTimeMs;\n const jitterMag = baseTime * personality.mouse.travelTimeJitter;\n const jitter = rng.nextFloat(-jitterMag, jitterMag);\n const total = (baseTime + jitter) * personality.speed * speedModeFactor(speed);\n return Math.max(0, total);\n}\n\nfunction describeTarget(target: Locator | string): string {\n return typeof target === 'string' ? target : (target.toString?.() ?? 'locator');\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return value < min ? min : value > max ? max : value;\n}\n","import {\n computeReadingDwellMs,\n countWords,\n type Personality,\n type Point,\n planReadingScan,\n type ReadKind,\n type Rng,\n} from '@humanjs/core';\nimport type { Locator, Page } from 'playwright';\nimport type { Speed } from '../index';\nimport { walkMouseAlongPath } from '../internal/mouse-walk';\nimport { sleep, speedModeFactor } from '../internal/timing';\n\n/** Runtime dependencies for a humanized reading dwell. */\nexport interface ReadingContext {\n readonly page: Page;\n readonly personality: Personality;\n readonly rng: Rng;\n readonly speed: Speed;\n /**\n * Last known cursor position. Used as the starting point for an eye-scan\n * when `withMotion: true`. Required even when motion is off — the read\n * adapter needs to know where the cursor *would* start if motion is later\n * enabled mid-session.\n */\n readonly getMousePosition: () => Point;\n /** Updates the tracked cursor position after a scan completes. */\n readonly setMousePosition: (point: Point) => void;\n}\n\n/**\n * What to read:\n * - `string`: a Playwright-compatible selector (matches `click()` / `type()`).\n * The element's `innerText` is resolved and word-counted.\n * - `Locator`: same, but you already have a Locator handle.\n * - `{ text }`: literal text (you have the string in hand, no selector).\n * - `{ words }`: pre-counted (bypass extraction entirely).\n */\nexport type ReadTarget = string | Locator | { readonly text: string } | { readonly words: number };\n\nexport interface ReadOptions {\n /**\n * Reading mode. Built-in multipliers on top of `personality.reading.wpm`:\n * - `'prose'`: 1.0× (default for non-code targets)\n * - `'code'`: 0.4× (default when target is a `<pre>` or `<code>` element)\n * - `'scan'`: 1.8× (skim mode, must be explicit)\n *\n * **Smart defaults:** when `kind` is omitted AND the target is a Locator/\n * selector, the adapter inspects the resolved element's tag — `<pre>` and\n * `<code>` auto-detect as `'code'`; everything else as `'prose'`. An\n * explicit `kind` always wins over auto-detection.\n */\n readonly kind?: ReadKind;\n /** Override the effective WPM multiplier directly. Wins over `kind`. */\n readonly wpmMultiplier?: number;\n /**\n * For selector/Locator targets: scroll the element into the viewport\n * before the dwell. A user can't read what isn't on screen. Defaults to\n * `false` — in most flows the caller has already scrolled there.\n */\n readonly scrollIntoView?: boolean;\n /**\n * For selector/Locator targets: trace a humanized cursor path through the\n * target's bounding box while the dwell elapses, like an eye tracking\n * lines of text. **Defaults to `true`** — \"reading\" implies looking, and\n * looking implies motion. Pass `false` to skip motion when you only\n * care about the temporal pattern (typical AI-agent use case where\n * the cursor position is irrelevant).\n *\n * The scan path is generated by `planReadingScan` from `@humanjs/core`\n * (same deterministic-by-seed contract as the rest of the library) and\n * walked over the computed dwell duration, so motion finishes exactly as\n * the read does. The cursor's tracked position is updated to the scan's\n * endpoint so the next click starts from where the eye landed.\n *\n * Ignored when the target is `{ text }` or `{ words }` (no bounding box).\n */\n readonly withMotion?: boolean;\n}\n\n/** Outcome of a read, returned to the caller for observability. */\nexport interface ReadResult {\n /** Number of words counted (after whitespace splitting / from caller's `{ words }`). */\n readonly words: number;\n /** Sleep duration in ms. Zero in `speed: 'instant'` or for zero-word inputs. */\n readonly durationMs: number;\n /** Final reading kind used (after auto-detection + explicit-override resolution). */\n readonly kind: ReadKind;\n}\n\n/**\n * Executes a humanized reading dwell.\n *\n * Flow:\n * 1. Resolve target → words + (optional) Locator\n * 2. Optionally scroll into view\n * 3. Resolve `kind` (explicit → auto-detected from tag → `'prose'`)\n * 4. Compute dwell via `computeReadingDwellMs` (jittered + scaled)\n * 5. Walk a humanized eye-scan path across the bounding box (default on),\n * pacing the steps so motion ends exactly as the dwell does\n */\nexport async function executeRead(\n target: ReadTarget,\n ctx: ReadingContext,\n options: ReadOptions = {},\n): Promise<ReadResult> {\n // ── Resolve target → words (+ optional Locator for tag detection / scroll) ─\n let words = 0;\n let locator: Locator | undefined;\n\n if (typeof target === 'string') {\n locator = ctx.page.locator(target);\n } else if ('words' in target) {\n words = target.words;\n } else if ('text' in target) {\n words = countWords(target.text);\n } else {\n // Only Locator remains in the union after the discriminator checks above.\n locator = target;\n }\n\n let autoDetectedKind: ReadKind | undefined;\n\n if (locator) {\n if (options.scrollIntoView) {\n await locator.scrollIntoViewIfNeeded();\n }\n const text = await locator.innerText().catch(() => '');\n words = countWords(text);\n\n // Auto-detect kind from tag only when caller didn't specify one.\n if (options.kind === undefined) {\n autoDetectedKind = await detectKindFromTag(locator);\n }\n }\n\n const kind = options.kind ?? autoDetectedKind ?? 'prose';\n\n const durationMs = computeReadingDwellMs(words, ctx.personality.reading, ctx.rng, {\n kind,\n wpmMultiplier: options.wpmMultiplier,\n personalitySpeed: ctx.personality.speed,\n speedFactor: speedModeFactor(ctx.speed),\n });\n\n const withMotion = options.withMotion ?? true;\n if (withMotion && locator && durationMs > 0) {\n // Walk a humanized scan path across the rendered text over the dwell\n // duration. `walkMouseAlongPath` paces the steps so motion ends exactly\n // as the read does — no extra sleep needed.\n const box = await locator.boundingBox().catch(() => null);\n if (box) {\n // Per-line rects make sweeps land on actual text edges, not the\n // element's full width — important for `<pre>` / `<code>` blocks\n // where individual lines are far narrower than the container.\n const lineRects = await getLineRects(locator).catch(() => []);\n const path = planReadingScan(box, ctx.rng, {\n start: ctx.getMousePosition(),\n lineRects: lineRects.length > 0 ? lineRects : undefined,\n });\n await walkMouseAlongPath(ctx.page, path, durationMs);\n const final = path[path.length - 1];\n if (final) ctx.setMousePosition(final);\n return { words, durationMs, kind };\n }\n // No box → fall through to a plain sleep (rare but possible if the\n // element disappeared between innerText() and boundingBox()).\n }\n\n if (durationMs > 0) await sleep(durationMs);\n\n return { words, durationMs, kind };\n}\n\n// ────────── helpers ──────────\n\n/**\n * Returns one rect per visible line of *text* inside the element, in\n * viewport coordinates (same space as `locator.boundingBox()`).\n *\n * Walks text-node descendants and collects each text node's line rects via\n * `Range.getClientRects()`. This is the only way to get tight per-line\n * geometry when the target contains block-level descendants — calling\n * `selectNodeContents` over a `<ul>` or `<div>` would return the block's\n * content box (full container width), not the rendered text edges.\n *\n * Rects on the same visual line (e.g. text broken by `<strong>` / `<em>`)\n * are merged so the planner sweeps each line as a single L→R stroke\n * rather than zigzagging through inline siblings.\n */\nasync function getLineRects(\n locator: Locator,\n): Promise<Array<{ x: number; y: number; width: number; height: number }>> {\n const result = await locator.evaluate((el) => {\n type Rect = { x: number; y: number; width: number; height: number };\n // `4` === `NodeFilter.SHOW_TEXT`. Hardcoded so we don't depend on the\n // global `NodeFilter` being in the Node-side TS lib.\n const walker = el.ownerDocument.createTreeWalker(el, 4);\n const rects: Rect[] = [];\n let node = walker.nextNode();\n while (node) {\n const text = node.textContent ?? '';\n if (text.trim().length > 0) {\n const range = el.ownerDocument.createRange();\n range.selectNodeContents(node);\n for (const r of Array.from(range.getClientRects())) {\n if (r.width > 0 && r.height > 0) {\n rects.push({ x: r.x, y: r.y, width: r.width, height: r.height });\n }\n }\n }\n node = walker.nextNode();\n }\n\n // Merge rects that sit on the same visual line — text broken by inline\n // siblings (\"hello <strong>bold</strong> world\") produces three rects\n // with the same y. Merged, that becomes a single L→R stroke across the\n // full line of text.\n rects.sort((a, b) => a.y - b.y || a.x - b.x);\n const merged: Rect[] = [];\n for (const r of rects) {\n const last = merged[merged.length - 1];\n if (last && Math.abs(last.y - r.y) < 1 && r.x - (last.x + last.width) < 6) {\n const right = Math.max(last.x + last.width, r.x + r.width);\n const bottom = Math.max(last.y + last.height, r.y + r.height);\n last.width = right - last.x;\n last.height = bottom - last.y;\n } else {\n merged.push({ ...r });\n }\n }\n return merged;\n });\n // Defensive: validate the evaluate result is an array of rect-shaped\n // objects before handing it to the planner. Cheap, and keeps the planner\n // from ever seeing junk from a mocked evaluate or an exotic test harness.\n if (!Array.isArray(result)) return [];\n return result.filter(\n (r): r is { x: number; y: number; width: number; height: number } =>\n r != null &&\n typeof r === 'object' &&\n typeof (r as { x?: unknown }).x === 'number' &&\n typeof (r as { y?: unknown }).y === 'number' &&\n typeof (r as { width?: unknown }).width === 'number' &&\n typeof (r as { height?: unknown }).height === 'number',\n );\n}\n\n/**\n * Inspects the element's tag for the smart default. Only the narrowest\n * heuristic (literal `<pre>` / `<code>` tagName) — CSS class sniffing would\n * be too magical. Returns undefined for anything else, so the caller's\n * resolution chain falls back to `'prose'`.\n */\nasync function detectKindFromTag(locator: Locator): Promise<ReadKind | undefined> {\n // `evaluate` runs the function in the browser context; Playwright's types\n // give the callback a DOM Element here without needing the lib.dom ref\n // on the Node-side tsconfig.\n const tag = await locator.evaluate((el) => el.tagName?.toLowerCase() ?? '').catch(() => '');\n if (tag === 'pre' || tag === 'code') return 'code';\n return undefined;\n}\n","import { spawn } from 'node:child_process';\nimport { rmSync } from 'node:fs';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, extname } from 'node:path';\nimport ffmpegStatic from 'ffmpeg-static';\nimport type { CaptureResult } from './capture';\n\n// Safety net for captured-frame temp dirs: every live Recording registers\n// its dir here, and a single lazy `process.on('exit')` handler sweeps\n// whatever's left when the process ends. The OS would eventually reap\n// `os.tmpdir()` anyway, but cleaning on exit avoids the \"100 demo runs and\n// now /tmp has 20GB of frames\" failure mode. Explicit `dispose()` removes\n// from this set so we don't double-clean.\nconst pendingFrameCleanups = new Set<string>();\nlet exitHandlerInstalled = false;\n\nfunction ensureExitHandler(): void {\n if (exitHandlerInstalled) return;\n exitHandlerInstalled = true;\n process.on('exit', () => {\n for (const dir of pendingFrameCleanups) {\n try {\n // Sync rm — `process.on('exit')` can't await Promises. The dirs are\n // small (frames already encoded into the user's output by now in\n // most cases), so the blocking shutdown cost is negligible.\n rmSync(dir, { recursive: true, force: true });\n } catch {\n // Swallow — exit-phase cleanup can race the OS or hit a perms\n // issue we can't recover from anyway. Worst case: the OS reaps\n // tmpdir on its own schedule.\n }\n }\n pendingFrameCleanups.clear();\n });\n}\n\n// `ffmpeg-static` returns the absolute path of a bundled ffmpeg binary at\n// require time, or `null` on platforms it can't support. We resolve once.\nconst FFMPEG_PATH = ffmpegStatic as unknown as string | null;\n\n/**\n * Encoding quality preset. Picks the per-frame capture quality + the\n * ffmpeg encode settings used to assemble them into a video.\n *\n * - `'fast'` — JPEG q=85, CRF 23, preset fast (iteration)\n * - `'standard'` — JPEG q=90, CRF 20, preset fast (balanced)\n * - `'high'` (default) — JPEG q=95, CRF 18, preset slow, tune animation (marketing-grade)\n * - `'lossless'` — PNG capture, CRF 12, preset veryslow (archival; huge temp files)\n */\nexport type RecordingQuality = 'fast' | 'high' | 'lossless' | 'standard';\n\n/** ffmpeg `-preset` values, ordered from fastest to slowest. */\nexport type FfmpegPreset =\n | 'fast'\n | 'faster'\n | 'medium'\n | 'slow'\n | 'slower'\n | 'superfast'\n | 'ultrafast'\n | 'veryfast'\n | 'veryslow';\n\n/** ffmpeg `-tune` values for libx264. */\nexport type FfmpegTune =\n | 'animation'\n | 'fastdecode'\n | 'film'\n | 'grain'\n | 'stillimage'\n | 'zerolatency';\n\ninterface QualityPreset {\n readonly crf: number;\n readonly preset: FfmpegPreset;\n readonly tune?: FfmpegTune;\n /** Per-frame capture format used to source the frames. */\n readonly captureFormat: 'jpeg' | 'png';\n readonly captureJpegQuality: number;\n /** Target capture FPS — the polling rate for `page.screenshot()`. */\n readonly captureFps: number;\n}\n\nconst QUALITY_PRESETS: Record<RecordingQuality, QualityPreset> = {\n fast: {\n captureFormat: 'jpeg',\n captureJpegQuality: 85,\n captureFps: 24,\n crf: 23,\n preset: 'fast',\n },\n standard: {\n captureFormat: 'jpeg',\n captureJpegQuality: 90,\n captureFps: 30,\n crf: 20,\n preset: 'fast',\n },\n high: {\n captureFormat: 'jpeg',\n captureJpegQuality: 95,\n captureFps: 30,\n crf: 18,\n preset: 'slow',\n // 'animation' suits screen content (large solid regions, sharp edges)\n // better than 'film' which is tuned for live-action grain.\n tune: 'animation',\n },\n lossless: {\n // PNG capture for perceptually lossless source frames. Temp files are\n // 10-20× larger than JPEG; output mp4 still benefits from the extra\n // headroom (no JPEG artifacts to preserve).\n captureFormat: 'png',\n captureJpegQuality: 100,\n captureFps: 30,\n crf: 12,\n preset: 'veryslow',\n tune: 'animation',\n },\n};\n\n/** Returns the capture-side settings for a quality preset. */\nexport function getCaptureSettingsForQuality(quality: RecordingQuality): {\n readonly format: 'jpeg' | 'png';\n readonly quality: number;\n readonly fps: number;\n} {\n const preset = QUALITY_PRESETS[quality];\n return {\n format: preset.captureFormat,\n quality: preset.captureJpegQuality,\n fps: preset.captureFps,\n };\n}\n\n/** Options for {@link Recording.toVideo}. */\nexport interface ToVideoOptions {\n /** Encoding quality preset. Defaults to `'high'`. */\n readonly quality?: RecordingQuality;\n /** Override CRF (0–51, lower = better). Defaults to the quality preset's CRF. */\n readonly crf?: number;\n /** Override ffmpeg `-preset`. Defaults to the quality preset's preset. */\n readonly preset?: FfmpegPreset;\n /** Override ffmpeg `-tune`. Defaults to the quality preset's tune (if any). */\n readonly tune?: FfmpegTune;\n}\n\n/** Options for {@link Recording.toGif}. */\nexport interface ToGifOptions {\n /**\n * Frames per second of the output GIF. Defaults to `15` — high enough for\n * smooth motion in most renderers, low enough to keep file size sane.\n * Renderers cap GIF playback around 50fps anyway.\n */\n readonly fps?: number;\n /**\n * Scale the GIF to this width (pixels). Height is computed to preserve\n * aspect ratio. Omit to keep the source viewport size — but most embedded\n * GIFs (README, PR, Slack) look fine at 640–960px and weigh a fraction.\n */\n readonly width?: number;\n}\n\n/** One action captured during a recording, as emitted in {@link Timeline.events}. */\nexport interface TimelineEvent {\n readonly type: string;\n readonly params: Readonly<Record<string, unknown>>;\n /** Offset (ms) from the recording start when the action began. */\n readonly tMs: number;\n readonly durationMs: number;\n /** Error message, present only if the action threw. */\n readonly error?: string;\n}\n\n/** Structured action timeline of a recording. */\nexport interface Timeline {\n readonly version: 1;\n readonly personality: string;\n readonly seed: string | null;\n readonly speed: string;\n readonly durationMs: number;\n readonly events: readonly TimelineEvent[];\n}\n\n/** Metadata passed from `human.record()` into the Recording constructor. */\nexport interface RecordingTimelineSource {\n readonly personality: string;\n readonly seed: string | null;\n readonly speed: string;\n readonly events: readonly TimelineEvent[];\n}\n\n/**\n * A recorded window of a humanized session. Returned by `human.record(cb)`.\n *\n * A Recording can hold a frame capture (`hasVideo === true`) OR be\n * timeline-only (`hasVideo === false`). `toVideo()` / `toGif()` require a\n * capture; `toTimeline()` and `.timeline` work either way.\n *\n * The video exporters are repeatable and interleavable — they read the\n * captured frames without consuming them. Calling `dispose()` is OPTIONAL:\n * the captured-frames temp dir is also swept by a `process.on('exit')`\n * handler installed on first use, so casual scripts can skip it entirely.\n * Call `dispose()` (or use `await using`) when you want to release the\n * frames proactively — long-running services, batch jobs, etc.\n */\nexport class Recording {\n readonly #capture: CaptureResult | null;\n readonly #windowStartMs: number;\n readonly #windowEndMs: number;\n readonly #timelineSource: RecordingTimelineSource;\n // Frames live on disk until `dispose()` is called. Exporters\n // (`toVideo`, `toGif`) are repeatable and interleavable — they read the\n // same frame source, they don't consume it.\n #disposed = false;\n\n constructor(\n capture: CaptureResult | null,\n windowStartMs: number,\n windowEndMs: number,\n timelineSource: RecordingTimelineSource,\n ) {\n this.#capture = capture;\n this.#windowStartMs = windowStartMs;\n this.#windowEndMs = windowEndMs;\n this.#timelineSource = timelineSource;\n\n // Register the captured-frames dir for sweep-on-exit. If the user\n // forgets to call `dispose()`, the process-exit handler still cleans\n // it up — so casual scripts don't have to think about lifecycle.\n if (capture !== null) {\n pendingFrameCleanups.add(capture.dir);\n ensureExitHandler();\n }\n }\n\n /** Wall-clock duration of the recorded window. */\n get durationMs(): number {\n return this.#windowEndMs - this.#windowStartMs;\n }\n\n /** True if frames were captured during this recording. */\n get hasVideo(): boolean {\n return this.#capture !== null;\n }\n\n /**\n * The structured action timeline of this recording — same data that\n * `toTimeline()` writes to disk.\n */\n get timeline(): Timeline {\n return {\n version: 1,\n personality: this.#timelineSource.personality,\n seed: this.#timelineSource.seed,\n speed: this.#timelineSource.speed,\n durationMs: this.durationMs,\n events: this.#timelineSource.events,\n };\n }\n\n /**\n * Assembles the captured frames into a video at `outputPath`. The output\n * format is inferred from the extension — `.mp4` (H.264, re-encoded\n * with the configured quality) or `.webm` (VP9).\n *\n * Repeatable and interleavable with `toGif()` — the frame source is read,\n * not consumed. Frames live until you call `dispose()` (or `await using`\n * goes out of scope, or the process exits and the OS reaps `tmpdir`).\n *\n * @returns the resolved output path.\n */\n async toVideo(outputPath: string, options: ToVideoOptions = {}): Promise<string> {\n if (this.#disposed) {\n throw new Error('Recording.toVideo() called after dispose() — the source frames are gone.');\n }\n if (this.#capture === null) {\n throw new Error(\n 'Recording.toVideo() requires video capture, which was disabled for this recording. ' +\n 'Call `human.record(cb)` (default captures video) or pass `output` to ' +\n \"@humanjs/recorder's `record()`. `toTimeline()` and `.timeline` work without capture.\",\n );\n }\n\n const preset = QUALITY_PRESETS[options.quality ?? 'high'];\n const crf = options.crf ?? preset.crf;\n const ffmpegPreset = options.preset ?? preset.preset;\n const tune = options.tune ?? preset.tune;\n\n const { dir, frames, startedAtMs, stoppedAtMs } = this.#capture;\n if (frames.length === 0) {\n throw new Error(\n 'No frames were captured. The recording window may have been too short, ' +\n 'or the page may not have rendered any frames before the callback completed.',\n );\n }\n\n await mkdir(dirname(outputPath), { recursive: true });\n\n const ext = extname(outputPath).toLowerCase();\n if (ext !== '.mp4' && ext !== '.webm') {\n throw new Error(`Unsupported output extension: ${ext || '(none)'}. Use .mp4 or .webm.`);\n }\n\n // Use a concat-demuxer file with per-frame duration so the assembled\n // video matches the real-world capture timing. Handles uneven frame\n // intervals from the polling loop.\n const concatPath = `${dir}/concat.txt`;\n const concatBody = buildConcatFile(frames, stoppedAtMs - startedAtMs);\n await writeFile(concatPath, concatBody, 'utf8');\n\n const args: string[] = ['-y', '-f', 'concat', '-safe', '0', '-i', concatPath, '-vsync', 'vfr'];\n\n if (ext === '.mp4') {\n args.push(\n '-c:v',\n 'libx264',\n '-pix_fmt',\n 'yuv420p',\n '-crf',\n String(crf),\n '-preset',\n ffmpegPreset,\n );\n if (tune) args.push('-tune', tune);\n args.push('-movflags', '+faststart');\n } else {\n // .webm via libvpx-vp9 — lossless friendly, good gradient handling.\n args.push(\n '-c:v',\n 'libvpx-vp9',\n '-pix_fmt',\n 'yuv420p',\n '-crf',\n String(crf),\n '-b:v',\n '0',\n '-deadline',\n ffmpegPreset === 'fast' || ffmpegPreset === 'veryfast' ? 'realtime' : 'good',\n );\n }\n\n args.push(outputPath);\n await runFfmpeg(args);\n\n return outputPath;\n }\n\n /**\n * Assembles the captured frames into an animated GIF at `outputPath`.\n * Optimized for embedding in READMEs, PRs, Slack, and docs — uses a\n * per-recording palette (`palettegen` + `paletteuse`) with Bayer dithering\n * so gradients stay smooth without exploding the file size.\n *\n * Repeatable and interleavable with `toVideo()` — call them in any order,\n * any number of times. Frames live until you call `dispose()`.\n *\n * @returns the resolved output path.\n */\n async toGif(outputPath: string, options: ToGifOptions = {}): Promise<string> {\n if (this.#disposed) {\n throw new Error('Recording.toGif() called after dispose() — the source frames are gone.');\n }\n if (this.#capture === null) {\n throw new Error(\n 'Recording.toGif() requires video capture, which was disabled for this recording. ' +\n 'Call `human.record(cb)` (default captures video) or pass `output` to ' +\n \"@humanjs/recorder's `record()`. `toTimeline()` and `.timeline` work without capture.\",\n );\n }\n\n const fps = options.fps ?? 15;\n const width = options.width;\n\n const { dir, frames, startedAtMs, stoppedAtMs } = this.#capture;\n if (frames.length === 0) {\n throw new Error(\n 'No frames were captured. The recording window may have been too short, ' +\n 'or the page may not have rendered any frames before the callback completed.',\n );\n }\n\n await mkdir(dirname(outputPath), { recursive: true });\n\n const ext = extname(outputPath).toLowerCase();\n if (ext !== '.gif') {\n throw new Error(`Unsupported output extension: ${ext || '(none)'}. Use .gif.`);\n }\n\n const concatPath = `${dir}/concat.txt`;\n const concatBody = buildConcatFile(frames, stoppedAtMs - startedAtMs);\n await writeFile(concatPath, concatBody, 'utf8');\n\n // Filter graph: drop to the target fps, optionally scale, then run the\n // standard split → palettegen → paletteuse pattern. `stats_mode=diff`\n // weights changing pixels more heavily so the palette favors regions\n // that actually move; Bayer dither at scale 5 is the usual sweet spot\n // for screen content (sharp edges, large flat regions).\n const filterSteps: string[] = [`fps=${fps}`];\n if (width !== undefined) {\n filterSteps.push(`scale=${width}:-1:flags=lanczos`);\n }\n const preFilter = filterSteps.join(',');\n const filterComplex =\n `${preFilter},split [a][b]; ` +\n `[a] palettegen=stats_mode=diff [p]; ` +\n `[b][p] paletteuse=dither=bayer:bayer_scale=5`;\n\n const args: string[] = [\n '-y',\n '-f',\n 'concat',\n '-safe',\n '0',\n '-i',\n concatPath,\n '-filter_complex',\n filterComplex,\n '-loop',\n '0',\n outputPath,\n ];\n await runFfmpeg(args);\n\n return outputPath;\n }\n\n /**\n * Writes the structured action timeline to `outputPath` as JSON.\n * Independent of `toVideo()` / `toGif()` — call before, after, in between,\n * or instead. Safe to call multiple times. Unaffected by `dispose()`\n * (the timeline lives in memory, not in the captured-frames temp dir).\n *\n * @returns the resolved output path.\n */\n async toTimeline(outputPath: string): Promise<string> {\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, `${JSON.stringify(this.timeline, null, 2)}\\n`, 'utf8');\n return outputPath;\n }\n\n /**\n * Releases the captured-frames temp directory. After this call, `toVideo()`\n * and `toGif()` throw — but `toTimeline()` and the in-memory `timeline`\n * still work because those don't depend on the frames.\n *\n * **Optional.** A process-exit handler also sweeps any un-disposed frame\n * dirs, so casual scripts can skip this entirely. Call it explicitly when\n * you want to release frames proactively (long-running services, batch\n * jobs, or anywhere you want predictable disk usage).\n *\n * Idempotent. Safe to call on a Recording that never had a capture\n * (timeline-only mode) — no-op there.\n *\n * Also wired to `Symbol.asyncDispose`, so the explicit-resource-management\n * `await using` syntax (TypeScript ≥ 5.2 / Node ≥ 20.4) works:\n *\n * ```ts\n * await using rec = await human.record(fn);\n * await rec.toVideo('demo.mp4');\n * await rec.toGif('demo.gif');\n * // frames cleaned up automatically when `rec` goes out of scope\n * ```\n */\n async dispose(): Promise<void> {\n if (this.#disposed) return;\n if (this.#capture !== null) {\n await this.#capture.cleanup();\n // Only unregister from the exit-sweep AFTER cleanup succeeds — if\n // cleanup throws, the sweep stays armed as a last-resort fallback.\n pendingFrameCleanups.delete(this.#capture.dir);\n }\n this.#disposed = true;\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n}\n\n/**\n * Builds an ffmpeg concat-demuxer file describing each captured frame and\n * its duration in seconds. The last frame's duration is the gap to the\n * stop timestamp.\n */\nfunction buildConcatFile(\n frames: readonly { readonly path: string; readonly tMs: number }[],\n totalMs: number,\n): string {\n const lines: string[] = [];\n for (let i = 0; i < frames.length; i++) {\n const frame = frames[i] as { readonly path: string; readonly tMs: number };\n const next = frames[i + 1];\n const nextTMs = next ? next.tMs : totalMs;\n const durationS = Math.max(0.001, (nextTMs - frame.tMs) / 1000);\n lines.push(`file '${frame.path.replaceAll(\"'\", \"'\\\\''\")}'`);\n lines.push(`duration ${durationS.toFixed(6)}`);\n }\n // ffmpeg's concat demuxer requires the final entry repeated without\n // duration so the encoder doesn't drop the last frame.\n const last = frames[frames.length - 1];\n if (last) {\n lines.push(`file '${last.path.replaceAll(\"'\", \"'\\\\''\")}'`);\n }\n return `${lines.join('\\n')}\\n`;\n}\n\nfunction runFfmpeg(args: readonly string[]): Promise<void> {\n if (!FFMPEG_PATH) {\n return Promise.reject(\n new Error(\n 'ffmpeg-static did not bundle a binary for this platform. ' +\n 'Install system ffmpeg and set FFMPEG_PATH, or run on a supported platform.',\n ),\n );\n }\n return new Promise<void>((resolve, reject) => {\n const proc = spawn(FFMPEG_PATH, [...args]);\n let stderr = '';\n proc.stderr?.on('data', (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n proc.on('error', reject);\n proc.on('close', (code) => {\n if (code === 0) resolve();\n else reject(new Error(`ffmpeg exited with code ${code}\\n${stderr.trim()}`));\n });\n });\n}\n","import { mkdtemp, rm, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { sleep } from '@humanjs/core';\nimport type { Page } from 'playwright';\n\n/**\n * A captured frame in the timer-based capture session. `tMs` is the\n * wall-clock offset (ms) from the capture start.\n */\nexport interface CapturedFrame {\n readonly path: string;\n readonly tMs: number;\n}\n\n/** Outcome of a finalized capture session. */\nexport interface CaptureResult {\n readonly dir: string;\n readonly frames: readonly CapturedFrame[];\n readonly startedAtMs: number;\n readonly stoppedAtMs: number;\n readonly format: 'jpeg' | 'png';\n readonly fps: number;\n /**\n * Removes the temp directory + all frames. Called by `Recording.dispose()`\n * and by the sweep-on-exit handler — not by the exporters, which are\n * repeatable and read the frames without consuming them. Also called\n * directly by the error path in `human.record()` when the callback throws.\n */\n cleanup(): Promise<void>;\n}\n\n/** Options for {@link startCapture}. */\nexport interface StartCaptureOptions {\n /**\n * Per-frame compression. `'png'` is lossless but produces large temp files\n * (~5-15 MB/frame at 1080p). `'jpeg'` with `quality` is the sweet spot —\n * quality 95+ is near-imperceptible.\n *\n * Defaults to `'jpeg'`.\n */\n readonly format?: 'jpeg' | 'png';\n /** JPEG quality (0-100). Ignored for PNG. Defaults to 95. */\n readonly quality?: number;\n /**\n * Target capture rate in frames per second. The actual rate may be lower\n * if individual screenshots take longer than the per-frame budget — we\n * never pile up captures, so a slow disk just lowers effective FPS.\n * Defaults to 30.\n */\n readonly fps?: number;\n}\n\n/** Live handle returned by {@link startCapture}. */\nexport interface CaptureSession {\n /** Stops the capture loop and returns the assembled frame list. */\n stop(): Promise<CaptureResult>;\n /**\n * Aborts the capture loop without producing a CaptureResult. Used by the\n * error path so the temp dir doesn't linger.\n */\n abort(): Promise<void>;\n}\n\n/**\n * Starts a timer-based capture loop that polls `page.screenshot()` at the\n * configured FPS and writes each frame to a temp directory.\n *\n * Timer-based polling is more reliable than CDP `Page.startScreencast` for\n * the HumanJS use case: the browser only fires screencast events when it\n * re-renders, which means long `sleep()` gaps between actions show up as\n * captured-frame gaps too. Polling gives consistent FPS regardless of\n * what the page is doing.\n *\n * Caller is responsible for invoking either `stop()` (happy path) or\n * `abort()` (error path) to release resources.\n */\nexport async function startCapture(\n page: Page,\n options: StartCaptureOptions = {},\n): Promise<CaptureSession> {\n const format = options.format ?? 'jpeg';\n const quality = options.quality ?? 95;\n const fps = Math.max(1, Math.min(60, options.fps ?? 30));\n const intervalMs = 1000 / fps;\n\n const dir = await mkdtemp(join(tmpdir(), 'humanjs-capture-'));\n const frames: CapturedFrame[] = [];\n const ext = format === 'png' ? 'png' : 'jpg';\n\n let stopped = false;\n let frameIndex = 0;\n // Collects every in-flight write so stop()/abort() can wait for them to\n // settle. Using `allSettled` (in `finish`) means an individual writeFile\n // failure drops one frame but never blocks the rest of the queue or the\n // abort() cleanup path — important when disk pressure shows up mid-capture.\n const writes: Promise<unknown>[] = [];\n\n const startedAtMs = Date.now();\n\n const captureLoop = async (): Promise<void> => {\n while (!stopped) {\n const loopStart = Date.now();\n try {\n const buf = await page.screenshot({\n type: format,\n quality: format === 'jpeg' ? quality : undefined,\n });\n if (stopped) return;\n const idx = frameIndex++;\n const path = join(dir, `frame_${String(idx).padStart(6, '0')}.${ext}`);\n const tMs = loopStart - startedAtMs;\n writes.push(\n writeFile(path, buf).then(\n () => {\n frames.push({ path, tMs });\n },\n (err) => {\n // A single failed write drops one frame but doesn't poison the\n // queue — finish()'s `allSettled` keeps the rest moving and lets\n // abort() reach its `rm()` cleanup.\n console.warn(`humanjs capture: write failed for frame ${idx}:`, err);\n },\n ),\n );\n } catch (err) {\n // Page closed mid-capture, or screenshot otherwise failed. Bail\n // cleanly rather than crashing the loop.\n if (stopped) return;\n console.warn('humanjs capture: screenshot failed, stopping loop:', err);\n stopped = true;\n return;\n }\n const elapsed = Date.now() - loopStart;\n const wait = intervalMs - elapsed;\n if (wait > 0) await sleep(wait);\n }\n };\n\n // Kick off the loop but don't await — it runs until stop()/abort().\n const loopPromise = captureLoop();\n\n const finish = async (): Promise<void> => {\n stopped = true;\n await loopPromise;\n // `allSettled` over `writes` makes individual failures non-blocking; the\n // per-write `.then` above already logs them, so callers see one warning\n // per failed frame and a complete (possibly shorter) frames array.\n await Promise.allSettled(writes);\n };\n\n return {\n async stop(): Promise<CaptureResult> {\n await finish();\n const stoppedAtMs = Date.now();\n return {\n dir,\n frames: [...frames].sort((a, b) => a.tMs - b.tMs),\n startedAtMs,\n stoppedAtMs,\n format,\n fps,\n cleanup: () => rm(dir, { recursive: true, force: true }).then(() => undefined),\n };\n },\n async abort(): Promise<void> {\n await finish();\n await rm(dir, { recursive: true, force: true }).catch(() => undefined);\n },\n };\n}\n","import { type Personality, planScroll, type Rng, type ScrollSegment } from '@humanjs/core';\nimport type { Locator, Page } from 'playwright';\nimport type { Speed } from '../index';\nimport { sleep, speedModeFactor } from '../internal/timing';\n\n/** Runtime dependencies for a humanized scroll. */\nexport interface ScrollContext {\n readonly page: Page;\n readonly personality: Personality;\n readonly rng: Rng;\n readonly speed: Speed;\n}\n\n/**\n * What / where to scroll. The chosen axis (`'y'` by default, `'x'` via\n * `options.axis`) decides whether targets resolve along the vertical or\n * horizontal axis.\n *\n * - `'natural'`: scroll one viewport along the chosen axis (default).\n * - `'end'`: scroll to the far edge (bottom for `'y'`, right for `'x'`).\n * - `'top'`: scroll to the origin (top for `'y'`, left for `'x'`).\n * - `string`: a Playwright-compatible selector — scroll until in view.\n * - `Locator`: same, but you already have a Locator handle.\n * - `{ by: number }`: relative pixel delta (negative = up / left).\n * - `{ to: number }`: absolute scroll position on the chosen axis.\n */\nexport type ScrollTarget =\n | 'natural'\n | 'end'\n | 'top'\n | string\n | Locator\n | { readonly by: number }\n | { readonly to: number };\n\nexport interface ScrollOptions {\n /**\n * Force an overshoot + correction even if the personality wouldn't choose\n * one. Useful when you want the humanization signal regardless of\n * personality (e.g. demos).\n */\n readonly overshoot?: boolean;\n /**\n * Disable mid-scroll micro-pauses. Defaults to `true` (pauses enabled).\n * Set `false` for the smoothest possible motion.\n */\n readonly withPauses?: boolean;\n /**\n * For element targets only: where to align the element along the chosen\n * axis when the scroll ends. Mirrors `scrollIntoView({ block })`.\n * Defaults to `'start'`.\n *\n * - `'start'`: element's leading edge aligns with the viewport's leading\n * edge (top for `axis: 'y'`, left for `axis: 'x'`)\n * - `'center'`: element centers in the viewport\n * - `'end'`: element's trailing edge aligns with the viewport's trailing\n * edge (bottom for `'y'`, right for `'x'`)\n * - `'nearest'`: do the minimum scroll — stay put if the element is\n * already fully visible, otherwise scroll to the closest edge\n */\n readonly block?: 'start' | 'center' | 'end' | 'nearest';\n /**\n * Scroll inside a scrollable container instead of the page. Accepts a\n * Playwright-compatible selector or a built `Locator`. Every scroll\n * semantic (`'natural'`, `'top'`, `'end'`, `{ by }`, `{ to }`, element\n * targets, `block` alignment) applies relative to the container.\n *\n * In humanized speed modes, the cursor parks over the container's\n * center so the visible cursor reads as \"human hand on the wheel\"\n * (when paired with `installMouseHelper`), and each planned segment\n * advances the container's `scrollLeft` / `scrollTop` directly. Direct\n * property assignment is more reliable than dispatching wheel events\n * into nested overflow containers — Playwright's synthetic wheel\n * events don't always route past the cursor element. In `'instant'`\n * mode, the container's scroll position is set with a single\n * `scrollTo` call.\n *\n * Common use: chat threads, modal bodies, infinite-scroll feeds, any\n * `<div style=\"overflow-y: auto\">` that owns its own scrollbar.\n */\n readonly within?: string | Locator;\n /**\n * Which axis to scroll along. Defaults to `'y'` (vertical).\n *\n * Set `'x'` for horizontal scrolling — carousels, kanban boards,\n * sideways galleries. Every target shape still works: `'natural'`\n * scrolls one viewport-width right, `'top'` jumps to scrollLeft 0,\n * `'end'` to (scrollWidth - clientWidth), `{ by }` / `{ to }` apply to\n * the X-axis, and element targets scroll until the element is visible\n * horizontally.\n */\n readonly axis?: 'x' | 'y';\n}\n\n/** Outcome of a scroll, returned to the caller for observability. */\nexport interface ScrollResult {\n /** Starting scroll position along the chosen axis (scrollY or scrollX). */\n readonly from: number;\n /** Final scroll position the scroll aimed for, along the chosen axis. */\n readonly to: number;\n /** Signed pixel distance (`to - from`). */\n readonly distance: number;\n /** Total elapsed dwell + motion time in ms. Zero in `speed: 'instant'`. */\n readonly durationMs: number;\n}\n\n/** Reserved string targets that select a behavior rather than an element. */\nconst RESERVED_TARGETS = new Set(['natural', 'end', 'top']);\n\n/**\n * Generic axis description — the planner doesn't care whether it's driving\n * `window.scrollY` or an element's `scrollTop`. Both look like the same\n * 1-D problem: a current position, a viewport length, and a max scrollable\n * extent.\n */\ninterface ScrollGeometry {\n /** Current scroll position. */\n readonly current: number;\n /** Visible length (viewport height for window, clientHeight for container). */\n readonly viewport: number;\n /** Total scrollable extent (docHeight for window, scrollHeight for container). */\n readonly total: number;\n /**\n * For container scrolls: viewport-relative center coordinates the cursor\n * should park at so an `installMouseHelper` overlay reads as \"human hand\n * on the wheel\" during the scroll. Undefined for window scrolls (no\n * cursor parking — the page scrolls without a per-target hover).\n */\n readonly hover?: { x: number; y: number };\n}\n\n/**\n * Executes a humanized scroll on either the page or a scrollable container,\n * along the chosen axis.\n *\n * Flow:\n * 1. Resolve the scope: page (default) or a container if `within` is set.\n * 2. Read current position + viewport + total geometry for the chosen axis.\n * 3. Resolve target → final position on that axis.\n * 4. Plan segments via `planScroll`.\n * 5. Walk the segments — for page scrolls, dispatch `page.mouse.wheel`\n * events on the chosen axis. For containers, advance the element's\n * `scrollLeft` / `scrollTop` directly (wheel events don't always\n * route into nested overflow containers). Containers also park the\n * cursor at their center so an `installMouseHelper` overlay reads\n * as \"hand on the wheel.\"\n *\n * In `speed: 'instant'`, all humanization is bypassed:\n * - Page scrolls call `window.scrollTo(...)` with the new position on the\n * chosen axis and the existing position on the other.\n * - Container scrolls evaluate `el.scrollTo(...)` with the same shape.\n */\nexport async function executeScroll(\n target: ScrollTarget | undefined,\n ctx: ScrollContext,\n options: ScrollOptions = {},\n): Promise<ScrollResult> {\n const { page, personality, rng, speed } = ctx;\n const speedFactor = speedModeFactor(speed);\n const axis: 'x' | 'y' = options.axis ?? 'y';\n\n const container = resolveWithin(options.within, ctx);\n const geom = container\n ? await readContainerGeometry(container, axis)\n : await readWindowGeometry(page, axis);\n if (!geom) {\n // Container not found / not scrollable — nothing to do.\n return { from: 0, to: 0, distance: 0, durationMs: 0 };\n }\n\n const from = geom.current;\n const targetPos = await resolveTarget(target, ctx, geom, container, axis, options.block);\n const to = clamp(targetPos, 0, Math.max(0, geom.total - geom.viewport));\n const distance = to - from;\n\n if (distance === 0) {\n return { from, to, distance: 0, durationMs: 0 };\n }\n\n if (speed === 'instant') {\n if (container) {\n await container.evaluate(\n (el, args) => {\n const a = args as { axis: 'x' | 'y'; pos: number };\n if (a.axis === 'x') el.scrollTo(a.pos, el.scrollTop);\n else el.scrollTo(el.scrollLeft, a.pos);\n },\n { axis, pos: to },\n );\n } else {\n await page.evaluate(\n (args) => {\n if (args.axis === 'x') window.scrollTo(args.pos, window.scrollY);\n else window.scrollTo(window.scrollX, args.pos);\n },\n { axis, pos: to },\n );\n }\n return { from, to, distance, durationMs: 0 };\n }\n\n const segments = planScroll(from, to, personality.scroll, rng, {\n forceOvershoot: options.overshoot,\n withPauses: options.withPauses,\n personalitySpeed: personality.speed,\n speedFactor,\n });\n\n // For container scrolls, park the cursor over the container so the\n // visible cursor (when `installMouseHelper` is in use) reads as \"human\n // hand on the wheel.\" Actual scrolling goes through direct\n // `scrollLeft` / `scrollTop` assignment in `walkSegments` — Playwright's\n // synthetic wheel events don't reliably route into nested overflow\n // containers.\n if (container && geom.hover) {\n await page.mouse.move(geom.hover.x, geom.hover.y);\n }\n\n const startedAt = Date.now();\n await walkSegments(page, segments, axis, container);\n const durationMs = Date.now() - startedAt;\n\n return { from, to, distance, durationMs };\n}\n\n/** Coerces the `within` option into a Locator (or null when unset). */\nfunction resolveWithin(within: ScrollOptions['within'], ctx: ScrollContext): Locator | null {\n if (!within) return null;\n return typeof within === 'string' ? ctx.page.locator(within) : within;\n}\n\n/** Reads the page's current scroll geometry for the chosen axis. */\nasync function readWindowGeometry(page: Page, axis: 'x' | 'y'): Promise<ScrollGeometry> {\n const g = await page.evaluate((a: 'x' | 'y') => {\n if (a === 'x') {\n return {\n current: window.scrollX,\n viewport: window.innerWidth,\n total: Math.max(document.documentElement.scrollWidth, document.body?.scrollWidth ?? 0),\n };\n }\n return {\n current: window.scrollY,\n viewport: window.innerHeight,\n total: Math.max(document.documentElement.scrollHeight, document.body?.scrollHeight ?? 0),\n };\n }, axis);\n return { current: g.current, viewport: g.viewport, total: g.total };\n}\n\n/**\n * Reads container scroll geometry for the chosen axis plus the center of\n * its viewport-relative bounding box (for cursor parking). Returns `null`\n * if the element resolves to nothing.\n */\nasync function readContainerGeometry(\n container: Locator,\n axis: 'x' | 'y',\n): Promise<ScrollGeometry | null> {\n return container\n .evaluate((el, a: 'x' | 'y') => {\n const rect = el.getBoundingClientRect();\n const isX = a === 'x';\n return {\n current: isX ? el.scrollLeft : el.scrollTop,\n viewport: isX ? el.clientWidth : el.clientHeight,\n total: isX ? el.scrollWidth : el.scrollHeight,\n hover: {\n x: rect.left + rect.width / 2,\n y: rect.top + rect.height / 2,\n },\n } as const;\n }, axis)\n .catch(() => null);\n}\n\n/**\n * Resolves a `ScrollTarget` to an absolute scroll position on the active\n * axis (window scroll or container scroll, vertical or horizontal).\n */\nasync function resolveTarget(\n target: ScrollTarget | undefined,\n ctx: ScrollContext,\n geom: ScrollGeometry,\n container: Locator | null,\n axis: 'x' | 'y',\n block: 'start' | 'center' | 'end' | 'nearest' = 'start',\n): Promise<number> {\n if (target === undefined || target === 'natural') return geom.current + geom.viewport;\n if (target === 'end') return geom.total;\n if (target === 'top') return 0;\n if (typeof target === 'object' && 'by' in target) return geom.current + target.by;\n if (typeof target === 'object' && 'to' in target) return target.to;\n\n // Element target — selector string or Locator.\n const elementLocator =\n typeof target === 'string' && !RESERVED_TARGETS.has(target)\n ? ctx.page.locator(target)\n : typeof target === 'string'\n ? null\n : target;\n if (!elementLocator) return geom.current + geom.viewport;\n\n return container\n ? resolveElementWithinContainer(elementLocator, container, geom, axis, block)\n : resolveElementInWindow(elementLocator, geom, axis, block);\n}\n\n/**\n * Computes the scroll position needed to align a window-level element per\n * `block` along the chosen axis. `rect.x` / `rect.y` are viewport-relative;\n * absolute position is `geom.current + rect.{axis}`.\n */\nasync function resolveElementInWindow(\n elementLocator: Locator,\n geom: ScrollGeometry,\n axis: 'x' | 'y',\n block: 'start' | 'center' | 'end' | 'nearest',\n): Promise<number> {\n const rect = await elementLocator.boundingBox().catch(() => null);\n if (!rect) return geom.current;\n const relStart = axis === 'x' ? rect.x : rect.y;\n const length = axis === 'x' ? rect.width : rect.height;\n const absoluteStart = geom.current + relStart;\n const absoluteEnd = absoluteStart + length;\n if (block === 'start') return absoluteStart;\n if (block === 'end') return absoluteEnd - geom.viewport;\n if (block === 'nearest') {\n if (relStart >= 0 && relStart + length <= geom.viewport) return geom.current;\n if (relStart < 0) return absoluteStart;\n return absoluteEnd - geom.viewport;\n }\n return absoluteStart - (geom.viewport - length) / 2;\n}\n\n/**\n * Computes the container's `scrollTop` / `scrollLeft` needed to align an\n * element per `block` along the chosen axis. Element offset from the\n * container's content origin =\n * `(element.rect.{axis} - container.rect.{axis}) + container.scroll{axis}`\n * regardless of whether the container is the element's positioning ancestor.\n */\nasync function resolveElementWithinContainer(\n elementLocator: Locator,\n container: Locator,\n geom: ScrollGeometry,\n axis: 'x' | 'y',\n block: 'start' | 'center' | 'end' | 'nearest',\n): Promise<number> {\n const rects = await container\n .evaluate(\n (containerEl, args: { sel: string | null; axis: 'x' | 'y' }) => {\n const elementEl = args.sel ? document.querySelector(args.sel) : null;\n // The element may not be a child of the container — handle that too.\n const targetEl = elementEl ?? (containerEl.querySelector(':scope > *') as Element | null);\n if (!targetEl) return null;\n const cRect = containerEl.getBoundingClientRect();\n const eRect = targetEl.getBoundingClientRect();\n return args.axis === 'x'\n ? { relStart: eRect.left - cRect.left, length: eRect.width }\n : { relStart: eRect.top - cRect.top, length: eRect.height };\n },\n { sel: await locatorSelector(elementLocator), axis },\n )\n .catch(() => null);\n if (!rects) return geom.current;\n // Offset from container's content origin = (visual delta) + scroll position.\n const offsetStart = rects.relStart + geom.current;\n const offsetEnd = offsetStart + rects.length;\n if (block === 'start') return offsetStart;\n if (block === 'end') return offsetEnd - geom.viewport;\n if (block === 'nearest') {\n if (rects.relStart >= 0 && rects.relStart + rects.length <= geom.viewport) {\n return geom.current;\n }\n if (rects.relStart < 0) return offsetStart;\n return offsetEnd - geom.viewport;\n }\n return offsetStart - (geom.viewport - rects.length) / 2;\n}\n\n/**\n * Best-effort recovery of the selector string a Locator wraps. Playwright's\n * `Locator.toString()` returns something like `locator('css=#foo')` — we\n * strip the wrapper to get the inner selector. Falls back to `null` when\n * the format isn't recognized; the caller treats that as \"no element.\"\n *\n * Fragile by design: depends on Playwright's internal `toString()` shape,\n * which isn't a stable public API. Used only when resolving element\n * targets *inside* a `within` container, since the executor needs to\n * `querySelector` from the container's perspective rather than the\n * document's. For chained Locators (e.g. `page.locator('foo').first()`)\n * the regex grabs the first quoted selector and ignores chained\n * refinements — good enough for the common case, but worth a richer\n * approach if Playwright ever changes the shape.\n */\nasync function locatorSelector(locator: Locator): Promise<string | null> {\n const s = locator.toString?.();\n if (typeof s !== 'string') return null;\n const match = /locator\\(['\"](.+?)['\"]/.exec(s);\n if (!match) return null;\n const raw = match[1] ?? '';\n // Strip an \"engine=\" prefix when present (Playwright sometimes prepends one).\n const eq = raw.indexOf('=');\n return eq > 0 && /^[a-z]+$/.test(raw.slice(0, eq)) ? raw.slice(eq + 1) : raw;\n}\n\n/**\n * Walks the planned segments. For page scrolls, dispatches wheel events\n * on the chosen axis — real wheel events trigger every page-level wheel\n * handler, which is part of HumanJS's \"dispatch what a human dispatches\"\n * promise.\n *\n * For container scrolls, advances `scrollLeft` / `scrollTop` directly via\n * `container.evaluate`. Playwright's `page.mouse.wheel` doesn't reliably\n * route into nested overflow containers (the event hits the element under\n * the cursor but may scroll a parent or no element instead). Direct\n * property assignment is deterministic — it always scrolls the target —\n * and the brand promise stays intact because the planner still owns the\n * bell-curve cadence, mid-scroll pauses, and overshoot.\n */\nasync function walkSegments(\n page: Page,\n segments: readonly ScrollSegment[],\n axis: 'x' | 'y',\n container: Locator | null,\n): Promise<void> {\n for (const segment of segments) {\n if (segment.delayBeforeMs > 0) await sleep(segment.delayBeforeMs);\n if (segment.delta === 0) continue;\n if (container) {\n await container.evaluate(\n (el, args) => {\n const a = args as { axis: 'x' | 'y'; delta: number };\n // Direct property assignment is more reliable than `scrollBy()`\n // — some flex/grid layouts have edge cases where scrollBy\n // becomes a no-op despite the element being scrollable.\n if (a.axis === 'x') el.scrollLeft += a.delta;\n else el.scrollTop += a.delta;\n },\n { axis, delta: segment.delta },\n );\n } else if (axis === 'x') {\n await page.mouse.wheel(segment.delta, 0);\n } else {\n await page.mouse.wheel(0, segment.delta);\n }\n }\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return value < min ? min : value > max ? max : value;\n}\n","/// <reference lib=\"dom\" />\n\nimport type { BrowserContext, Page } from 'playwright';\n\n/**\n * Canonical HumanJS cursor path — kept in lockstep with the SVG used by the\n * web app's `HumanCursorIcon`. Inlined here so the playwright package has no\n * dependency on `@humanjs/web` or DOM assets.\n */\nconst CURSOR_PATH = 'M 0 0 L 16 6 L 8 9.5 L 5 19 Z';\n\n/** Options for {@link installMouseHelper}. */\nexport interface InstallMouseHelperOptions {\n /** Cursor fill color. Defaults to the HumanJS amber `#f5a55c`. */\n readonly color?: string;\n /** Cursor visual size in pixels. Defaults to 22. */\n readonly size?: number;\n /**\n * Render a ripple at each `mousedown` position so clicks read on video.\n * Defaults to `true`.\n */\n readonly showClicks?: boolean;\n /** Halo opacity behind the cursor. Defaults to `0.18`. */\n readonly haloOpacity?: number;\n}\n\n/**\n * Installs a visual cursor overlay that follows every `mousemove` on each\n * page in the target. Real synthetic motion from Playwright (e.g.\n * `human.click()`, `human.read(..., { withMotion: true })`) is *already*\n * happening — the page just doesn't render a system cursor for it. This\n * helper injects a HumanJS-styled SVG cursor that listens to mousemove\n * events and follows them, making the motion visible in headed demos and\n * screen recordings.\n *\n * Re-runs on every navigation via `addInitScript`, so the overlay survives\n * page reloads. Idempotent — guard flag on `window` prevents double-install.\n *\n * Accepts either a `Page` (overlay applies to that page) or a\n * `BrowserContext` (overlay applies to every page in the context, including\n * pages opened later).\n *\n * @example\n * ```ts\n * const browser = await chromium.launch({ headless: false });\n * const context = await browser.newContext();\n * await installMouseHelper(context);\n * const page = await context.newPage();\n * // human-driven actions are now visible in the page\n * ```\n */\n// Cross-realm idempotency flag. We stash this on the target so a second\n// `installMouseHelper(samePageOrContext)` call is a no-op instead of\n// quietly stacking duplicate listeners. `Symbol.for` keeps the key stable\n// across bundle boundaries if @humanjs/playwright ever ends up duplicated\n// in node_modules (e.g. peer-dep resolution quirks).\nconst INSTALLED_FLAG = Symbol.for('@humanjs/playwright:mouse-helper:installed');\n\nexport async function installMouseHelper(\n target: BrowserContext | Page,\n options: InstallMouseHelperOptions = {},\n): Promise<void> {\n // Guard against re-install on the same target. Without this, every extra\n // call adds another 'domcontentloaded' and 'page' listener, multiplying\n // the per-navigation evaluate() round-trips (the DOM guard in\n // `installScript` makes them no-ops, but the round-trips still cost).\n const tagged = target as unknown as { [k: symbol]: true | undefined };\n if (tagged[INSTALLED_FLAG]) return;\n tagged[INSTALLED_FLAG] = true;\n\n const config: HelperConfig = {\n color: options.color ?? '#f5a55c',\n stroke: '#020203',\n size: options.size ?? 22,\n showClicks: options.showClicks ?? true,\n haloOpacity: options.haloOpacity ?? 0.18,\n path: CURSOR_PATH,\n };\n\n // Register for every future navigation. Fires on goto, reload, frame\n // attachment — but, critically, NOT on `page.setContent()` (Playwright\n // doesn't treat setContent as a navigation, so its new document never\n // sees the init script). The `domcontentloaded` listener below catches\n // that case.\n await target.addInitScript(installScript, config);\n\n // Per-page re-injection on every DOMContentLoaded. `setContent` fires\n // this event but doesn't trigger addInitScript; this is the bridge.\n // The DOM-element guard inside `installScript` makes duplicate installs\n // into a no-op, so it's safe to run on top of addInitScript.\n const attachPageHooks = (page: Page): void => {\n page.on('domcontentloaded', () => {\n page.evaluate(installScript, config).catch(() => undefined);\n });\n };\n\n // Existing pages + future ones (when the target is a context).\n const pages: Page[] = 'pages' in target ? target.pages() : [target];\n for (const page of pages) attachPageHooks(page);\n if ('on' in target && 'newPage' in target) {\n // BrowserContext branch — pages opened after `installMouseHelper`\n // returns still get the same lifecycle hooks.\n target.on('page', attachPageHooks);\n }\n\n // Initial injection for already-loaded pages: if the page is already on a\n // document (via goto/setContent before we attached), the DOMContentLoaded\n // event already fired and our listener won't see it. Evaluate once now.\n await Promise.all(\n pages.map((page) => page.evaluate(installScript, config).catch(() => undefined)),\n );\n}\n\ninterface HelperConfig {\n color: string;\n stroke: string;\n size: number;\n showClicks: boolean;\n haloOpacity: number;\n path: string;\n}\n\n/**\n * Serialized into the page and re-run on every navigation. No closures —\n * everything threads through `config`. `window` and `document` are the\n * page's globals at runtime, not the Node-side types.\n */\nfunction installScript(config: HelperConfig) {\n // Guard on the *document*'s cursor element, not the window. `page.setContent()`\n // replaces the document (and our cursor element inside it) but keeps the\n // same `window`, so a window-level flag would falsely report \"already\n // installed\" while the cursor is actually gone. Querying the DOM is the\n // reliable check.\n if (document.querySelector('[data-humanjs-cursor]')) return;\n\n const attach = () => {\n const cursor = document.createElement('div');\n cursor.setAttribute('aria-hidden', 'true');\n cursor.setAttribute('data-humanjs-cursor', 'true');\n cursor.style.cssText = [\n 'position: fixed',\n 'left: 0',\n 'top: 0',\n `width: ${config.size}px`,\n `height: ${config.size + 4}px`,\n 'pointer-events: none',\n 'z-index: 2147483647',\n // Start visible at (0, 0) so the cursor is on screen from the moment\n // the page loads — without this the helper looks like nothing happened\n // until the first mousemove arrives.\n 'opacity: 1',\n 'transform: translate(0px, 0px)',\n // CSS interpolates between successive `mousemove` updates so the\n // cursor reads as continuous motion instead of discrete hops. Slightly\n // longer than the path-walker's typical step interval (~30–80ms) so\n // each tween is still settling when the next move lands → no pauses.\n 'transition: transform 110ms ease-out, opacity 0.18s ease-out',\n 'will-change: transform',\n ].join('; ');\n const haloRadius = Math.round(config.size * 0.6);\n cursor.innerHTML = `\n <svg width=\"${config.size}\" height=\"${config.size + 4}\" viewBox=\"0 0 22 24\" style=\"overflow: visible;\">\n <circle cx=\"0\" cy=\"0\" r=\"${haloRadius}\" fill=\"${config.color}\" opacity=\"${config.haloOpacity}\" />\n <path d=\"${config.path}\" fill=\"${config.color}\" stroke=\"${config.stroke}\" stroke-width=\"0.7\" stroke-linejoin=\"round\" />\n </svg>\n `;\n document.body.appendChild(cursor);\n\n let lastX = 0;\n let lastY = 0;\n const onMove = (e: MouseEvent) => {\n lastX = e.clientX;\n lastY = e.clientY;\n cursor.style.transform = `translate(${lastX}px, ${lastY}px)`;\n cursor.style.opacity = '1';\n };\n // Listen on both window and document so we catch synthetic mouse events\n // dispatched at either level (Playwright sends them via CDP, and the\n // target frame can vary by event source).\n window.addEventListener('mousemove', onMove, { capture: true, passive: true });\n document.addEventListener('mousemove', onMove, { capture: true, passive: true });\n document.addEventListener(\n 'mouseleave',\n () => {\n cursor.style.opacity = '0';\n },\n { capture: true, passive: true },\n );\n\n if (config.showClicks) {\n const styleEl = document.createElement('style');\n styleEl.textContent =\n '@keyframes humanjs-ripple { 0% { transform: translate(-50%, -50%) scale(0.4); opacity: 0.9; } 100% { transform: translate(-50%, -50%) scale(2); opacity: 0; } }';\n document.head.appendChild(styleEl);\n\n window.addEventListener(\n 'mousedown',\n () => {\n const ripple = document.createElement('div');\n ripple.style.cssText = [\n 'position: fixed',\n `left: ${lastX}px`,\n `top: ${lastY}px`,\n 'width: 28px',\n 'height: 28px',\n 'border-radius: 50%',\n `border: 1.5px solid ${config.color}`,\n 'pointer-events: none',\n 'z-index: 2147483646',\n 'animation: humanjs-ripple 0.45s ease-out forwards',\n ].join('; ');\n document.body.appendChild(ripple);\n window.setTimeout(() => ripple.remove(), 500);\n },\n { capture: true, passive: true },\n );\n }\n };\n\n if (document.body) attach();\n else document.addEventListener('DOMContentLoaded', attach, { once: true });\n}\n","import {\n type ActionResult,\n createRng,\n type HumanAction,\n type HumanPlugin,\n type Personality,\n type PersonalityConfig,\n type PluginContext,\n type Point,\n resolvePersonality,\n sleep,\n} from '@humanjs/core';\nimport type { Locator, Page } from 'playwright';\nimport { executeType } from './keyboard';\nimport { executeClick } from './mouse';\nimport { executeRead, type ReadOptions, type ReadResult, type ReadTarget } from './reading';\nimport {\n getCaptureSettingsForQuality,\n Recording,\n type RecordingQuality,\n type TimelineEvent,\n} from './recording';\nimport { startCapture } from './recording/capture';\nimport { executeScroll, type ScrollOptions, type ScrollResult, type ScrollTarget } from './scroll';\n\nexport type {\n ActionResult,\n ActionType,\n BezierPathOptions,\n ComputeReadingDwellOptions,\n DwellProfile,\n HumanAction,\n HumanizePathOptions,\n HumanPlugin,\n Keystroke,\n KnownActionType,\n MouseProfile,\n Personality,\n PersonalityConfig,\n PersonalityExtension,\n PlanTypingOptions,\n PluginContext,\n Point,\n PresetName,\n ReadingProfile,\n ReadKind,\n Rng,\n ScrollProfile,\n ScrollSegment,\n TypingProfile,\n} from '@humanjs/core';\n// Re-exports of the public core API so consumers have one import surface.\nexport {\n applyMicroJitter,\n applyVelocityProfile,\n bezierPath,\n blend,\n careful,\n computeReadingDwellMs,\n countWords,\n createRng,\n distracted,\n fast,\n humanizePath,\n planScroll,\n planTypeKeystrokes,\n precise,\n resolvePersonality,\n sleep,\n} from '@humanjs/core';\n// Playwright primitives re-exported for one-import convenience. These are\n// the unmodified upstream values/types — `@humanjs/playwright` is a\n// Playwright integration, so users shouldn't have to dual-import for the\n// common case. Add more re-exports here only when a real user need shows\n// up; the goal is \"covers 95% of demo/test code,\" not \"mirrors all of\n// playwright.\"\nexport {\n type Browser,\n type BrowserContext,\n type BrowserContextOptions,\n chromium,\n type ElementHandle,\n firefox,\n type LaunchOptions,\n type Locator,\n type Page,\n webkit,\n} from 'playwright';\nexport type { InstallMouseHelperOptions } from './mouse-helper';\nexport { installMouseHelper } from './mouse-helper';\nexport type { ReadOptions, ReadResult, ReadTarget } from './reading';\nexport {\n type FfmpegPreset,\n type FfmpegTune,\n Recording,\n type RecordingQuality,\n type Timeline,\n type TimelineEvent,\n type ToGifOptions,\n type ToVideoOptions,\n} from './recording';\nexport type { ScrollOptions, ScrollResult, ScrollTarget } from './scroll';\n\n/**\n * How fast the humanized session runs.\n * - `'human'` — full humanization (default)\n * - `'fast'` — humanized but accelerated\n * - `'instant'` — bypass all humanization, straight Playwright\n */\nexport type Speed = 'fast' | 'human' | 'instant';\n\n/** Options for {@link createHuman}. */\nexport interface CreateHumanOptions {\n /** Personality preset, extension, or fully built personality. Defaults to `'careful'`. */\n readonly personality?: PersonalityConfig;\n /** Seed for the session's PRNG. Same seed produces identical trajectories. */\n readonly seed?: number | string;\n /** Speed mode. Defaults to `'human'`. */\n readonly speed?: Speed;\n /** Plugins installed on this session, invoked in registration order. */\n readonly plugins?: readonly HumanPlugin[];\n /**\n * Starting cursor position used as the origin of the first humanized path.\n * Defaults to `{ x: 0, y: 0 }`. Set this if you've already moved the cursor\n * (e.g. via `page.mouse.move`) before creating the session, so the first\n * click's path starts from the correct location.\n */\n readonly initialMousePosition?: Point;\n}\n\n/** A humanized Playwright session bound to a single `Page`. */\nexport interface Human {\n /** The resolved personality this session is using. */\n readonly personality: Personality;\n /** The speed mode this session was created with. */\n readonly speed: Speed;\n /**\n * Navigate to `url`. Plugins observe the action via `'goto'`; the underlying\n * `page.goto(url)` is awaited unchanged.\n */\n goto(url: string): Promise<void>;\n /**\n * Move the mouse along a humanized Bezier path to `target` and click.\n *\n * `target` accepts either a Playwright-compatible selector string (e.g.\n * `'button:has-text(\"Buy now\")'`) or a built `Locator`. The click point\n * inside the element is Gaussian-distributed around the center.\n *\n * In `speed: 'instant'`, all humanization is skipped and Playwright's\n * native `locator.click()` is used directly.\n */\n click(target: Locator | string): Promise<void>;\n /**\n * Type `value` into `target` with humanized per-key timing, optional typo\n * injection (with backspace recovery), and occasional think-pauses.\n *\n * Per-key `keydown`/`press`/`up` events fire for each character, so\n * handlers like autocomplete dropdowns still receive every keystroke.\n *\n * In `speed: 'instant'`, falls back to `locator.pressSequentially` with\n * zero inter-key delay — events still fire, but humanization is skipped.\n */\n type(target: Locator | string, value: string): Promise<void>;\n /**\n * Dwell as if reading `target` — the third pillar of humanization after\n * the cursor and the keyboard. Real users pause to read; HumanJS models\n * that pause from word count + the personality's reading WPM (+ jitter).\n *\n * **Targets:**\n * - `string`: a Playwright-compatible selector (matches `click()`/`type()`).\n * - `Locator`: a pre-built Locator.\n * - `{ text }`: literal text in hand (no DOM lookup).\n * - `{ words }`: pre-counted — skip text extraction entirely.\n *\n * **Smart defaults** (only when the caller doesn't override):\n * - `kind` auto-detects as `'code'` for `<pre>` and `<code>` tags;\n * everything else falls back to `'prose'`. Explicit `kind` always wins.\n * - `scrollIntoView: false` — most flows already scrolled to the content.\n *\n * Plugins observe `'read'` actions with `{ target, words, kind }` in params.\n * The text content itself is never echoed — passwords, tokens, and other\n * sensitive strings stay out of telemetry by default.\n *\n * In `speed: 'instant'`, dwell collapses to 0 ms but the action still fires\n * so observability stays consistent.\n *\n * Returns a {@link ReadResult} with the word count, final kind (after\n * auto-detection), and total dwell duration — useful for assertions in\n * tests or for surfacing reading metadata to a UI.\n */\n read(target: ReadTarget, options?: ReadOptions): Promise<ReadResult>;\n /**\n * Scroll the page (or a scrollable container) humanly. Multi-segment\n * motion with a bell-curve velocity profile, optional mid-scroll\n * micro-pauses, and (for the `distracted` personality) the occasional\n * overshoot + correction.\n *\n * **Targets:**\n * - `'natural'` (default): scroll one viewport in the chosen axis\n * - `'end'` / `'top'`: jump to the document/container edges, humanized\n * - `string`: a Playwright-compatible selector — scroll until in view\n * - `Locator`: same, but with a pre-built handle\n * - `{ by: n }`: relative pixel delta (negative = up / left)\n * - `{ to: n }`: absolute scroll position on the chosen axis\n *\n * **Options:** `axis: 'x' | 'y'` (default `'y'`) picks the direction;\n * `within: selector | Locator` scopes the scroll to a scrollable\n * container; `block: 'start' | 'center' | 'end' | 'nearest'` aligns\n * element targets; `overshoot` / `withPauses` toggle individual\n * humanization signals.\n *\n * Plugins observe `'scroll'` actions with `{ target }` in\n * `beforeAction`'s params (a human-readable description of the target).\n * The full {@link ScrollResult} (`from` / `to` / `distance` /\n * `durationMs`) is available via `afterAction`'s `result` argument.\n *\n * In `speed: 'instant'`, the page (or container) is moved with a single\n * `scrollTo` call. No wheel events, no segments — but the action still\n * fires for observability.\n *\n * Returns a {@link ScrollResult} for assertions in tests.\n */\n scroll(target?: ScrollTarget, options?: ScrollOptions): Promise<ScrollResult>;\n /**\n * Pause for `ms` milliseconds. Equivalent to importing `sleep` from\n * `@humanjs/playwright` and calling it — exposed on the Human instance\n * so users who already have `human` in scope don't need an extra import.\n *\n * Not a humanized action: this is a raw `setTimeout` wait, not scaled\n * by personality or speed mode, and no plugin events fire. Use it for\n * generic pacing between humanized actions.\n */\n sleep(ms: number): Promise<void>;\n /**\n * Records `fn`'s actions. Returns a {@link Recording} you can export\n * to mp4/webm video (`toVideo`), JSON timeline (`toTimeline`), or both.\n *\n * By default, frames are captured from the browser via timer-polled\n * `page.screenshot()` — fully controllable quality, not limited by\n * Playwright's recordVideo bitrate ceiling. Pass `{ video: false }` to\n * skip capture entirely (timeline-only mode, zero video overhead).\n *\n * Plugins observe `'record'` in `beforeAction` / `afterAction` so\n * recording shows up alongside the other primitives in observability\n * pipelines.\n *\n * Single-use per session: `Recording.toVideo()` finalizes the captured\n * frames, so calling `human.record()` twice on the same human throws.\n *\n * @example\n * ```ts\n * // Default: video + timeline\n * const rec = await human.record(async () => {\n * await human.click('#login');\n * });\n * await rec.toVideo('demo.mp4');\n * await rec.toTimeline('demo.json');\n *\n * // Timeline-only, no video overhead\n * const rec = await human.record({ video: false }, async () => {\n * await human.click('#login');\n * });\n * await rec.toTimeline('demo.json');\n * ```\n */\n record(fn: () => Promise<void>): Promise<Recording>;\n record(options: HumanRecordOptions, fn: () => Promise<void>): Promise<Recording>;\n}\n\n/** Options for {@link Human.record}. */\nexport interface HumanRecordOptions {\n /**\n * Whether to capture frames for video output. Defaults to `true`.\n * Set to `false` for timeline-only recordings (no capture loop, no\n * temp files, no encoding overhead — `toTimeline()` still works).\n */\n readonly video?: boolean;\n /**\n * Capture quality preset. Controls per-frame JPEG quality (or PNG for\n * lossless) AND the ffmpeg encode settings used by `toVideo()`.\n * Defaults to `'high'`. Ignored when `video: false`.\n */\n readonly quality?: RecordingQuality;\n}\n\n/**\n * Creates a humanized session bound to a Playwright `Page`.\n *\n * @example\n * ```ts\n * import { chromium } from 'playwright';\n * import { createHuman } from '@humanjs/playwright';\n *\n * const browser = await chromium.launch();\n * const page = await browser.newPage();\n *\n * const human = await createHuman(page, {\n * personality: 'careful',\n * seed: 'session-42',\n * });\n *\n * await human.goto('https://example.com');\n * ```\n */\nexport async function createHuman(page: Page, options: CreateHumanOptions = {}): Promise<Human> {\n const personality = resolvePersonality(options.personality ?? 'careful');\n const rng = createRng(options.seed);\n const speed = options.speed ?? 'human';\n const plugins = options.plugins ?? [];\n\n const context: PluginContext = { personality, rng };\n for (const plugin of plugins) {\n await plugin.install?.(context);\n }\n\n // Each session can only produce one Recording — we can't run two\n // concurrent capture loops on the same page without their screenshots\n // interfering. Reject loudly rather than letting both quietly drop frames.\n let hasRecorded = false;\n\n // Timeline capture is \"armed\" only while `human.record(cb)` is running:\n // a non-null `activeRecordingEvents` means performAction should push each\n // child action into the buffer. The wrapper `record` action itself is\n // skipped — consumers care about what happened inside, not the wrapper.\n let activeRecordingEvents: TimelineEvent[] | null = null;\n let activeRecordingStartMs = 0;\n\n async function performAction<T>(action: HumanAction, actionFn: () => Promise<T>): Promise<T> {\n for (const plugin of plugins) {\n await plugin.beforeAction?.(action);\n }\n const startedAt = Date.now();\n try {\n const value = await actionFn();\n const durationMs = Date.now() - startedAt;\n const result: ActionResult = { type: action.type, durationMs };\n if (activeRecordingEvents !== null && action.type !== 'record') {\n activeRecordingEvents.push({\n type: action.type,\n params: action.params ?? {},\n tMs: startedAt - activeRecordingStartMs,\n durationMs,\n });\n }\n for (const plugin of plugins) {\n await plugin.afterAction?.(action, result);\n }\n return value;\n } catch (error) {\n if (activeRecordingEvents !== null && action.type !== 'record') {\n activeRecordingEvents.push({\n type: action.type,\n params: action.params ?? {},\n tMs: startedAt - activeRecordingStartMs,\n durationMs: Date.now() - startedAt,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n for (const plugin of plugins) {\n await plugin.onError?.(action, error);\n }\n throw error;\n }\n }\n\n let lastMousePosition: Point = options.initialMousePosition ?? { x: 0, y: 0 };\n\n return {\n personality,\n speed,\n async goto(url) {\n await performAction({ type: 'goto', params: { url } }, async () => {\n await page.goto(url);\n });\n },\n async click(target) {\n const description = typeof target === 'string' ? target : (target.toString?.() ?? 'locator');\n await performAction({ type: 'click', params: { target: description } }, async () => {\n await executeClick(target, {\n page,\n personality,\n rng,\n speed,\n getMousePosition: () => lastMousePosition,\n setMousePosition: (point) => {\n lastMousePosition = point;\n },\n });\n });\n },\n async type(target, value) {\n const description = typeof target === 'string' ? target : (target.toString?.() ?? 'locator');\n // `value` itself is intentionally not echoed into params — typed input may\n // be sensitive (passwords, tokens). Expose length only for observability.\n await performAction(\n { type: 'type', params: { target: description, length: value.length } },\n async () => {\n await executeType(target, value, { page, personality, rng, speed });\n },\n );\n },\n async read(target, options) {\n const description = describeReadTarget(target);\n // Same privacy posture as `type`: never echo arbitrary content into\n // action params. `target` description, words (when known up front), and\n // kind are inert metadata; the text itself never lands here.\n return performAction(\n {\n type: 'read',\n params: {\n target: description,\n kind: options?.kind,\n },\n },\n () =>\n executeRead(\n target,\n {\n page,\n personality,\n rng,\n speed,\n // Read shares the session's tracked cursor position so an eye\n // scan starts from where the last click left off, and the next\n // click starts from where the scan ended.\n getMousePosition: () => lastMousePosition,\n setMousePosition: (point) => {\n lastMousePosition = point;\n },\n },\n options,\n ),\n );\n },\n async scroll(target, options) {\n const description = describeScrollTarget(target);\n return performAction(\n {\n type: 'scroll',\n params: { target: description },\n },\n () => executeScroll(target, { page, personality, rng, speed }, options),\n );\n },\n async sleep(ms) {\n // Wrap the core `sleep` in performAction so each pause shows up as a\n // 'sleep' event in the recorded timeline. Without this, `human.record()`\n // exporters (toPlaywright, toHumanJS, ...) would emit replay code that\n // skips the user's intentional pauses entirely.\n await performAction({ type: 'sleep', params: { ms } }, () => sleep(ms));\n },\n async record(\n optionsOrFn: HumanRecordOptions | (() => Promise<void>),\n maybeFn?: () => Promise<void>,\n ): Promise<Recording> {\n const [recordOptions, fn] =\n typeof optionsOrFn === 'function'\n ? [{}, optionsOrFn]\n : [optionsOrFn, maybeFn as () => Promise<void>];\n\n if (hasRecorded) {\n throw new Error(\n 'human.record() can only be called once per session. Create a ' +\n 'new browser context (and a new human session) to record a ' +\n 'separate clip.',\n );\n }\n hasRecorded = true;\n\n const captureEnabled = recordOptions.video !== false;\n const captureQuality = recordOptions.quality ?? 'high';\n\n // Start frame capture before any action runs so the first action's\n // motion is in the recording.\n let captureSession: Awaited<ReturnType<typeof startCapture>> | null = null;\n if (captureEnabled) {\n const { format, quality, fps } = getCaptureSettingsForQuality(captureQuality);\n captureSession = await startCapture(page, { format, quality, fps });\n }\n\n // Arm the timeline buffer. `performAction` pushes a TimelineEvent for\n // every child action while this is non-null; the wrapper `record` action\n // is filtered out there.\n const events: TimelineEvent[] = [];\n const windowStartMs = Date.now();\n activeRecordingEvents = events;\n activeRecordingStartMs = windowStartMs;\n let windowEndMs = windowStartMs;\n\n try {\n await performAction({ type: 'record', params: {} }, async () => {\n try {\n await fn();\n } finally {\n windowEndMs = Date.now();\n }\n });\n } catch (error) {\n // If the callback threw, abort the capture so the temp dir doesn't\n // linger. Then re-throw.\n if (captureSession) await captureSession.abort();\n throw error;\n } finally {\n activeRecordingEvents = null;\n }\n\n const captureResult = captureSession ? await captureSession.stop() : null;\n\n return new Recording(captureResult, windowStartMs, windowEndMs, {\n personality: personality.name,\n seed: options.seed === undefined ? null : String(options.seed),\n speed,\n events,\n });\n },\n };\n}\n\n/**\n * Human-readable description of a scroll target for action params. Strings\n * (presets + selectors) pass through; objects describe their shape; the\n * Locator branch falls back to `toString()`.\n */\nfunction describeScrollTarget(target: ScrollTarget | undefined): string {\n if (target === undefined) return 'natural';\n if (typeof target === 'string') return target;\n if ('by' in target) return `by:${target.by}`;\n if ('to' in target) return `to:${target.to}`;\n return target.toString?.() ?? 'locator';\n}\n\n/**\n * Human-readable description of a read target for action params. Echoes\n * selectors and `{ words }` (both inert); abbreviates literal text to its\n * length so we never expose content even via accidental logging.\n */\nfunction describeReadTarget(target: ReadTarget): string {\n if (typeof target === 'string') return target;\n if ('words' in target && typeof target.words === 'number') return `${target.words} words`;\n if ('text' in target && typeof target.text === 'string') {\n return `text:${target.text.length} chars`;\n }\n return target.toString?.() ?? 'locator';\n}\n"]}
|