effect-browser 0.2.0-beta.3 → 0.2.0-beta.5

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.
@@ -1842,6 +1842,10 @@ const nativeSelection = (files) => {
1842
1842
  */
1843
1843
  const makeActions = (context, targets, observation, isTimeoutError) => {
1844
1844
  const { current } = targets;
1845
+ let invalidatePointer = (_page) => {};
1846
+ const setPointerInvalidator = (invalidate) => {
1847
+ invalidatePointer = invalidate;
1848
+ };
1845
1849
  const navigationControls = /* @__PURE__ */ new Map();
1846
1850
  let downloadSerial = 0;
1847
1851
  /** A result URL is only ever an http(s) address without credentials. */
@@ -1887,11 +1891,29 @@ const makeActions = (context, targets, observation, isTimeoutError) => {
1887
1891
  };
1888
1892
  /** What is left of a step's deadline for reading back, keeping a margin for the owner. */
1889
1893
  const readBack = (ticket, wanted) => Math.min(wanted, Math.max(0, ticket.remainingMillis() - 250));
1890
- const click = (target, ticket, policy, browserTarget) => sanitize(async () => {
1891
- await withElement(target, ticket, (element) => element.click({ timeout: timeout(ticket) }), policy, browserTarget);
1894
+ const clickElement = (page, element, ticket, capture) => {
1895
+ const dispatch = async () => {
1896
+ invalidatePointer(page);
1897
+ await element.click({ timeout: timeout(ticket) });
1898
+ };
1899
+ return capture === void 0 ? dispatch().then(() => void 0) : capture(dispatch, { position: null });
1900
+ };
1901
+ const click = (target, ticket, capture, policy, browserTarget) => sanitize(async () => {
1902
+ const { page } = current(browserTarget).entry;
1903
+ const input = await withElement(target, ticket, (element) => clickElement(page, element, ticket, capture), policy, browserTarget);
1892
1904
  ticket.check();
1893
- return postUrl(browserTarget);
1905
+ if (input === void 0) throw failure(Reasons.Malformed.make({}), "unknown");
1906
+ return {
1907
+ url: postUrl(browserTarget),
1908
+ input
1909
+ };
1894
1910
  });
1911
+ const clickWithoutReceipt = async (target, ticket) => {
1912
+ const { page } = current().entry;
1913
+ await withElement(target, ticket, (element) => clickElement(page, element, ticket));
1914
+ ticket.check();
1915
+ return postUrl();
1916
+ };
1895
1917
  /**
1896
1918
  * A file the provider already stores is named to the browser process, which opens it; this
1897
1919
  * client never reads that path. Exactly one main-frame node may match, and exactly one
@@ -1977,11 +1999,12 @@ const makeActions = (context, targets, observation, isTimeoutError) => {
1977
1999
  * in the requested state, and a native radio is never asked to clear itself. Options are the
1978
2000
  * issued nodes of the same observation, exactly as `selectOption` sends them.
1979
2001
  */
1980
- const formStep = (target, field, ticket, policy, settleMillis) => sanitize(async () => {
2002
+ const formStep = (target, field, ticket, policy, settleMillis, capture) => sanitize(async () => {
1981
2003
  const { element, check, facts } = await observation.resolve(target, ticket, policy, false, void 0, true);
1982
2004
  check();
1983
2005
  if (facts === void 0) throw failure(Reasons.Malformed.make({}), "undispatched");
1984
2006
  let act;
2007
+ let input;
1985
2008
  if (field.options !== void 0) {
1986
2009
  const admitted = await observation.selectOptions(target, field.options, element, ticket, true);
1987
2010
  act = async () => {
@@ -1994,7 +2017,9 @@ const makeActions = (context, targets, observation, isTimeoutError) => {
1994
2017
  if (facts.checked !== field.checked) {
1995
2018
  if (!field.checked && facts.inputType === "radio") throw failure(Reasons.Unsupported.make({}), "undispatched");
1996
2019
  await refuseInput(element);
1997
- act = () => element.click({ timeout: timeout(ticket) });
2020
+ act = async () => {
2021
+ input = await clickElement(current().entry.page, element, ticket, capture);
2022
+ };
1998
2023
  }
1999
2024
  } else {
2000
2025
  const text = field.value ?? "";
@@ -2019,14 +2044,20 @@ const makeActions = (context, targets, observation, isTimeoutError) => {
2019
2044
  status: act === void 0 ? "unchanged" : "set",
2020
2045
  reached: field.checked === void 0 || state === void 0 || holdsChecked(state, field.checked),
2021
2046
  state,
2022
- url: postUrl()
2047
+ url: postUrl(),
2048
+ ...input === void 0 ? {} : { input }
2023
2049
  };
2024
2050
  });
2025
2051
  /** The one submit click, on an exact observed node that may have become enabled. */
2026
- const formSubmit = (target, ticket, policy) => sanitize(async () => {
2027
- await withAdmittedElement(target, ticket, (element) => refuseInput(element), (element) => element.click({ timeout: timeout(ticket) }), policy, void 0, true);
2052
+ const formSubmit = (target, ticket, capture, policy) => sanitize(async () => {
2053
+ const { page } = current().entry;
2054
+ const input = await withAdmittedElement(target, ticket, (element) => refuseInput(element), (element) => clickElement(page, element, ticket, capture), policy, void 0, true);
2028
2055
  ticket.check();
2029
- return postUrl();
2056
+ if (input === void 0) throw failure(Reasons.Malformed.make({}), "unknown");
2057
+ return {
2058
+ url: postUrl(),
2059
+ input
2060
+ };
2030
2061
  });
2031
2062
  const selectOption = (target, options, ticket, policy) => sanitize(async () => {
2032
2063
  await withAdmittedElement(target, ticket, (element) => observation.selectOptions(target, options, element, ticket), async (element, admitted) => {
@@ -2136,15 +2167,18 @@ const makeActions = (context, targets, observation, isTimeoutError) => {
2136
2167
  dispose: leased.release
2137
2168
  };
2138
2169
  });
2139
- const clickAndWait = (target, ticket) => sanitize(async () => {
2170
+ const clickAndWait = (target, ticket, capture) => sanitize(async () => {
2140
2171
  const { entry, frame } = current();
2141
2172
  const observer = waitEvent((on) => entry.page.on("framenavigated", on), (off) => entry.page.off("framenavigated", off), ticket, (changedFrame) => changedFrame === frame);
2142
2173
  try {
2143
- await click(target, ticket);
2174
+ const clicked = await click(target, ticket, capture);
2144
2175
  await observer.promise;
2145
2176
  await frame.waitForLoadState("domcontentloaded", { timeout: timeout(ticket) });
2146
2177
  ticket.check();
2147
- return postUrl();
2178
+ return {
2179
+ url: postUrl(),
2180
+ input: clicked.input
2181
+ };
2148
2182
  } finally {
2149
2183
  observer.cancel();
2150
2184
  }
@@ -2153,7 +2187,7 @@ const makeActions = (context, targets, observation, isTimeoutError) => {
2153
2187
  const page = current().entry.page;
2154
2188
  const observer = waitEvent((on) => page.on("download", on), (off) => page.off("download", off), ticket);
2155
2189
  try {
2156
- await click(target, ticket);
2190
+ await clickWithoutReceipt(target, ticket);
2157
2191
  const download = await observer.promise;
2158
2192
  const filename = safeDecode(SafeFilename, download.suggestedFilename());
2159
2193
  const error = await download.failure();
@@ -2180,7 +2214,7 @@ const makeActions = (context, targets, observation, isTimeoutError) => {
2180
2214
  const page = current().entry.page;
2181
2215
  const observer = waitEvent((on) => page.on("filechooser", on), (off) => page.off("filechooser", off), ticket);
2182
2216
  try {
2183
- await click(target, ticket);
2217
+ await clickWithoutReceipt(target, ticket);
2184
2218
  const chooser = await observer.promise;
2185
2219
  ticket.check();
2186
2220
  if (!chooser.isMultiple() && selection.payload.length > 1) throw failure(Reasons.Unsupported.make({}));
@@ -2192,6 +2226,7 @@ const makeActions = (context, targets, observation, isTimeoutError) => {
2192
2226
  }
2193
2227
  });
2194
2228
  return {
2229
+ setPointerInvalidator,
2195
2230
  withAdmittedElement,
2196
2231
  beginNavigation,
2197
2232
  /** Capture the exact navigation at dialog arrival; acknowledgement never looks it up again. */
@@ -2282,11 +2317,18 @@ const makeCaptureSources = (targets) => {
2282
2317
  const mainFrameId = frameId(entry.page.mainFrame());
2283
2318
  for (const watcher of [...watchers]) {
2284
2319
  if (frame !== void 0 && changedFrameId !== mainFrameId && changedFrameId !== watcher.frameId) continue;
2285
- if (frame !== void 0 && watcher.document !== void 0 && reason === "target-changed" && changedFrameId === mainFrameId && watcher.frameId === mainFrameId) watcher.document(frame.url());
2320
+ if (frame !== void 0 && watcher.document !== void 0 && reason === "target-changed" && changedFrameId === mainFrameId && watcher.frameId === mainFrameId) watcher.document(frame.url(), false);
2286
2321
  else watcher.invalidate(reason);
2287
2322
  }
2288
2323
  };
2324
+ const sameDocumentNavigated = (entry, frame) => {
2325
+ const watchers = captureWatchers.get(entry.id);
2326
+ if (watchers === void 0 || frame !== entry.page.mainFrame()) return;
2327
+ for (const watcher of watchers) if (watcher.frameId === frameId(frame)) watcher.document?.(frame.url(), true);
2328
+ };
2289
2329
  const forget = (entry) => {
2330
+ const watchers = captureWatchers.get(entry.id);
2331
+ if (watchers !== void 0) for (const watcher of [...watchers]) watcher.invalidate("target-closed");
2290
2332
  captureWatchers.delete(entry.id);
2291
2333
  };
2292
2334
  const clear = () => {
@@ -2327,39 +2369,35 @@ const makeCaptureSources = (targets) => {
2327
2369
  };
2328
2370
  watcherSet.add(watcher);
2329
2371
  opened?.(captureFrame.url());
2372
+ await page.screencast.start({
2373
+ quality,
2374
+ ...size === void 0 ? {} : { size },
2375
+ onFrame: (frame) => {
2376
+ receive(frame);
2377
+ }
2378
+ });
2379
+ }),
2380
+ stop: () => sanitize(async () => {
2381
+ if (page.isClosed()) return;
2330
2382
  try {
2331
- await page.screencast.start({
2332
- quality,
2333
- ...size === void 0 ? {} : { size },
2334
- onFrame: (frame) => {
2335
- receive(frame);
2336
- }
2337
- });
2383
+ await page.screencast.stop();
2338
2384
  } catch (error) {
2339
- watcherSet.delete(watcher);
2340
- watcher = void 0;
2341
- if (watcherSet.size === 0) captureWatchers.delete(entry.id);
2342
- throw error;
2385
+ if (!page.isClosed()) throw error;
2343
2386
  }
2344
2387
  }),
2345
- stop: () => sanitize(async () => {
2388
+ release: () => {
2346
2389
  if (watcher !== void 0 && watcherSet !== void 0) {
2347
2390
  watcherSet.delete(watcher);
2348
2391
  watcher = void 0;
2349
2392
  if (watcherSet.size === 0) captureWatchers.delete(entry.id);
2350
2393
  }
2351
- if (page.isClosed()) return;
2352
- try {
2353
- await page.screencast.stop();
2354
- } catch (error) {
2355
- if (!page.isClosed()) throw error;
2356
- }
2357
- })
2394
+ }
2358
2395
  }
2359
2396
  };
2360
2397
  });
2361
2398
  return {
2362
2399
  invalidate,
2400
+ sameDocumentNavigated,
2363
2401
  forget,
2364
2402
  clear,
2365
2403
  capture
@@ -3276,6 +3314,9 @@ const makePointer = (targets, actions) => {
3276
3314
  positions.set(page, point);
3277
3315
  };
3278
3316
  const receipt = (page) => ({ position: positions.get(page) ?? null });
3317
+ const invalidate = (page) => {
3318
+ positions.set(page, null);
3319
+ };
3279
3320
  const pointerMove = (point, ticket, target) => sanitize(async () => {
3280
3321
  const { page } = current(target).entry;
3281
3322
  ticket.dispatch();
@@ -3349,7 +3390,8 @@ const makePointer = (targets, actions) => {
3349
3390
  pointerMove,
3350
3391
  hover,
3351
3392
  wheel,
3352
- receipt
3393
+ receipt,
3394
+ invalidate
3353
3395
  };
3354
3396
  };
3355
3397
  //#endregion
@@ -3470,6 +3512,16 @@ const TargetInfo = Schema.Struct({ targetInfo: Schema.Struct({
3470
3512
  }) });
3471
3513
  const URLText = Schema.String.check(Schema.isMaxLength(8192));
3472
3514
  /**
3515
+ * Playwright 1.63.0's client Frame emits this synchronous private event before the public Page
3516
+ * `framenavigated` event. Its `newDocument` field distinguishes document commits from URL-only
3517
+ * changes. A missing emitter is an unsupported runtime, never a reason to guess from the URL.
3518
+ */
3519
+ const clientNavigationEmitter = (frame) => {
3520
+ const emitter = frame._eventEmitter;
3521
+ if (typeof emitter?.on !== "function" || typeof emitter.off !== "function") throw new Error("Playwright 1.63.0 client Frame navigation event is unavailable");
3522
+ return emitter;
3523
+ };
3524
+ /**
3473
3525
  * The connection's pages, frames and document epochs, and the one selected target every other
3474
3526
  * seam acts on. A page beyond the configured bound is handed to policy recovery, never tracked.
3475
3527
  */
@@ -3505,7 +3557,25 @@ const makeTargets = (browser, context, options, connectionNamespace, closing, ho
3505
3557
  return entry;
3506
3558
  }
3507
3559
  entries.set(entry.id, entry);
3508
- for (const frame of page.frames()) frameId(frame);
3560
+ const sameDocumentNavigations = /* @__PURE__ */ new WeakSet();
3561
+ const frameNavigationOff = /* @__PURE__ */ new WeakMap();
3562
+ const watchFrameNavigation = (frame) => {
3563
+ if (frameNavigationOff.has(frame)) return;
3564
+ const emitter = clientNavigationEmitter(frame);
3565
+ const onNavigated = (event) => {
3566
+ if (event.error !== void 0 || event.newDocument !== void 0) return;
3567
+ sameDocumentNavigations.add(frame);
3568
+ hooks.sameDocumentNavigated(entry, frame);
3569
+ };
3570
+ emitter.on("navigated", onNavigated);
3571
+ const off = () => emitter.off("navigated", onNavigated);
3572
+ frameNavigationOff.set(frame, off);
3573
+ entry.off.push(off);
3574
+ };
3575
+ for (const frame of page.frames()) {
3576
+ frameId(frame);
3577
+ watchFrameNavigation(frame);
3578
+ }
3509
3579
  const onClose = () => {
3510
3580
  hooks.closed(entry);
3511
3581
  entries.delete(entry.id);
@@ -3517,6 +3587,7 @@ const makeTargets = (browser, context, options, connectionNamespace, closing, ho
3517
3587
  }
3518
3588
  };
3519
3589
  const onNavigation = (frame) => {
3590
+ if (sameDocumentNavigations.delete(frame)) return;
3520
3591
  hooks.navigating(entry, frame);
3521
3592
  documentEpochs.set(frame, epochOf(frame) + 1);
3522
3593
  hooks.navigated(entry, frame);
@@ -3525,6 +3596,11 @@ const makeTargets = (browser, context, options, connectionNamespace, closing, ho
3525
3596
  if (selection.entry === entry && (selection.frame === frame || frame === page.mainFrame())) hooks.changed("target-changed", { pageId: entry.id });
3526
3597
  };
3527
3598
  const onDetached = (frame) => {
3599
+ const off = frameNavigationOff.get(frame);
3600
+ off?.();
3601
+ const cleanupIndex = off === void 0 ? -1 : entry.off.indexOf(off);
3602
+ if (cleanupIndex !== -1) entry.off.splice(cleanupIndex, 1);
3603
+ frameNavigationOff.delete(frame);
3528
3604
  hooks.frameChanged(entry, frame);
3529
3605
  if (selection.frame === frame) {
3530
3606
  selection.frame = void 0;
@@ -3534,11 +3610,16 @@ const makeTargets = (browser, context, options, connectionNamespace, closing, ho
3534
3610
  const onDialog = (dialog) => {
3535
3611
  hooks.dialog(entry, dialog);
3536
3612
  };
3613
+ const onFrameAttached = (frame) => {
3614
+ frameId(frame);
3615
+ watchFrameNavigation(frame);
3616
+ };
3537
3617
  page.on("close", onClose);
3618
+ page.on("frameattached", onFrameAttached);
3538
3619
  page.on("framenavigated", onNavigation);
3539
3620
  page.on("framedetached", onDetached);
3540
3621
  page.on("dialog", onDialog);
3541
- entry.off.push(() => page.off("close", onClose), () => page.off("framenavigated", onNavigation), () => page.off("framedetached", onDetached), () => page.off("dialog", onDialog));
3622
+ entry.off.push(() => page.off("close", onClose), () => page.off("frameattached", onFrameAttached), () => page.off("framenavigated", onNavigation), () => page.off("framedetached", onDetached), () => page.off("dialog", onDialog));
3542
3623
  hooks.opened(entry, creatingPage);
3543
3624
  return entry;
3544
3625
  };
@@ -3801,6 +3882,7 @@ const makePlaywrightDriver = async (browser, identity, options, events) => {
3801
3882
  let closing = false;
3802
3883
  let initialized = false;
3803
3884
  let browserCdp;
3885
+ let sameDocumentCapture = () => {};
3804
3886
  const targets = makeTargets(browser, context, options, identity.namespace, () => closing, {
3805
3887
  opened: (entry, created) => {
3806
3888
  if (initialized && !created && options.popupPolicy === "close") {
@@ -3830,13 +3912,13 @@ const makePlaywrightDriver = async (browser, identity, options, events) => {
3830
3912
  }
3831
3913
  observation.invalidate({ pageId: entry.id });
3832
3914
  pageControl.closed(entry);
3833
- captures.invalidate(entry, "target-changed");
3834
3915
  captures.forget(entry);
3835
3916
  },
3836
3917
  navigating: (entry, frame) => pageControl.navigating(entry, frame),
3837
3918
  navigated: (entry, frame) => {
3838
3919
  if (initialized) initialization.attachFrame(frame, entry.page);
3839
3920
  },
3921
+ sameDocumentNavigated: (entry, frame) => sameDocumentCapture(entry, frame),
3840
3922
  frameChanged: (entry, frame) => {
3841
3923
  actions.waitChanged(entry, frame);
3842
3924
  observation.invalidate({
@@ -3865,8 +3947,10 @@ const makePlaywrightDriver = async (browser, identity, options, events) => {
3865
3947
  const observation = makeObservation(targets, identity.namespace, events);
3866
3948
  const actions = makeActions(context, targets, observation, (error) => error instanceof errors.TimeoutError);
3867
3949
  const pointer = makePointer(targets, actions);
3950
+ actions.setPointerInvalidator(pointer.invalidate);
3868
3951
  const keyboard = makeKeyboard(targets, actions, pointer.receipt);
3869
3952
  const captures = makeCaptureSources(targets);
3953
+ sameDocumentCapture = captures.sameDocumentNavigated;
3870
3954
  const pageControl = makePageControl(browser, context, options, identity.namespace, targets, callbacks, events, () => closing, (pageId) => observation.held(pageId));
3871
3955
  const onPage = (page) => {
3872
3956
  register(page);
@@ -4051,7 +4135,10 @@ const makeTarget = (bound) => ({
4051
4135
  stop: operation.stop
4052
4136
  }))),
4053
4137
  readText: (request) => checked(ReadTextRequest, request, "read-text").pipe(Effect.flatMap((value) => bound.readText(value.selector)), Effect.flatMap((text) => decoded(TextResult$1, "read-text", "undispatched")({ text }))),
4054
- click: (request) => checked(ClickRequest, request, "click").pipe(Effect.flatMap((value) => bound.click(value.selector)), Effect.flatMap((url) => action({ url }))),
4138
+ click: (request) => checked(ClickRequest, request, "click").pipe(Effect.flatMap((value) => bound.click(value.selector)), Effect.flatMap(({ url, input }) => action({
4139
+ url,
4140
+ input
4141
+ }))),
4055
4142
  fill: (request) => checked(FillRequest, request, "fill").pipe(Effect.flatMap((value) => bound.fill(value.selector, value.value)), Effect.flatMap((url) => action({ url }))),
4056
4143
  scroll: (request) => checked(ScrollRequest, request, "scroll").pipe(Effect.flatMap((value) => bound.scroll(value.deltaX, value.deltaY)), Effect.flatMap((url) => action({ url }))),
4057
4144
  pointerMove: (request) => checked(PointerMoveRequest, request, "pointer-move").pipe(Effect.flatMap((value) => bound.pointerMove(value.to)), Effect.flatMap((input) => pointerMoved({
@@ -4126,7 +4213,10 @@ const makeSession = (controls, bindings) => {
4126
4213
  }))),
4127
4214
  controlFacts: (reference) => checked(ObservedElement, reference, "control-facts").pipe(Effect.flatMap(controls.controlFacts)),
4128
4215
  revalidateElement: (reference) => checked(ObservedElement, reference, "revalidate").pipe(Effect.flatMap((value) => controls.revalidate(value).pipe(Effect.as(value)))),
4129
- clickElement: (reference, admission) => checked(ObservedElement, reference, "click").pipe(Effect.flatMap((value) => controls.operations.click(value, admission?.admit)), Effect.flatMap(navigate)),
4216
+ clickElement: (reference, admission) => checked(ObservedElement, reference, "click").pipe(Effect.flatMap((value) => controls.operations.click(value, admission?.admit)), Effect.flatMap(({ url, input }) => action({
4217
+ url,
4218
+ input
4219
+ }))),
4130
4220
  fillElement: (reference, value, admission) => checked(ObservedElement, reference, "fill").pipe(Effect.flatMap((element) => checked(FillRequest.fields.value, value, "fill").pipe(Effect.flatMap((text) => controls.operations.fill(element, text, admission?.admit)))), Effect.flatMap(navigate)),
4131
4221
  selectOption: (reference, options, admission) => checked(ObservedElement, reference, "select-option").pipe(Effect.flatMap((element) => checked(SelectOptions, options, "select-option").pipe(Effect.flatMap((ids) => controls.selectOption(element, ids, admission?.admit)))), Effect.flatMap(navigate)),
4132
4222
  fillForm: (request, admission, options = {}) => checked(FillFormRequest, request, "fill-form").pipe(Effect.flatMap((form) => checked(FillFormOptions, options, "fill-form").pipe(Effect.flatMap((settings) => controls.fillForm(form, admission?.admit, {
@@ -4157,7 +4247,10 @@ const makeSession = (controls, bindings) => {
4157
4247
  resizeViewport: (viewport) => checked(Viewport, viewport, "resize").pipe(Effect.flatMap(controls.resize)),
4158
4248
  waitFor: (request) => checked(Wait, request, "wait").pipe(Effect.flatMap((value) => controls.waitFor(value.selector, value.state))),
4159
4249
  waitForElement: (request) => checked(WaitForElementRequest, request, "wait").pipe(Effect.flatMap(controls.waitForElement)),
4160
- clickAndWait: (request) => checked(ClickRequest, request, "click-and-wait").pipe(Effect.flatMap((value) => controls.clickAndWait(value.selector)), Effect.flatMap((url) => action({ url }))),
4250
+ clickAndWait: (request) => checked(ClickRequest, request, "click-and-wait").pipe(Effect.flatMap((value) => controls.clickAndWait(value.selector)), Effect.flatMap(({ url, input }) => action({
4251
+ url,
4252
+ input
4253
+ }))),
4161
4254
  ready: controls.readiness.pipe(Effect.mapError((error) => InitializationError.make({
4162
4255
  operation: "ready",
4163
4256
  step: "session",
@@ -5136,6 +5229,17 @@ const acquireSession = Effect.fnUntraced(function* (limits, options) {
5136
5229
  * Direct operations resolve selection under admission. Retained operations capture selection
5137
5230
  * and generation; pinned operations capture generation and their explicit page/frame only.
5138
5231
  */
5232
+ const captureClickAt = (target) => async (dispatch, dispatched) => {
5233
+ const startedMonotonicNanos = clock.monotonicTimeNanosUnsafe();
5234
+ await dispatch();
5235
+ return {
5236
+ ...dispatched,
5237
+ target,
5238
+ kind: "click",
5239
+ startedMonotonicNanos,
5240
+ completedMonotonicNanos: clock.monotonicTimeNanosUnsafe()
5241
+ };
5242
+ };
5139
5243
  const makeOperations = (retained) => {
5140
5244
  const browserTarget = retained?.target;
5141
5245
  const check = () => {
@@ -5321,7 +5425,7 @@ const acquireSession = Effect.fnUntraced(function* (limits, options) {
5321
5425
  startNavigation,
5322
5426
  navigate: (url, timeoutMillis) => Effect.scoped(startNavigation(url, timeoutMillis).pipe(Effect.flatMap((operation) => operation.completed))),
5323
5427
  readText: (selector) => run("read-text", (driver, ticket) => driver.readText(selector, options.maxReturnedBytes, ticket, browserTarget)),
5324
- click: (target, policy) => run("click", (driver, ticket) => driver.click(target, ticket, policy, browserTarget), true),
5428
+ click: (target, policy) => run("click", (driver, ticket) => driver.click(target, ticket, captureClickAt(operationTarget()), policy, browserTarget), true),
5325
5429
  fill: (target, value, policy) => run("fill", (driver, ticket) => driver.fill(target, value, ticket, policy, browserTarget), true),
5326
5430
  scroll: (x, y) => run("scroll", (driver, ticket) => driver.scroll(x, y, ticket, browserTarget), true),
5327
5431
  pointerMove: (to) => input("pointer-move", (driver, ticket) => driver.pointerMove(to, ticket, browserTarget)),
@@ -5459,7 +5563,7 @@ const acquireSession = Effect.fnUntraced(function* (limits, options) {
5459
5563
  }
5460
5564
  });
5461
5565
  for (const field of request.fields) {
5462
- const exit = yield* Effect.exit(nativeOperation("fill-form", (driver, ticket) => driver.formStep(reference(field.elementId), field, ticket, policy, form.settleMillis), {
5566
+ const exit = yield* Effect.exit(nativeOperation("fill-form", (driver, ticket) => driver.formStep(reference(field.elementId), field, ticket, policy, form.settleMillis, captureClickAt(capture.target())), {
5463
5567
  mutation: true,
5464
5568
  mutationScope: kept
5465
5569
  }));
@@ -5471,7 +5575,8 @@ const acquireSession = Effect.fnUntraced(function* (limits, options) {
5471
5575
  const step = exit.value;
5472
5576
  fields.push({
5473
5577
  elementId: field.elementId,
5474
- status: step.status
5578
+ status: step.status,
5579
+ ...step.input === void 0 ? {} : { input: step.input }
5475
5580
  });
5476
5581
  states.push(step.state);
5477
5582
  url = step.url;
@@ -5499,14 +5604,17 @@ const acquireSession = Effect.fnUntraced(function* (limits, options) {
5499
5604
  }
5500
5605
  if (request.submit === void 0) return finish(false);
5501
5606
  const submit = request.submit;
5502
- const exit = yield* Effect.exit(nativeOperation("fill-form", (driver, ticket) => driver.formSubmit(reference(submit), ticket, policy), { mutation: true }));
5607
+ const exit = yield* Effect.exit(nativeOperation("fill-form", (driver, ticket) => driver.formSubmit(reference(submit), ticket, captureClickAt(capture.target()), policy), { mutation: true }));
5503
5608
  if (Exit.isFailure(exit)) {
5504
5609
  const error = Cause.findErrorOption(exit.cause);
5505
5610
  if (Option.isNone(error)) return yield* Effect.failCause(exit.cause);
5506
5611
  return stop("submit", error.value, submit);
5507
5612
  }
5508
- url = exit.value;
5509
- return finish(true);
5613
+ url = exit.value.url;
5614
+ return {
5615
+ ...finish(true),
5616
+ submitInput: exit.value.input
5617
+ };
5510
5618
  }).pipe(Effect.ensuring(Effect.sync(() => {
5511
5619
  if (pageId !== void 0) owner.invalidate("observation", { pageId });
5512
5620
  })));
@@ -5545,7 +5653,7 @@ const acquireSession = Effect.fnUntraced(function* (limits, options) {
5545
5653
  }),
5546
5654
  waitFor: (selector, state) => wait((driver, ticket, target) => driver.waitFor(selector, state, ticket, target)),
5547
5655
  waitForElement: (request) => wait((driver, ticket, target) => driver.waitForElement(request.reference, request.state, ticket, target), request.timeoutMillis),
5548
- clickAndWait: (target) => nativeOperation("click-and-wait", (driver, ticket) => driver.clickAndWait(target, ticket), { mutation: true }),
5656
+ clickAndWait: (target) => nativeOperation("click-and-wait", (driver, ticket) => driver.clickAndWait(target, ticket, captureClickAt(capture.target())), { mutation: true }),
5549
5657
  clickForDownload: (target) => nativeOperation("download-action", (driver, ticket) => driver.clickForDownload(target, ticket), { mutation: true }),
5550
5658
  selectFiles: (target, files) => nativeOperation("select-files", (driver, ticket) => driver.selectFiles(target, files, ticket), { mutation: true }),
5551
5659
  clickForFileSelection: (target, files) => nativeOperation("file-chooser", (driver, ticket) => driver.clickForFileSelection(target, files, ticket), { mutation: true }),
@@ -5787,4 +5895,4 @@ const make = Effect.fnUntraced(function* (options) {
5787
5895
  //#endregion
5788
5896
  export { cleanupStep as a, checked as c, randomUuid as i, fromNativeAttempt as l, make as n, noConnection as o, playwright as r, reported as s, DownloadEvent as t, issueBinding as u };
5789
5897
 
5790
- //# sourceMappingURL=BrowserRuntime-xBCLDy_5.mjs.map
5898
+ //# sourceMappingURL=BrowserRuntime-DImn_Oe_.mjs.map