@humanjs/playwright 0.9.0 → 0.10.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.
@@ -1,4 +1,4 @@
1
- import { resolvePersonality, createRng, sleep as sleep$1, planScroll, countWords, computeReadingDwellMs, planReadingScan, planTypeKeystrokes, bezierPath, humanizePath } from '@humanjs/core';
1
+ import { sleep as sleep$1, resolvePersonality, createRng, planScroll, countWords, computeReadingDwellMs, planReadingScan, planTypeKeystrokes, bezierPath, humanizePath } from '@humanjs/core';
2
2
  export { applyMicroJitter, applyVelocityProfile, bezierPath, blend, careful, computeReadingDwellMs, countWords, createRng, distracted, fast, humanizePath, planScroll, planTypeKeystrokes, precise, resolvePersonality, sleep } from '@humanjs/core';
3
3
  import { spawn } from 'child_process';
4
4
  import { rmSync } from 'fs';
@@ -940,8 +940,17 @@ async function detectKindFromTag(locator) {
940
940
  return void 0;
941
941
  }
942
942
 
943
- // src/recording/codegen.ts
943
+ // src/recording/targets.ts
944
944
  var POINT_RE = /^point\((-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)\)$/;
945
+ function parsePointTarget(desc) {
946
+ const match = String(desc ?? "").match(POINT_RE);
947
+ return match ? { x: Number(match[1]), y: Number(match[2]) } : null;
948
+ }
949
+ function resolveMouseTarget(desc) {
950
+ return parsePointTarget(desc) ?? String(desc ?? "");
951
+ }
952
+
953
+ // src/recording/codegen.ts
945
954
  var POINT_COMMENT = " // raw coordinate \u2014 replace with a locator for a stable selector";
946
955
  var UNCAPTURED_COMMENT = " // input not captured (masked or captureInputs disabled) \u2014 fill in (e.g. process.env.X)";
947
956
  function q(value) {
@@ -1177,6 +1186,150 @@ ${todo}
1177
1186
  });
