@humanjs/playwright 0.2.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 +231 -4
- package/dist/index.cjs +1013 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +478 -4
- package/dist/index.d.ts +478 -4
- package/dist/index.js +978 -39
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,350 @@
|
|
|
1
|
-
import { PersonalityConfig, HumanPlugin, Point, Personality } from '@humanjs/core';
|
|
2
|
-
export { ActionResult, ActionType, BezierPathOptions, DwellProfile, HumanAction, HumanPlugin, HumanizePathOptions, KnownActionType, MouseProfile, Personality, PersonalityConfig, PersonalityExtension, PluginContext, Point, PresetName, ReadingProfile, Rng, TypingProfile, applyMicroJitter, applyVelocityProfile, bezierPath, blend, careful, createRng, distracted, fast, humanizePath, precise, resolvePersonality } from '@humanjs/core';
|
|
3
|
-
import { Locator, Page } from 'playwright';
|
|
1
|
+
import { ReadKind, PersonalityConfig, HumanPlugin, Point, Personality } from '@humanjs/core';
|
|
2
|
+
export { ActionResult, ActionType, BezierPathOptions, ComputeReadingDwellOptions, DwellProfile, HumanAction, HumanPlugin, HumanizePathOptions, Keystroke, KnownActionType, MouseProfile, Personality, PersonalityConfig, PersonalityExtension, PlanTypingOptions, PluginContext, Point, PresetName, ReadKind, ReadingProfile, Rng, ScrollProfile, ScrollSegment, TypingProfile, applyMicroJitter, applyVelocityProfile, bezierPath, blend, careful, computeReadingDwellMs, countWords, createRng, distracted, fast, humanizePath, planScroll, planTypeKeystrokes, precise, resolvePersonality, sleep } from '@humanjs/core';
|
|
3
|
+
import { Locator, BrowserContext, Page } from 'playwright';
|
|
4
|
+
export { Browser, BrowserContext, BrowserContextOptions, ElementHandle, LaunchOptions, Locator, Page, chromium, firefox, webkit } from 'playwright';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What to read:
|
|
8
|
+
* - `string`: a Playwright-compatible selector (matches `click()` / `type()`).
|
|
9
|
+
* The element's `innerText` is resolved and word-counted.
|
|
10
|
+
* - `Locator`: same, but you already have a Locator handle.
|
|
11
|
+
* - `{ text }`: literal text (you have the string in hand, no selector).
|
|
12
|
+
* - `{ words }`: pre-counted (bypass extraction entirely).
|
|
13
|
+
*/
|
|
14
|
+
type ReadTarget = string | Locator | {
|
|
15
|
+
readonly text: string;
|
|
16
|
+
} | {
|
|
17
|
+
readonly words: number;
|
|
18
|
+
};
|
|
19
|
+
interface ReadOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Reading mode. Built-in multipliers on top of `personality.reading.wpm`:
|
|
22
|
+
* - `'prose'`: 1.0× (default for non-code targets)
|
|
23
|
+
* - `'code'`: 0.4× (default when target is a `<pre>` or `<code>` element)
|
|
24
|
+
* - `'scan'`: 1.8× (skim mode, must be explicit)
|
|
25
|
+
*
|
|
26
|
+
* **Smart defaults:** when `kind` is omitted AND the target is a Locator/
|
|
27
|
+
* selector, the adapter inspects the resolved element's tag — `<pre>` and
|
|
28
|
+
* `<code>` auto-detect as `'code'`; everything else as `'prose'`. An
|
|
29
|
+
* explicit `kind` always wins over auto-detection.
|
|
30
|
+
*/
|
|
31
|
+
readonly kind?: ReadKind;
|
|
32
|
+
/** Override the effective WPM multiplier directly. Wins over `kind`. */
|
|
33
|
+
readonly wpmMultiplier?: number;
|
|
34
|
+
/**
|
|
35
|
+
* For selector/Locator targets: scroll the element into the viewport
|
|
36
|
+
* before the dwell. A user can't read what isn't on screen. Defaults to
|
|
37
|
+
* `false` — in most flows the caller has already scrolled there.
|
|
38
|
+
*/
|
|
39
|
+
readonly scrollIntoView?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* For selector/Locator targets: trace a humanized cursor path through the
|
|
42
|
+
* target's bounding box while the dwell elapses, like an eye tracking
|
|
43
|
+
* lines of text. **Defaults to `true`** — "reading" implies looking, and
|
|
44
|
+
* looking implies motion. Pass `false` to skip motion when you only
|
|
45
|
+
* care about the temporal pattern (typical AI-agent use case where
|
|
46
|
+
* the cursor position is irrelevant).
|
|
47
|
+
*
|
|
48
|
+
* The scan path is generated by `planReadingScan` from `@humanjs/core`
|
|
49
|
+
* (same deterministic-by-seed contract as the rest of the library) and
|
|
50
|
+
* walked over the computed dwell duration, so motion finishes exactly as
|
|
51
|
+
* the read does. The cursor's tracked position is updated to the scan's
|
|
52
|
+
* endpoint so the next click starts from where the eye landed.
|
|
53
|
+
*
|
|
54
|
+
* Ignored when the target is `{ text }` or `{ words }` (no bounding box).
|
|
55
|
+
*/
|
|
56
|
+
readonly withMotion?: boolean;
|
|
57
|
+
}
|
|
58
|
+
/** Outcome of a read, returned to the caller for observability. */
|
|
59
|
+
interface ReadResult {
|
|
60
|
+
/** Number of words counted (after whitespace splitting / from caller's `{ words }`). */
|
|
61
|
+
readonly words: number;
|
|
62
|
+
/** Sleep duration in ms. Zero in `speed: 'instant'` or for zero-word inputs. */
|
|
63
|
+
readonly durationMs: number;
|
|
64
|
+
/** Final reading kind used (after auto-detection + explicit-override resolution). */
|
|
65
|
+
readonly kind: ReadKind;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A captured frame in the timer-based capture session. `tMs` is the
|
|
70
|
+
* wall-clock offset (ms) from the capture start.
|
|
71
|
+
*/
|
|
72
|
+
interface CapturedFrame {
|
|
73
|
+
readonly path: string;
|
|
74
|
+
readonly tMs: number;
|
|
75
|
+
}
|
|
76
|
+
/** Outcome of a finalized capture session. */
|
|
77
|
+
interface CaptureResult {
|
|
78
|
+
readonly dir: string;
|
|
79
|
+
readonly frames: readonly CapturedFrame[];
|
|
80
|
+
readonly startedAtMs: number;
|
|
81
|
+
readonly stoppedAtMs: number;
|
|
82
|
+
readonly format: 'jpeg' | 'png';
|
|
83
|
+
readonly fps: number;
|
|
84
|
+
/**
|
|
85
|
+
* Removes the temp directory + all frames. Called by `Recording.dispose()`
|
|
86
|
+
* and by the sweep-on-exit handler — not by the exporters, which are
|
|
87
|
+
* repeatable and read the frames without consuming them. Also called
|
|
88
|
+
* directly by the error path in `human.record()` when the callback throws.
|
|
89
|
+
*/
|
|
90
|
+
cleanup(): Promise<void>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Encoding quality preset. Picks the per-frame capture quality + the
|
|
95
|
+
* ffmpeg encode settings used to assemble them into a video.
|
|
96
|
+
*
|
|
97
|
+
* - `'fast'` — JPEG q=85, CRF 23, preset fast (iteration)
|
|
98
|
+
* - `'standard'` — JPEG q=90, CRF 20, preset fast (balanced)
|
|
99
|
+
* - `'high'` (default) — JPEG q=95, CRF 18, preset slow, tune animation (marketing-grade)
|
|
100
|
+
* - `'lossless'` — PNG capture, CRF 12, preset veryslow (archival; huge temp files)
|
|
101
|
+
*/
|
|
102
|
+
type RecordingQuality = 'fast' | 'high' | 'lossless' | 'standard';
|
|
103
|
+
/** ffmpeg `-preset` values, ordered from fastest to slowest. */
|
|
104
|
+
type FfmpegPreset = 'fast' | 'faster' | 'medium' | 'slow' | 'slower' | 'superfast' | 'ultrafast' | 'veryfast' | 'veryslow';
|
|
105
|
+
/** ffmpeg `-tune` values for libx264. */
|
|
106
|
+
type FfmpegTune = 'animation' | 'fastdecode' | 'film' | 'grain' | 'stillimage' | 'zerolatency';
|
|
107
|
+
/** Options for {@link Recording.toVideo}. */
|
|
108
|
+
interface ToVideoOptions {
|
|
109
|
+
/** Encoding quality preset. Defaults to `'high'`. */
|
|
110
|
+
readonly quality?: RecordingQuality;
|
|
111
|
+
/** Override CRF (0–51, lower = better). Defaults to the quality preset's CRF. */
|
|
112
|
+
readonly crf?: number;
|
|
113
|
+
/** Override ffmpeg `-preset`. Defaults to the quality preset's preset. */
|
|
114
|
+
readonly preset?: FfmpegPreset;
|
|
115
|
+
/** Override ffmpeg `-tune`. Defaults to the quality preset's tune (if any). */
|
|
116
|
+
readonly tune?: FfmpegTune;
|
|
117
|
+
}
|
|
118
|
+
/** Options for {@link Recording.toGif}. */
|
|
119
|
+
interface ToGifOptions {
|
|
120
|
+
/**
|
|
121
|
+
* Frames per second of the output GIF. Defaults to `15` — high enough for
|
|
122
|
+
* smooth motion in most renderers, low enough to keep file size sane.
|
|
123
|
+
* Renderers cap GIF playback around 50fps anyway.
|
|
124
|
+
*/
|
|
125
|
+
readonly fps?: number;
|
|
126
|
+
/**
|
|
127
|
+
* Scale the GIF to this width (pixels). Height is computed to preserve
|
|
128
|
+
* aspect ratio. Omit to keep the source viewport size — but most embedded
|
|
129
|
+
* GIFs (README, PR, Slack) look fine at 640–960px and weigh a fraction.
|
|
130
|
+
*/
|
|
131
|
+
readonly width?: number;
|
|
132
|
+
}
|
|
133
|
+
/** One action captured during a recording, as emitted in {@link Timeline.events}. */
|
|
134
|
+
interface TimelineEvent {
|
|
135
|
+
readonly type: string;
|
|
136
|
+
readonly params: Readonly<Record<string, unknown>>;
|
|
137
|
+
/** Offset (ms) from the recording start when the action began. */
|
|
138
|
+
readonly tMs: number;
|
|
139
|
+
readonly durationMs: number;
|
|
140
|
+
/** Error message, present only if the action threw. */
|
|
141
|
+
readonly error?: string;
|
|
142
|
+
}
|
|
143
|
+
/** Structured action timeline of a recording. */
|
|
144
|
+
interface Timeline {
|
|
145
|
+
readonly version: 1;
|
|
146
|
+
readonly personality: string;
|
|
147
|
+
readonly seed: string | null;
|
|
148
|
+
readonly speed: string;
|
|
149
|
+
readonly durationMs: number;
|
|
150
|
+
readonly events: readonly TimelineEvent[];
|
|
151
|
+
}
|
|
152
|
+
/** Metadata passed from `human.record()` into the Recording constructor. */
|
|
153
|
+
interface RecordingTimelineSource {
|
|
154
|
+
readonly personality: string;
|
|
155
|
+
readonly seed: string | null;
|
|
156
|
+
readonly speed: string;
|
|
157
|
+
readonly events: readonly TimelineEvent[];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* A recorded window of a humanized session. Returned by `human.record(cb)`.
|
|
161
|
+
*
|
|
162
|
+
* A Recording can hold a frame capture (`hasVideo === true`) OR be
|
|
163
|
+
* timeline-only (`hasVideo === false`). `toVideo()` / `toGif()` require a
|
|
164
|
+
* capture; `toTimeline()` and `.timeline` work either way.
|
|
165
|
+
*
|
|
166
|
+
* The video exporters are repeatable and interleavable — they read the
|
|
167
|
+
* captured frames without consuming them. Calling `dispose()` is OPTIONAL:
|
|
168
|
+
* the captured-frames temp dir is also swept by a `process.on('exit')`
|
|
169
|
+
* handler installed on first use, so casual scripts can skip it entirely.
|
|
170
|
+
* Call `dispose()` (or use `await using`) when you want to release the
|
|
171
|
+
* frames proactively — long-running services, batch jobs, etc.
|
|
172
|
+
*/
|
|
173
|
+
declare class Recording {
|
|
174
|
+
#private;
|
|
175
|
+
constructor(capture: CaptureResult | null, windowStartMs: number, windowEndMs: number, timelineSource: RecordingTimelineSource);
|
|
176
|
+
/** Wall-clock duration of the recorded window. */
|
|
177
|
+
get durationMs(): number;
|
|
178
|
+
/** True if frames were captured during this recording. */
|
|
179
|
+
get hasVideo(): boolean;
|
|
180
|
+
/**
|
|
181
|
+
* The structured action timeline of this recording — same data that
|
|
182
|
+
* `toTimeline()` writes to disk.
|
|
183
|
+
*/
|
|
184
|
+
get timeline(): Timeline;
|
|
185
|
+
/**
|
|
186
|
+
* Assembles the captured frames into a video at `outputPath`. The output
|
|
187
|
+
* format is inferred from the extension — `.mp4` (H.264, re-encoded
|
|
188
|
+
* with the configured quality) or `.webm` (VP9).
|
|
189
|
+
*
|
|
190
|
+
* Repeatable and interleavable with `toGif()` — the frame source is read,
|
|
191
|
+
* not consumed. Frames live until you call `dispose()` (or `await using`
|
|
192
|
+
* goes out of scope, or the process exits and the OS reaps `tmpdir`).
|
|
193
|
+
*
|
|
194
|
+
* @returns the resolved output path.
|
|
195
|
+
*/
|
|
196
|
+
toVideo(outputPath: string, options?: ToVideoOptions): Promise<string>;
|
|
197
|
+
/**
|
|
198
|
+
* Assembles the captured frames into an animated GIF at `outputPath`.
|
|
199
|
+
* Optimized for embedding in READMEs, PRs, Slack, and docs — uses a
|
|
200
|
+
* per-recording palette (`palettegen` + `paletteuse`) with Bayer dithering
|
|
201
|
+
* so gradients stay smooth without exploding the file size.
|
|
202
|
+
*
|
|
203
|
+
* Repeatable and interleavable with `toVideo()` — call them in any order,
|
|
204
|
+
* any number of times. Frames live until you call `dispose()`.
|
|
205
|
+
*
|
|
206
|
+
* @returns the resolved output path.
|
|
207
|
+
*/
|
|
208
|
+
toGif(outputPath: string, options?: ToGifOptions): Promise<string>;
|
|
209
|
+
/**
|
|
210
|
+
* Writes the structured action timeline to `outputPath` as JSON.
|
|
211
|
+
* Independent of `toVideo()` / `toGif()` — call before, after, in between,
|
|
212
|
+
* or instead. Safe to call multiple times. Unaffected by `dispose()`
|
|
213
|
+
* (the timeline lives in memory, not in the captured-frames temp dir).
|
|
214
|
+
*
|
|
215
|
+
* @returns the resolved output path.
|
|
216
|
+
*/
|
|
217
|
+
toTimeline(outputPath: string): Promise<string>;
|
|
218
|
+
/**
|
|
219
|
+
* Releases the captured-frames temp directory. After this call, `toVideo()`
|
|
220
|
+
* and `toGif()` throw — but `toTimeline()` and the in-memory `timeline`
|
|
221
|
+
* still work because those don't depend on the frames.
|
|
222
|
+
*
|
|
223
|
+
* **Optional.** A process-exit handler also sweeps any un-disposed frame
|
|
224
|
+
* dirs, so casual scripts can skip this entirely. Call it explicitly when
|
|
225
|
+
* you want to release frames proactively (long-running services, batch
|
|
226
|
+
* jobs, or anywhere you want predictable disk usage).
|
|
227
|
+
*
|
|
228
|
+
* Idempotent. Safe to call on a Recording that never had a capture
|
|
229
|
+
* (timeline-only mode) — no-op there.
|
|
230
|
+
*
|
|
231
|
+
* Also wired to `Symbol.asyncDispose`, so the explicit-resource-management
|
|
232
|
+
* `await using` syntax (TypeScript ≥ 5.2 / Node ≥ 20.4) works:
|
|
233
|
+
*
|
|
234
|
+
* ```ts
|
|
235
|
+
* await using rec = await human.record(fn);
|
|
236
|
+
* await rec.toVideo('demo.mp4');
|
|
237
|
+
* await rec.toGif('demo.gif');
|
|
238
|
+
* // frames cleaned up automatically when `rec` goes out of scope
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
dispose(): Promise<void>;
|
|
242
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* What / where to scroll. The chosen axis (`'y'` by default, `'x'` via
|
|
247
|
+
* `options.axis`) decides whether targets resolve along the vertical or
|
|
248
|
+
* horizontal axis.
|
|
249
|
+
*
|
|
250
|
+
* - `'natural'`: scroll one viewport along the chosen axis (default).
|
|
251
|
+
* - `'end'`: scroll to the far edge (bottom for `'y'`, right for `'x'`).
|
|
252
|
+
* - `'top'`: scroll to the origin (top for `'y'`, left for `'x'`).
|
|
253
|
+
* - `string`: a Playwright-compatible selector — scroll until in view.
|
|
254
|
+
* - `Locator`: same, but you already have a Locator handle.
|
|
255
|
+
* - `{ by: number }`: relative pixel delta (negative = up / left).
|
|
256
|
+
* - `{ to: number }`: absolute scroll position on the chosen axis.
|
|
257
|
+
*/
|
|
258
|
+
type ScrollTarget = 'natural' | 'end' | 'top' | string | Locator | {
|
|
259
|
+
readonly by: number;
|
|
260
|
+
} | {
|
|
261
|
+
readonly to: number;
|
|
262
|
+
};
|
|
263
|
+
interface ScrollOptions {
|
|
264
|
+
/**
|
|
265
|
+
* Force an overshoot + correction even if the personality wouldn't choose
|
|
266
|
+
* one. Useful when you want the humanization signal regardless of
|
|
267
|
+
* personality (e.g. demos).
|
|
268
|
+
*/
|
|
269
|
+
readonly overshoot?: boolean;
|
|
270
|
+
/**
|
|
271
|
+
* Disable mid-scroll micro-pauses. Defaults to `true` (pauses enabled).
|
|
272
|
+
* Set `false` for the smoothest possible motion.
|
|
273
|
+
*/
|
|
274
|
+
readonly withPauses?: boolean;
|
|
275
|
+
/**
|
|
276
|
+
* For element targets only: where to align the element along the chosen
|
|
277
|
+
* axis when the scroll ends. Mirrors `scrollIntoView({ block })`.
|
|
278
|
+
* Defaults to `'start'`.
|
|
279
|
+
*
|
|
280
|
+
* - `'start'`: element's leading edge aligns with the viewport's leading
|
|
281
|
+
* edge (top for `axis: 'y'`, left for `axis: 'x'`)
|
|
282
|
+
* - `'center'`: element centers in the viewport
|
|
283
|
+
* - `'end'`: element's trailing edge aligns with the viewport's trailing
|
|
284
|
+
* edge (bottom for `'y'`, right for `'x'`)
|
|
285
|
+
* - `'nearest'`: do the minimum scroll — stay put if the element is
|
|
286
|
+
* already fully visible, otherwise scroll to the closest edge
|
|
287
|
+
*/
|
|
288
|
+
readonly block?: 'start' | 'center' | 'end' | 'nearest';
|
|
289
|
+
/**
|
|
290
|
+
* Scroll inside a scrollable container instead of the page. Accepts a
|
|
291
|
+
* Playwright-compatible selector or a built `Locator`. Every scroll
|
|
292
|
+
* semantic (`'natural'`, `'top'`, `'end'`, `{ by }`, `{ to }`, element
|
|
293
|
+
* targets, `block` alignment) applies relative to the container.
|
|
294
|
+
*
|
|
295
|
+
* In humanized speed modes, the cursor parks over the container's
|
|
296
|
+
* center so the visible cursor reads as "human hand on the wheel"
|
|
297
|
+
* (when paired with `installMouseHelper`), and each planned segment
|
|
298
|
+
* advances the container's `scrollLeft` / `scrollTop` directly. Direct
|
|
299
|
+
* property assignment is more reliable than dispatching wheel events
|
|
300
|
+
* into nested overflow containers — Playwright's synthetic wheel
|
|
301
|
+
* events don't always route past the cursor element. In `'instant'`
|
|
302
|
+
* mode, the container's scroll position is set with a single
|
|
303
|
+
* `scrollTo` call.
|
|
304
|
+
*
|
|
305
|
+
* Common use: chat threads, modal bodies, infinite-scroll feeds, any
|
|
306
|
+
* `<div style="overflow-y: auto">` that owns its own scrollbar.
|
|
307
|
+
*/
|
|
308
|
+
readonly within?: string | Locator;
|
|
309
|
+
/**
|
|
310
|
+
* Which axis to scroll along. Defaults to `'y'` (vertical).
|
|
311
|
+
*
|
|
312
|
+
* Set `'x'` for horizontal scrolling — carousels, kanban boards,
|
|
313
|
+
* sideways galleries. Every target shape still works: `'natural'`
|
|
314
|
+
* scrolls one viewport-width right, `'top'` jumps to scrollLeft 0,
|
|
315
|
+
* `'end'` to (scrollWidth - clientWidth), `{ by }` / `{ to }` apply to
|
|
316
|
+
* the X-axis, and element targets scroll until the element is visible
|
|
317
|
+
* horizontally.
|
|
318
|
+
*/
|
|
319
|
+
readonly axis?: 'x' | 'y';
|
|
320
|
+
}
|
|
321
|
+
/** Outcome of a scroll, returned to the caller for observability. */
|
|
322
|
+
interface ScrollResult {
|
|
323
|
+
/** Starting scroll position along the chosen axis (scrollY or scrollX). */
|
|
324
|
+
readonly from: number;
|
|
325
|
+
/** Final scroll position the scroll aimed for, along the chosen axis. */
|
|
326
|
+
readonly to: number;
|
|
327
|
+
/** Signed pixel distance (`to - from`). */
|
|
328
|
+
readonly distance: number;
|
|
329
|
+
/** Total elapsed dwell + motion time in ms. Zero in `speed: 'instant'`. */
|
|
330
|
+
readonly durationMs: number;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Options for {@link installMouseHelper}. */
|
|
334
|
+
interface InstallMouseHelperOptions {
|
|
335
|
+
/** Cursor fill color. Defaults to the HumanJS amber `#f5a55c`. */
|
|
336
|
+
readonly color?: string;
|
|
337
|
+
/** Cursor visual size in pixels. Defaults to 22. */
|
|
338
|
+
readonly size?: number;
|
|
339
|
+
/**
|
|
340
|
+
* Render a ripple at each `mousedown` position so clicks read on video.
|
|
341
|
+
* Defaults to `true`.
|
|
342
|
+
*/
|
|
343
|
+
readonly showClicks?: boolean;
|
|
344
|
+
/** Halo opacity behind the cursor. Defaults to `0.18`. */
|
|
345
|
+
readonly haloOpacity?: number;
|
|
346
|
+
}
|
|
347
|
+
declare function installMouseHelper(target: BrowserContext | Page, options?: InstallMouseHelperOptions): Promise<void>;
|
|
4
348
|
|
|
5
349
|
/**
|
|
6
350
|
* How fast the humanized session runs.
|
|
@@ -49,6 +393,136 @@ interface Human {
|
|
|
49
393
|
* native `locator.click()` is used directly.
|
|
50
394
|
*/
|
|
51
395
|
click(target: Locator | string): Promise<void>;
|
|
396
|
+
/**
|
|
397
|
+
* Type `value` into `target` with humanized per-key timing, optional typo
|
|
398
|
+
* injection (with backspace recovery), and occasional think-pauses.
|
|
399
|
+
*
|
|
400
|
+
* Per-key `keydown`/`press`/`up` events fire for each character, so
|
|
401
|
+
* handlers like autocomplete dropdowns still receive every keystroke.
|
|
402
|
+
*
|
|
403
|
+
* In `speed: 'instant'`, falls back to `locator.pressSequentially` with
|
|
404
|
+
* zero inter-key delay — events still fire, but humanization is skipped.
|
|
405
|
+
*/
|
|
406
|
+
type(target: Locator | string, value: string): Promise<void>;
|
|
407
|
+
/**
|
|
408
|
+
* Dwell as if reading `target` — the third pillar of humanization after
|
|
409
|
+
* the cursor and the keyboard. Real users pause to read; HumanJS models
|
|
410
|
+
* that pause from word count + the personality's reading WPM (+ jitter).
|
|
411
|
+
*
|
|
412
|
+
* **Targets:**
|
|
413
|
+
* - `string`: a Playwright-compatible selector (matches `click()`/`type()`).
|
|
414
|
+
* - `Locator`: a pre-built Locator.
|
|
415
|
+
* - `{ text }`: literal text in hand (no DOM lookup).
|
|
416
|
+
* - `{ words }`: pre-counted — skip text extraction entirely.
|
|
417
|
+
*
|
|
418
|
+
* **Smart defaults** (only when the caller doesn't override):
|
|
419
|
+
* - `kind` auto-detects as `'code'` for `<pre>` and `<code>` tags;
|
|
420
|
+
* everything else falls back to `'prose'`. Explicit `kind` always wins.
|
|
421
|
+
* - `scrollIntoView: false` — most flows already scrolled to the content.
|
|
422
|
+
*
|
|
423
|
+
* Plugins observe `'read'` actions with `{ target, words, kind }` in params.
|
|
424
|
+
* The text content itself is never echoed — passwords, tokens, and other
|
|
425
|
+
* sensitive strings stay out of telemetry by default.
|
|
426
|
+
*
|
|
427
|
+
* In `speed: 'instant'`, dwell collapses to 0 ms but the action still fires
|
|
428
|
+
* so observability stays consistent.
|
|
429
|
+
*
|
|
430
|
+
* Returns a {@link ReadResult} with the word count, final kind (after
|
|
431
|
+
* auto-detection), and total dwell duration — useful for assertions in
|
|
432
|
+
* tests or for surfacing reading metadata to a UI.
|
|
433
|
+
*/
|
|
434
|
+
read(target: ReadTarget, options?: ReadOptions): Promise<ReadResult>;
|
|
435
|
+
/**
|
|
436
|
+
* Scroll the page (or a scrollable container) humanly. Multi-segment
|
|
437
|
+
* motion with a bell-curve velocity profile, optional mid-scroll
|
|
438
|
+
* micro-pauses, and (for the `distracted` personality) the occasional
|
|
439
|
+
* overshoot + correction.
|
|
440
|
+
*
|
|
441
|
+
* **Targets:**
|
|
442
|
+
* - `'natural'` (default): scroll one viewport in the chosen axis
|
|
443
|
+
* - `'end'` / `'top'`: jump to the document/container edges, humanized
|
|
444
|
+
* - `string`: a Playwright-compatible selector — scroll until in view
|
|
445
|
+
* - `Locator`: same, but with a pre-built handle
|
|
446
|
+
* - `{ by: n }`: relative pixel delta (negative = up / left)
|
|
447
|
+
* - `{ to: n }`: absolute scroll position on the chosen axis
|
|
448
|
+
*
|
|
449
|
+
* **Options:** `axis: 'x' | 'y'` (default `'y'`) picks the direction;
|
|
450
|
+
* `within: selector | Locator` scopes the scroll to a scrollable
|
|
451
|
+
* container; `block: 'start' | 'center' | 'end' | 'nearest'` aligns
|
|
452
|
+
* element targets; `overshoot` / `withPauses` toggle individual
|
|
453
|
+
* humanization signals.
|
|
454
|
+
*
|
|
455
|
+
* Plugins observe `'scroll'` actions with `{ target }` in
|
|
456
|
+
* `beforeAction`'s params (a human-readable description of the target).
|
|
457
|
+
* The full {@link ScrollResult} (`from` / `to` / `distance` /
|
|
458
|
+
* `durationMs`) is available via `afterAction`'s `result` argument.
|
|
459
|
+
*
|
|
460
|
+
* In `speed: 'instant'`, the page (or container) is moved with a single
|
|
461
|
+
* `scrollTo` call. No wheel events, no segments — but the action still
|
|
462
|
+
* fires for observability.
|
|
463
|
+
*
|
|
464
|
+
* Returns a {@link ScrollResult} for assertions in tests.
|
|
465
|
+
*/
|
|
466
|
+
scroll(target?: ScrollTarget, options?: ScrollOptions): Promise<ScrollResult>;
|
|
467
|
+
/**
|
|
468
|
+
* Pause for `ms` milliseconds. Equivalent to importing `sleep` from
|
|
469
|
+
* `@humanjs/playwright` and calling it — exposed on the Human instance
|
|
470
|
+
* so users who already have `human` in scope don't need an extra import.
|
|
471
|
+
*
|
|
472
|
+
* Not a humanized action: this is a raw `setTimeout` wait, not scaled
|
|
473
|
+
* by personality or speed mode, and no plugin events fire. Use it for
|
|
474
|
+
* generic pacing between humanized actions.
|
|
475
|
+
*/
|
|
476
|
+
sleep(ms: number): Promise<void>;
|
|
477
|
+
/**
|
|
478
|
+
* Records `fn`'s actions. Returns a {@link Recording} you can export
|
|
479
|
+
* to mp4/webm video (`toVideo`), JSON timeline (`toTimeline`), or both.
|
|
480
|
+
*
|
|
481
|
+
* By default, frames are captured from the browser via timer-polled
|
|
482
|
+
* `page.screenshot()` — fully controllable quality, not limited by
|
|
483
|
+
* Playwright's recordVideo bitrate ceiling. Pass `{ video: false }` to
|
|
484
|
+
* skip capture entirely (timeline-only mode, zero video overhead).
|
|
485
|
+
*
|
|
486
|
+
* Plugins observe `'record'` in `beforeAction` / `afterAction` so
|
|
487
|
+
* recording shows up alongside the other primitives in observability
|
|
488
|
+
* pipelines.
|
|
489
|
+
*
|
|
490
|
+
* Single-use per session: `Recording.toVideo()` finalizes the captured
|
|
491
|
+
* frames, so calling `human.record()` twice on the same human throws.
|
|
492
|
+
*
|
|
493
|
+
* @example
|
|
494
|
+
* ```ts
|
|
495
|
+
* // Default: video + timeline
|
|
496
|
+
* const rec = await human.record(async () => {
|
|
497
|
+
* await human.click('#login');
|
|
498
|
+
* });
|
|
499
|
+
* await rec.toVideo('demo.mp4');
|
|
500
|
+
* await rec.toTimeline('demo.json');
|
|
501
|
+
*
|
|
502
|
+
* // Timeline-only, no video overhead
|
|
503
|
+
* const rec = await human.record({ video: false }, async () => {
|
|
504
|
+
* await human.click('#login');
|
|
505
|
+
* });
|
|
506
|
+
* await rec.toTimeline('demo.json');
|
|
507
|
+
* ```
|
|
508
|
+
*/
|
|
509
|
+
record(fn: () => Promise<void>): Promise<Recording>;
|
|
510
|
+
record(options: HumanRecordOptions, fn: () => Promise<void>): Promise<Recording>;
|
|
511
|
+
}
|
|
512
|
+
/** Options for {@link Human.record}. */
|
|
513
|
+
interface HumanRecordOptions {
|
|
514
|
+
/**
|
|
515
|
+
* Whether to capture frames for video output. Defaults to `true`.
|
|
516
|
+
* Set to `false` for timeline-only recordings (no capture loop, no
|
|
517
|
+
* temp files, no encoding overhead — `toTimeline()` still works).
|
|
518
|
+
*/
|
|
519
|
+
readonly video?: boolean;
|
|
520
|
+
/**
|
|
521
|
+
* Capture quality preset. Controls per-frame JPEG quality (or PNG for
|
|
522
|
+
* lossless) AND the ffmpeg encode settings used by `toVideo()`.
|
|
523
|
+
* Defaults to `'high'`. Ignored when `video: false`.
|
|
524
|
+
*/
|
|
525
|
+
readonly quality?: RecordingQuality;
|
|
52
526
|
}
|
|
53
527
|
/**
|
|
54
528
|
* Creates a humanized session bound to a Playwright `Page`.
|
|
@@ -71,4 +545,4 @@ interface Human {
|
|
|
71
545
|
*/
|
|
72
546
|
declare function createHuman(page: Page, options?: CreateHumanOptions): Promise<Human>;
|
|
73
547
|
|
|
74
|
-
export { type CreateHumanOptions, type Human, type Speed, createHuman };
|
|
548
|
+
export { type CreateHumanOptions, type FfmpegPreset, type FfmpegTune, type Human, type HumanRecordOptions, type InstallMouseHelperOptions, type ReadOptions, type ReadResult, type ReadTarget, Recording, type RecordingQuality, type ScrollOptions, type ScrollResult, type ScrollTarget, type Speed, type Timeline, type TimelineEvent, type ToGifOptions, type ToVideoOptions, createHuman, installMouseHelper };
|