@geometra/proxy 1.65.4 → 1.65.6

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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"}
@@ -3314,11 +3314,158 @@ async function beginFileInputCommitGuard(handle) {
3314
3314
  }
3315
3315
  return null;
3316
3316
  }
3317
+ function explicitlyAssociatesFileInput(scope, exactInput) {
3318
+ const broadContainerSelector = 'main, nav, aside, article, section, header, footer, ' +
3319
+ '[role="main"], [role="application"], [role="document"], [role="navigation"]';
3320
+ if (scope.matches(broadContainerSelector) || scope.querySelector(broadContainerSelector)) {
3321
+ return false;
3322
+ }
3323
+ const labels = Array.from(exactInput.labels ?? []);
3324
+ const interactiveElements = Array.from(scope.querySelectorAll('input, select, textarea, button, [role="button"], a[href], [contenteditable="true"]'));
3325
+ const hasUnrelatedControl = interactiveElements.some((control) => {
3326
+ if (control === exactInput)
3327
+ return false;
3328
+ if (control instanceof HTMLLabelElement && labels.includes(control))
3329
+ return false;
3330
+ const style = getComputedStyle(control);
3331
+ const visible = style.display !== 'none' &&
3332
+ style.visibility !== 'hidden' &&
3333
+ style.visibility !== 'collapse' &&
3334
+ control.getClientRects().length > 0;
3335
+ if (!visible)
3336
+ return false;
3337
+ if (!control.matches('button, [role="button"], a[href]'))
3338
+ return true;
3339
+ const accessibleText = (control.getAttribute('aria-label') ??
3340
+ control.getAttribute('title') ??
3341
+ control.textContent ?? '').replace(/\s+/g, ' ').trim();
3342
+ if (/^(?:box|dropbox|google drive|iCloud(?: drive)?|one ?drive)$/i.test(accessibleText)) {
3343
+ return false;
3344
+ }
3345
+ if (/^(?:enter|paste|type)(?: (?:the )?(?:file|document|resume|cv|cover letter|text))? manually$/i.test(accessibleText)) {
3346
+ return false;
3347
+ }
3348
+ const action = /^(attach|upload|browse|choose|select|add)\b/i.exec(accessibleText);
3349
+ if (!action)
3350
+ return true;
3351
+ const remainder = accessibleText.slice(action[0].length).trim();
3352
+ if (!remainder)
3353
+ return !/^(attach|upload|browse|choose)$/i.test(action[1] ?? '');
3354
+ return !/^(?:(?:a|an|the|this|new|your)\s+)?(?:files?|attachments?|upload|documents?|resume|cv|cover letter)\b/i.test(remainder);
3355
+ });
3356
+ if (hasUnrelatedControl)
3357
+ return false;
3358
+ if (scope instanceof HTMLLabelElement &&
3359
+ labels.includes(scope))
3360
+ return true;
3361
+ if (labels.some(label => scope.contains(label)))
3362
+ return true;
3363
+ if (scope instanceof HTMLFieldSetElement)
3364
+ return true;
3365
+ if (scope.getAttribute('role')?.trim().toLowerCase() !== 'group')
3366
+ return false;
3367
+ if (scope.getAttribute('aria-label')?.trim())
3368
+ return true;
3369
+ const labelledBy = scope.getAttribute('aria-labelledby')?.trim().split(/\s+/).filter(Boolean) ?? [];
3370
+ return labelledBy.some(id => {
3371
+ const label = exactInput.ownerDocument.getElementById(id);
3372
+ return Boolean(label && (scope.contains(label) || label === scope.previousElementSibling));
3373
+ });
3374
+ }
3375
+ function localFileGroupReceiptBranch(scope, exactInput) {
3376
+ if (scope.getAttribute('role')?.trim().toLowerCase() !== 'group')
3377
+ return null;
3378
+ if (scope instanceof HTMLFormElement ||
3379
+ scope instanceof HTMLBodyElement ||
3380
+ scope instanceof HTMLHtmlElement ||
3381
+ scope.querySelectorAll('input[type="file"]').length !== 1)
3382
+ return null;
3383
+ const broadContainerSelector = 'main, nav, aside, article, section, header, footer, ' +
3384
+ '[role="main"], [role="application"], [role="document"], [role="navigation"]';
3385
+ if (scope.matches(broadContainerSelector) || scope.querySelector(broadContainerSelector)) {
3386
+ return null;
3387
+ }
3388
+ const labelledBy = scope.getAttribute('aria-labelledby')?.trim().split(/\s+/).filter(Boolean) ?? [];
3389
+ const labels = labelledBy.map(id => {
3390
+ const label = exactInput.ownerDocument.getElementById(id);
3391
+ return label && (scope.contains(label) || label === scope.previousElementSibling)
3392
+ ? label
3393
+ : null;
3394
+ }).filter((label) => label !== null);
3395
+ if (labels.length === 0)
3396
+ return null;
3397
+ const ignoredIdentityTokens = new Set([
3398
+ 'add', 'application', 'attach', 'browse', 'button', 'candidate',
3399
+ 'choose', 'control', 'field', 'file', 'form', 'input', 'job',
3400
+ 'label', 'select', 'upload',
3401
+ ]);
3402
+ const identityTokens = (values) => new Set(values.flatMap(value => value?.toLowerCase().match(/[a-z0-9]+/g) ?? [])
3403
+ .filter(token => token.length >= 2 && !ignoredIdentityTokens.has(token)));
3404
+ const groupIdentity = identityTokens(labels.flatMap(label => [label.id, label.textContent]));
3405
+ const inputLabelledBy = exactInput.getAttribute('aria-labelledby')?.trim().split(/\s+/).filter(Boolean) ?? [];
3406
+ const inputIdentity = identityTokens([
3407
+ exactInput.id,
3408
+ exactInput.getAttribute('name'),
3409
+ exactInput.getAttribute('aria-label'),
3410
+ exactInput.getAttribute('title'),
3411
+ ...Array.from(exactInput.labels ?? []).flatMap(label => [label.id, label.textContent]),
3412
+ ...inputLabelledBy.flatMap(id => {
3413
+ const label = exactInput.ownerDocument.getElementById(id);
3414
+ return label ? [label.id, label.textContent] : [];
3415
+ }),
3416
+ ]);
3417
+ if (!Array.from(groupIdentity).some(token => inputIdentity.has(token)))
3418
+ return null;
3419
+ let branch = exactInput;
3420
+ let parent = composedParent(branch);
3421
+ while (parent && parent !== scope) {
3422
+ branch = parent;
3423
+ parent = composedParent(branch);
3424
+ }
3425
+ if (parent !== scope || branch === exactInput)
3426
+ return null;
3427
+ if (branch.matches(broadContainerSelector) || branch.querySelector(broadContainerSelector))
3428
+ return null;
3429
+ if (branch.querySelectorAll('input[type="file"]').length !== 1)
3430
+ return null;
3431
+ if (labels.some(label => branch.contains(label)))
3432
+ return null;
3433
+ if (!explicitlyAssociatesFileInput(branch, exactInput))
3434
+ return null;
3435
+ return branch;
3436
+ }
3317
3437
  const input = element instanceof HTMLInputElement && element.type === 'file' ? element : null;
