@geometra/proxy 1.65.2 → 1.65.4

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 +1 @@
1
- {"version":3,"file":"dom-actions.d.ts","sourceRoot":"","sources":["../src/dom-actions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dom-actions.d.ts","sourceRoot":"","sources":["../src/dom-actions.ts"],"names":[],"mappings":"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"}
@@ -2429,26 +2429,67 @@ async function readFormLevelInvalidState(handle, opts) {
2429
2429
  const form = el.closest('form');
2430
2430
  if (!form)
2431
2431
  return false;
2432
- // Find the field wrapper — the closest ancestor that "contains a label"
2433
- // and is still inside the form. This is the react-hook-form convention:
2434
- // every field lives under a wrapper that renders both the control and
2435
- // the error message as siblings. If we can't find one, use a bounded
2436
- // walk up to ~6 ancestors and look at each one's siblings.
2437
- let wrapper = el;
2438
- let depth = 0;
2439
- while (wrapper && wrapper !== form && depth < 6) {
2440
- // A "field wrapper" is anything that contains a <label>, a legend,
2441
- // or a [class*="field"] class up to 6 ancestors deep.
2442
- const hasLabel = wrapper.querySelector && (wrapper.querySelector('label') || wrapper.querySelector('legend'));
2443
- const className = (wrapper.getAttribute('class') ?? '').toLowerCase();
2444
- const looksLikeField = className.includes('field') ||
2445
- className.includes('form-group') ||
2446
- className.includes('form-control') ||
2447
- className.includes('input-wrapper');
2448
- if (hasLabel || looksLikeField)
2449
- break;
2450
- wrapper = wrapper.parentElement;
2451
- depth++;
2432
+ function rootScopedElementById(owner, id) {
2433
+ const root = owner.getRootNode();
2434
+ if (root instanceof ShadowRoot)
2435
+ return root.getElementById(id);
2436
+ if (root instanceof Document)
2437
+ return root.getElementById(id);
2438
+ return null;
2439
+ }
2440
+ function linkedLabels(control) {
2441
+ const labels = new Set();
2442
+ for (const id of (control.getAttribute('aria-labelledby') ?? '').split(/\s+/).filter(Boolean)) {
2443
+ const label = rootScopedElementById(control, id);
2444
+ if (label)
2445
+ labels.add(label);
2446
+ }
2447
+ if (control instanceof HTMLInputElement || control instanceof HTMLSelectElement || control instanceof HTMLTextAreaElement) {
2448
+ for (const label of Array.from(control.labels ?? []))
2449
+ labels.add(label);
2450
+ }
2451
+ return [...labels];
2452
+ }
2453
+ function linkedFieldWrapper(control) {
2454
+ let best = null;
2455
+ for (const label of linkedLabels(control)) {
2456
+ let candidate = control;
2457
+ let depth = 0;
2458
+ while (candidate && candidate !== form && depth < 12) {
2459
+ if (candidate.contains(label) && (!best || depth < best.depth)) {
2460
+ best = { wrapper: candidate, depth };
2461
+ break;
2462
+ }
2463
+ candidate = candidate.parentElement;
2464
+ depth++;
2465
+ }
2466
+ }
2467
+ return best?.wrapper ?? null;
2468
+ }
2469
+ // Prefer an ancestor that jointly owns this exact control and its
2470
+ // explicitly linked label. Looking for any descendant <label> can pull
2471
+ // an unrelated required field into the verification scope.
2472
+ let wrapper = linkedFieldWrapper(el);
2473
+ if (!wrapper) {
2474
+ const semanticGroup = el.closest('fieldset, [role="group"]');
2475
+ if (semanticGroup && semanticGroup !== form)
2476
+ wrapper = semanticGroup;
2477
+ }
2478
+ if (!wrapper) {
2479
+ let candidate = el;
2480
+ let depth = 0;
2481
+ while (candidate && candidate !== form && depth < 8) {
2482
+ const className = (candidate.getAttribute('class') ?? '').toLowerCase();
2483
+ if (className.includes('field') ||
2484
+ className.includes('form-group') ||
2485
+ className.includes('form-control') ||
2486
+ className.includes('input-wrapper')) {
2487
+ wrapper = candidate;
2488
+ break;
2489
+ }
2490
+ candidate = candidate.parentElement;
2491
+ depth++;
2492
+ }
2452
2493
  }
2453
2494
  if (!wrapper || wrapper === form)
2454
2495
  wrapper = el.parentElement ?? el;
@@ -3284,6 +3325,7 @@ async function beginFileInputCommitGuard(handle) {
3284
3325
  validFileInput: Boolean(input?.isConnected),
3285
3326
  blockReason: input ? mutationBlockReason(input) : 'detached or no longer a file input',
3286
3327
  accept: input?.getAttribute('accept') ?? null,
3328
+ multiple: input?.multiple ?? false,
3287
3329
  names: input ? Array.from(input.files ?? []).map(file => file.name) : [],
3288
3330
  };
3289
3331
  const state = {
@@ -3391,7 +3433,84 @@ async function clearAmbiguousRetainedFiles(handle, actualNames, initialNames) {
3391
3433
  }
3392
3434
  return 'the changed selection was cleared best-effort without trying another candidate';
3393
3435
  }
3394
- async function commitFilesToExactInput(handle, paths, targetName, setFiles = async (selectedPaths) => handle.setInputFiles(selectedPaths)) {
3436
+ async function confirmSameKeyFileInputSuccessor(page, original, fieldKey, paths, initial) {
3437
+ const expectedNames = expectedUploadNames(paths);
3438
+ const deadline = Date.now() + 1_500;
3439
+ while (Date.now() < deadline) {
3440
+ let successor;
3441
+ try {
3442
+ successor = await findAuthoredFieldByKeyInPage(page, fieldKey, 'file');
3443
+ }
3444
+ catch {
3445
+ // Duplicate authored keys are terminal ambiguity, not a reason to pick
3446
+ // one candidate or try another uploader.
3447
+ return false;
3448
+ }
3449
+ if (!successor) {
3450
+ await delay(50);
3451
+ continue;
3452
+ }
3453
+ const state = await successor.evaluate((input, prior) => {
3454
+ if (!(input instanceof HTMLInputElement) || input.type !== 'file' || !input.isConnected)
3455
+ return null;
3456
+ return {
3457
+ isSuccessor: input !== prior,
3458
+ accept: input.getAttribute('accept'),
3459
+ multiple: input.multiple,
3460
+ names: Array.from(input.files ?? []).map(file => file.name),
3461
+ };
3462
+ }, original).catch(() => null);
3463
+ if (!state?.isSuccessor) {
3464
+ await delay(50);
3465
+ continue;
3466
+ }
3467
+ // Only accept a stable successor whose authored contract still permits
3468
+ // this upload and whose exact FileList proves that this upload, rather
3469
+ // than a sibling input, survived.
3470
+ if (state.multiple !== initial.multiple)
3471
+ return false;
3472
+ try {
3473
+ assertPathsMatchFileAccept(paths, state.accept);
3474
+ }
3475
+ catch {
3476
+ return false;
3477
+ }
3478
+ if (!uploadedNamesMatch(state.names, expectedNames)) {
3479
+ await delay(50);
3480
+ continue;
3481
+ }
3482
+ await delay(40);
3483
+ let confirmed;
3484
+ try {
3485
+ confirmed = await findAuthoredFieldByKeyInPage(page, fieldKey, 'file');
3486
+ }
3487
+ catch {
3488
+ return false;
3489
+ }
3490
+ if (!confirmed)
3491
+ return false;
3492
+ const stable = await confirmed.evaluate((input, payload) => {
3493
+ if (!(input instanceof HTMLInputElement) ||
3494
+ input.type !== 'file' ||
3495
+ !input.isConnected ||
3496
+ input === payload.prior ||
3497
+ input.getAttribute('accept') !== payload.accept ||
3498
+ input.multiple !== payload.multiple)
3499
+ return false;
3500
+ const names = Array.from(input.files ?? []).map(file => file.name);
3501
+ return names.length === payload.names.length &&
3502
+ names.every((name, index) => name === payload.names[index]);
3503
+ }, {
3504
+ prior: original,
3505
+ accept: state.accept,
3506
+ multiple: initial.multiple,
3507
+ names: expectedNames,
3508
+ }).catch(() => false);
3509
+ return stable;
3510
+ }
3511
+ return false;
3512
+ }
3513
+ async function commitFilesToExactInput(handle, paths, targetName, opts) {
3395
3514
  const { guard, initial } = await beginFileInputCommitGuard(handle);
3396
3515
  try {
3397
3516
  if (!initial.validFileInput) {
@@ -3411,7 +3530,7 @@ async function commitFilesToExactInput(handle, paths, targetName, setFiles = asy
3411
3530
  }
3412
3531
  let operationError;
3413
3532
  try {
3414
- await setFiles(paths);
3533
+ await (opts?.setFiles ?? (async (selectedPaths) => handle.setInputFiles(selectedPaths)))(paths);
3415
3534
  }
3416
3535
  catch (error) {
3417
3536
  operationError = error;
@@ -3424,7 +3543,16 @@ async function commitFilesToExactInput(handle, paths, targetName, setFiles = asy
3424
3543
  names: [],
3425
3544
  }));
3426
3545
  const retained = uploadedNamesMatch(final.names, expectedUploadNames(paths));
3427
- if (!operationError && !final.invalidReason && retained)
3546
+ const originalConnected = await handle.evaluate(input => input.isConnected).catch(() => false);
3547
+ if (!operationError && !final.invalidReason && retained && originalConnected)
3548
+ return;
3549
+ const identityChanged = final.invalidReason === 'the exact target identity changed during commit' ||
3550
+ final.invalidReason === 'the exact target detached during commit' ||
3551
+ (!final.invalidReason && !originalConnected);
3552
+ if (!operationError &&
3553
+ identityChanged &&
3554
+ opts?.successor &&
3555
+ await confirmSameKeyFileInputSuccessor(opts.successor.page, handle, opts.successor.fieldKey, paths, initial))
3428
3556
  return;
3429
3557
  const cleanupOutcome = await clearAmbiguousRetainedFiles(handle, final.names, initial.names);
3430
3558
  const operationReason = operationError instanceof Error
@@ -3446,7 +3574,9 @@ async function attachHiddenInAllFrames(page, paths, opts) {
3446
3574
  const handle = await labeled.elementHandle();
3447
3575
  if (!handle)
3448
3576
  throw new Error('file: matched input detached before upload');
3449
- await commitFilesToExactInput(handle, paths, 'matched input');
3577
+ await commitFilesToExactInput(handle, paths, 'matched input', {
3578
+ successor: opts?.fieldKey ? { page, fieldKey: opts.fieldKey } : undefined,
3579
+ });
3450
3580
  return true;
3451
3581
  }
3452
3582
  return false;
@@ -3486,8 +3616,10 @@ async function attachViaChooser(page, paths, clickX, clickY) {
3486
3616
  ]);
3487
3617
  const input = chooser.element();
3488
3618
  try {
3489
- await commitFilesToExactInput(input, paths, 'chooser input', async (selectedPaths) => {
3490
- await chooser.setFiles(selectedPaths);
3619
+ await commitFilesToExactInput(input, paths, 'chooser input', {
3620
+ setFiles: async (selectedPaths) => {
3621
+ await chooser.setFiles(selectedPaths);
3622
+ },
3491
3623
  });
3492
3624
  }
3493
3625
  finally {