1178
1187
  `;
1179
1188
  }
1189
+ function abortError() {
1190
+ const error = new Error("Replay aborted");
1191
+ error.name = "AbortError";
1192
+ return error;
1193
+ }
1194
+ async function replayTimeline(page, timeline, options = {}) {
1195
+ const events = Array.isArray(timeline) ? timeline : timeline.events;
1196
+ const { onStep, signal } = options;
1197
+ if (signal?.aborted) throw abortError();
1198
+ const human = await createHuman(page, {
1199
+ personality: options.personality ?? "careful",
1200
+ speed: options.speed ?? "human",
1201
+ ...options.seed !== void 0 ? { seed: options.seed } : {},
1202
+ ...options.cursor !== void 0 ? { cursor: options.cursor } : {}
1203
+ });
1204
+ const startedAt = Date.now();
1205
+ const steps = [];
1206
+ for (const [index, event] of events.entries()) {
1207
+ if (signal?.aborted) throw abortError();
1208
+ onStep?.({ index, type: event.type, status: "running" });
1209
+ try {
1210
+ await runEvent(human, page, event);
1211
+ } catch (cause) {
1212
+ const error = cause instanceof Error ? cause.message : String(cause);
1213
+ steps.push({ index, type: event.type, status: "fail", error });
1214
+ onStep?.({ index, type: event.type, status: "fail", error });
1215
+ return { status: "fail", steps, failedIndex: index, durationMs: Date.now() - startedAt };
1216
+ }
1217
+ steps.push({ index, type: event.type, status: "pass" });
1218
+ onStep?.({ index, type: event.type, status: "pass" });
1219
+ }
1220
+ return { status: "pass", steps, durationMs: Date.now() - startedAt };
1221
+ }
1222
+ function parseScrollTarget(target) {
1223
+ const value = String(target ?? "natural");
1224
+ const by = value.match(/^by:(-?\d+(?:\.\d+)?)$/);
1225
+ if (by) return { by: Number(by[1]) };
1226
+ const to = value.match(/^to:(-?\d+(?:\.\d+)?)$/);
1227
+ if (to) return { to: Number(to[1]) };
1228
+ return value;
1229
+ }
1230
+ var normalizeText = (value) => (value ?? "").replace(/\s+/g, " ").trim();
1231
+ async function runAssert(page, params) {
1232
+ const kind = String(params.kind ?? "visible");
1233
+ if (kind === "url") {
1234
+ const actual = page.url();
1235
+ const expected = String(params.value ?? "");
1236
+ if (actual !== expected) {
1237
+ throw new Error(`expected URL ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
1238
+ }
1239
+ return;
1240
+ }
1241
+ const locator = page.locator(String(params.target ?? "")).first();
1242
+ await locator.waitFor({ state: "visible" });
1243
+ if (kind === "text") {
1244
+ const actual = normalizeText(await locator.textContent());
1245
+ const expected = normalizeText(String(params.value ?? ""));
1246
+ if (actual !== expected) {
1247
+ throw new Error(`expected text ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
1248
+ }
1249
+ }
1250
+ }
1251
+ async function runEvent(human, page, event) {
1252
+ const p = event.params;
1253
+ switch (event.type) {
1254
+ case "goto":
1255
+ await human.goto(String(p.url ?? ""));
1256
+ return;
1257
+ case "click":
1258
+ await human.click(resolveMouseTarget(p.target));
1259
+ return;
1260
+ case "rightClick":
1261
+ await human.rightClick(resolveMouseTarget(p.target));
1262
+ return;
1263
+ case "doubleClick":
1264
+ await human.doubleClick(resolveMouseTarget(p.target));
1265
+ return;
1266
+ case "move":
1267
+ await human.move(resolveMouseTarget(p.target));
1268
+ return;
1269
+ case "hover":
1270
+ await human.hover(String(p.target ?? ""));
1271
+ return;
1272
+ case "drag":
1273
+ await human.drag(resolveMouseTarget(p.from), resolveMouseTarget(p.to));
1274
+ return;
1275
+ case "type":
1276
+ await human.type(String(p.target ?? ""), event.inputValue ?? "");
1277
+ return;
1278
+ case "paste":
1279
+ await human.paste(String(p.target ?? ""), event.inputValue ?? "");
1280
+ return;
1281
+ case "clear":
1282
+ await human.clear(String(p.target ?? ""));
1283
+ return;
1284
+ case "check":
1285
+ await human.check(String(p.target ?? ""));
1286
+ return;
1287
+ case "uncheck":
1288
+ await human.uncheck(String(p.target ?? ""));
1289
+ return;
1290
+ case "selectText":
1291
+ await human.selectText(
1292
+ String(p.target ?? ""),
1293
+ typeof p.text === "string" ? { text: p.text } : void 0
1294
+ );
1295
+ return;
1296
+ case "selectOption":
1297
+ await human.selectOption(String(p.target ?? ""), p.values);
1298
+ return;
1299
+ case "upload":
1300
+ await human.upload(String(p.target ?? ""), p.files);
1301
+ return;
1302
+ case "press":
1303
+ await human.press(String(p.key ?? ""));
1304
+ return;
1305
+ case "scroll":
1306
+ await human.scroll(parseScrollTarget(p.target));
1307
+ return;
1308
+ case "read": {
1309
+ const target = String(p.target ?? "");
1310
+ if (/^\d+ words$/.test(target) || /^text:\d+ chars$/.test(target)) return;
1311
+ await human.read(target);
1312
+ return;
1313
+ }
1314
+ case "sleep":
1315
+ await sleep$1(Number(p.ms) || 0);
1316
+ return;
1317
+ case "reload":
1318
+ await human.reload();
1319
+ return;
1320
+ case "goBack":
1321
+ await human.goBack();
1322
+ return;
1323
+ case "goForward":
1324
+ await human.goForward();
1325
+ return;
1326
+ case "assert":
1327
+ await runAssert(page, p);
1328
+ return;
1329
+ default:
1330
+ return;
1331
+ }
1332
+ }
1180
1333
 
1181
1334
  // src/recording/index.ts
1182
1335
  var pendingFrameCleanups = /* @__PURE__ */ new Set();
@@ -1992,6 +2145,6 @@ function describeReadTarget(target) {
1992
2145
  return target.toString?.() ?? "locator";
1993
2146
  }
1994
2147
 
1995
- export { Recording, createHuman, generateHumanJS, generatePlaywrightTest, installMouseHelper };
1996
- //# sourceMappingURL=chunk-3TXDODCO.js.map
1997
- //# sourceMappingURL=chunk-3TXDODCO.js.map
2148
+ export { Recording, createHuman, generateHumanJS, generatePlaywrightTest, installMouseHelper, replayTimeline };
2149
+ //# sourceMappingURL=chunk-I2PQGZU7.js.map
2150
+ //# sourceMappingURL=chunk-I2PQGZU7.js.map