3318
3438
  const guardedAncestors = new Set();
3439
+ const receiptScopes = [];
3440
+ let capturedReceiptScope = false;
3441
+ let capturedFieldGroupReceiptScope = false;
3319
3442
  let current = input;
3320
3443
  while (current) {
3321
3444
  guardedAncestors.add(current);
3445
+ const associatedLocalWrapper = !capturedReceiptScope &&
3446
+ input !== null &&
3447
+ current !== input &&
3448
+ !(current instanceof HTMLFormElement) &&
3449
+ !(current instanceof HTMLBodyElement) &&
3450
+ !(current instanceof HTMLHtmlElement) &&
3451
+ current.querySelectorAll('input[type="file"]').length === 1 &&
3452
+ explicitlyAssociatesFileInput(current, input);
3453
+ const fieldGroupReceiptBranch = !capturedFieldGroupReceiptScope && input !== null
3454
+ ? localFileGroupReceiptBranch(current, input)
3455
+ : null;
3456
+ const receiptScope = associatedLocalWrapper ? current : fieldGroupReceiptBranch;
3457
+ if (receiptScope && !receiptScopes.some(entry => entry.element === receiptScope)) {
3458
+ const initialExactTexts = [receiptScope, ...Array.from(receiptScope.querySelectorAll('*'))]
3459
+ .map(candidate => candidate.textContent?.replace(/\s+/g, ' ').trim() ?? '')
3460
+ .filter(Boolean);
3461
+ receiptScopes.push({ element: receiptScope, initialExactTexts });
3462
+ }
3463
+ if (associatedLocalWrapper)
3464
+ capturedReceiptScope = true;
3465
+ if (fieldGroupReceiptBranch)
3466
+ capturedFieldGroupReceiptScope = true;
3467
+ if (current instanceof HTMLFormElement)
3468
+ break;
3322
3469
  current = composedParent(current);
3323
3470
  }
3324
3471
  const initial = {
@@ -3331,6 +3478,7 @@ async function beginFileInputCommitGuard(handle) {
3331
3478
  const state = {
3332
3479
  input,
3333
3480
  initial,
3481
+ receiptScopes,
3334
3482
  invalidReason: null,
3335
3483
  observer: null,
3336
3484
  record: () => { },
@@ -3413,6 +3561,125 @@ async function stopFileInputCommitGuard(guard) {
3413
3561
  value.observer.disconnect();
3414
3562
  }).catch(() => { });
3415
3563
  }
3564
+ async function readReactiveFileReceipt(guard, expectedNames) {
3565
+ return await guard.evaluate((value, names) => {
3566
+ function visible(element) {
3567
+ const style = getComputedStyle(element);
3568
+ return style.display !== 'none' &&
3569
+ style.visibility !== 'hidden' &&
3570
+ style.visibility !== 'collapse' &&
3571
+ element.getClientRects().length > 0;
3572
+ }
3573
+ const normalize = (text) => (text ?? '').replace(/\s+/g, ' ').trim();
3574
+ // Each scope is either the exact input's nearest local wrapper or the
3575
+ // input-bearing branch of an explicitly labelled field group. Never scan
3576
+ // the group itself or combine evidence from sibling/ancestor controls.
3577
+ const candidate = value.receiptScopes.find((entry) => entry.element.isConnected);
3578
+ if (!candidate) {
3579
+ return { hasScope: false, hasReceipt: false, hasProgress: false, hasError: false };
3580
+ }
3581
+ if (names.some(name => candidate.initialExactTexts.includes(name))) {
3582
+ return { hasScope: true, hasReceipt: false, hasProgress: false, hasError: false };
3583
+ }
3584
+ const scope = candidate.element;
3585
+ const descendants = [scope, ...Array.from(scope.querySelectorAll('*'))];
3586
+ const expectedCounts = new Map();
3587
+ for (const name of names)
3588
+ expectedCounts.set(name, (expectedCounts.get(name) ?? 0) + 1);
3589
+ const observedCounts = new Map();
3590
+ for (const element of descendants) {
3591
+ if (!visible(element))
3592
+ continue;
3593
+ const text = normalize(element.textContent);
3594
+ if (!expectedCounts.has(text))
3595
+ continue;
3596
+ // Count only the innermost exact-text element so nested wrappers cannot
3597
+ // manufacture duplicate filename evidence for one rendered receipt.
3598
+ const nestedExactText = Array.from(element.querySelectorAll('*')).some(descendant => visible(descendant) && normalize(descendant.textContent) === text);
3599
+ if (!nestedExactText)
3600
+ observedCounts.set(text, (observedCounts.get(text) ?? 0) + 1);
3601
+ }
3602
+ const hasExactNames = Array.from(expectedCounts).every(([name, count]) => observedCounts.get(name) === count);
3603
+ const hasRemovalControl = descendants.some((element) => {
3604
+ if (!visible(element) || !element.matches('button, [role="button"], a'))
3605
+ return false;
3606
+ const accessibleText = normalize(element.getAttribute('aria-label') ??
3607
+ element.getAttribute('title') ??
3608
+ element.textContent);
3609
+ const action = /^(remove|delete|clear|replace|change)\b/i.exec(accessibleText);
3610
+ if (!action)
3611
+ return false;
3612
+ const remainder = accessibleText.slice(action[0].length).trim();
3613
+ if (!remainder)
3614
+ return true;
3615
+ if (names.some(name => remainder.toLowerCase().includes(name.toLowerCase())))
3616
+ return true;
3617
+ return /^(?:(?:this|the|selected|uploaded|current)\s+)?(?:file|attachment|upload|document|resume|cv|cover letter)\b/i.test(remainder);
3618
+ });
3619
+ const hasProgress = descendants.some((element) => {
3620
+ if (!visible(element))
3621
+ return false;
3622
+ if (element.matches('[role="progressbar"], progress, [aria-busy="true"]'))
3623
+ return true;
3624
+ for (const attribute of ['data-state', 'data-status', 'data-upload-state', 'data-file-state']) {
3625
+ const state = element.getAttribute(attribute)?.trim().toLowerCase();
3626
+ if (state && /^(?:uploading|pending|processing|in[-_ ]?progress|queued)$/.test(state))
3627
+ return true;
3628
+ }
3629
+ return false;
3630
+ });
3631
+ const hasError = descendants.some((element) => {
3632
+ if (!visible(element))
3633
+ return false;
3634
+ const role = normalize(element.getAttribute('role')).toLowerCase();
3635
+ if (role === 'alert' || role === 'alertdialog')
3636
+ return true;
3637
+ if (element.getAttribute('aria-invalid')?.trim().toLowerCase() === 'true')
3638
+ return true;
3639
+ const dataError = element.getAttribute('data-error');
3640
+ if (dataError !== null &&
3641
+ !/^(?:false|0|off|none)$/i.test(dataError.trim()))
3642
+ return true;
3643
+ for (const attribute of ['data-state', 'data-status', 'data-upload-state', 'data-file-state']) {
3644
+ const state = element.getAttribute(attribute)?.trim().toLowerCase();
3645
+ if (state && /^(?:error|invalid|failed|failure|rejected)$/.test(state))
3646
+ return true;
3647
+ }
3648
+ return Array.from(element.classList).some(className => /(?:^|[-_:])(?:error|errors|invalid|failed|failure)(?:$|[-_:])/i.test(className));
3649
+ });
3650
+ if (hasExactNames && hasRemovalControl && !hasProgress && !hasError) {
3651
+ return { hasScope: true, hasReceipt: true, hasProgress: false, hasError: false };
3652
+ }
3653
+ return { hasScope: true, hasReceipt: false, hasProgress, hasError };
3654
+ }, expectedNames);
3655
+ }
3656
+ /**
3657
+ * Some reactive uploaders detach the native input while streaming the file,
3658
+ * then render a filename receipt in the same field wrapper. Treat that as
3659
+ * success only when the exact new filenames and a removal control remain
3660
+ * stable after progress completes. A filename in an error message is not a
3661
+ * receipt, and an unbounded form-level match is never considered.
3662
+ */
3663
+ async function confirmStableReactiveFileReceipt(guard, paths) {
3664
+ const expectedNames = expectedUploadNames(paths);
3665
+ const signalDeadline = Date.now() + 500;
3666
+ let completionDeadline = null;
3667
+ while (Date.now() < (completionDeadline ?? signalDeadline)) {
3668
+ const state = await readReactiveFileReceipt(guard, expectedNames).catch(() => null);
3669
+ if (!state?.hasScope || state.hasError)
3670
+ return false;
3671
+ if (state.hasReceipt) {
3672
+ await delay(100);
3673
+ const stable = await readReactiveFileReceipt(guard, expectedNames).catch(() => null);
3674
+ return Boolean(stable?.hasReceipt && !stable.hasProgress && !stable.hasError);
3675
+ }
3676
+ if (state.hasProgress && completionDeadline === null) {
3677
+ completionDeadline = Date.now() + 15_000;
3678
+ }
3679
+ await delay(50);
3680
+ }
3681
+ return false;
3682
+ }
3416
3683
  async function clearAmbiguousRetainedFiles(handle, actualNames, initialNames) {
3417
3684
  const selectionChanged = !uploadedNamesMatch(actualNames, initialNames);
3418
3685
  if (!selectionChanged) {
@@ -3554,6 +3821,10 @@ async function commitFilesToExactInput(handle, paths, targetName, opts) {
3554
3821
  opts?.successor &&
3555
3822
  await confirmSameKeyFileInputSuccessor(opts.successor.page, handle, opts.successor.fieldKey, paths, initial))
3556
3823
  return;
3824
+ if (!operationError &&
3825
+ identityChanged &&
3826
+ await confirmStableReactiveFileReceipt(guard, paths))
3827
+ return;
3557
3828
  const cleanupOutcome = await clearAmbiguousRetainedFiles(handle, final.names, initial.names);
3558
3829
  const operationReason = operationError instanceof Error
3559
3830
  ? `Playwright could not confirm the exact-target commit: ${operationError.message}`