@geometra/proxy 1.62.1 → 1.62.3

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 CHANGED
@@ -1,13 +1,13 @@
1
1
  # @geometra/proxy
2
2
 
3
- Chromium proxy ( **`headed` / visible window by default** ) that extracts **live DOM layout** (`getBoundingClientRect`) plus a **synthetic Geometra UI tree** and streams **GEOM v1** `frame` / `patch` messages over WebSocket (JSON text). Use it with [`@geometra/mcp`](../../mcp/README.md) to drive arbitrary web apps without screenshots.
3
+ Chromium proxy ( **headless by default** ) that extracts **live DOM layout** (`getBoundingClientRect`) plus a **synthetic Geometra UI tree** and streams **GEOM v1** `frame` / `patch` messages over WebSocket (JSON text). Use it with [`@geometra/mcp`](../../mcp/README.md) to drive arbitrary web apps without screenshots.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
8
  npm install @geometra/proxy playwright
9
9
  npx playwright install chromium
10
- # Optional: prefetch CloakBrowser's patched Chromium for --stealth mode
10
+ # Optional: prefetch CloakBrowser's Chromium for authorized --stealth testing
11
11
  npx cloakbrowser install
12
12
  ```
13
13
 
@@ -16,14 +16,14 @@ npx cloakbrowser install
16
16
  ```bash
17
17
  npx geometra-proxy https://example.com --port 3200
18
18
  npx geometra-proxy http://localhost:8080 --width 1440 --height 900
19
- npx geometra-proxy https://example.com --port 3200 --headless
19
+ npx geometra-proxy https://example.com --port 3200 --headed
20
20
  npx geometra-proxy https://example.com --port 3200 --slow-mo 50
21
21
  npx geometra-proxy https://example.com --port 3200 --stealth
22
22
  ```
23
23
 
24
- **Default is a visible browser** so you can watch MCP-driven clicks and typing. Use **`--headless`** (or env **`GEOMETRA_HEADLESS=1`**) for automation on servers / CI. **`--slow-mo <ms>`** (or **`GEOMETRA_SLOW_MO`**) adds Playwright `slowMo` to make actions easier to follow.
24
+ **Default is headless** so MCP-driven browsing does not open windows. Use **`--headed`** when you need to watch clicks and typing. **`--slow-mo <ms>`** (or **`GEOMETRA_SLOW_MO`**) adds Playwright `slowMo` to make actions easier to follow.
25
25
 
26
- Use **`--stealth`** (or env **`GEOMETRA_STEALTH=1`** / **`GEOMETRA_BROWSER=stealth`**) to launch CloakBrowser's patched Chromium through the same Geometra proxy protocol. Stock Playwright Chromium remains the default; pass **`--no-stealth`** to override a stealth env default for one run. CloakBrowser downloads its patched Chromium on first launch and caches it under `~/.cloakbrowser/`.
26
+ Use **`--stealth`** (or env **`GEOMETRA_STEALTH=1`** / **`GEOMETRA_BROWSER=stealth`**) to launch CloakBrowser's Chromium through the same Geometra proxy protocol for authorized testing. Stock Playwright Chromium remains the default; pass **`--no-stealth`** to override a stealth env default for one run. CloakBrowser downloads its browser binary on first launch and caches it under `~/.cloakbrowser/`.
27
27
 
28
28
  The proxy only opens **`http://`** or **`https://`** pages. For debugging you can still choose a fixed `--port`, but `--port 0` asks the OS for an ephemeral free port (useful for tools that auto-spawn the proxy).
29
29
 
@@ -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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"}
@@ -1986,6 +1986,108 @@ async function pressEnterToCommitListbox(page, handle) {
1986
1986
  return false;
1987
1987
  }
1988
1988
  }
1989
+ /**
1990
+ * After a custom listbox option click, make the committed value visible to
1991
+ * framework state managers that listen on the backing input/select instead of
1992
+ * the popup option. This covers React controlled inputs, react-hook-form
1993
+ * hidden inputs, and intl-tel-input country pickers without site branching.
1994
+ */
1995
+ async function dispatchListboxCommitEvents(handle) {
1996
+ if (!handle)
1997
+ return false;
1998
+ try {
1999
+ return await handle.evaluate((el) => {
2000
+ if (!(el instanceof Element))
2001
+ return false;
2002
+ function clearReactTracker(target) {
2003
+ const tracker = target._valueTracker;
2004
+ if (!tracker || typeof tracker.setValue !== 'function')
2005
+ return;
2006
+ try {
2007
+ if (target instanceof HTMLInputElement && (target.type === 'checkbox' || target.type === 'radio')) {
2008
+ tracker.setValue(String(!target.checked));
2009
+ return;
2010
+ }
2011
+ const current = target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement
2012
+ ? target.value
2013
+ : target instanceof HTMLSelectElement
2014
+ ? target.value
2015
+ : target.textContent ?? '';
2016
+ tracker.setValue(current === '' ? '__geometra_empty__' : '');
2017
+ }
2018
+ catch { /* ignore */ }
2019
+ }
2020
+ function dispatchCommit(target) {
2021
+ clearReactTracker(target);
2022
+ const inputType = target instanceof HTMLInputElement ? target.type.toLowerCase() : '';
2023
+ const isSearchQueryInput = target instanceof HTMLInputElement &&
2024
+ !['hidden', 'checkbox', 'radio', 'tel'].includes(inputType) &&
2025
+ (target.getAttribute('role') === 'combobox' ||
2026
+ target.hasAttribute('aria-autocomplete') ||
2027
+ target.hasAttribute('aria-activedescendant'));
2028
+ if (!isSearchQueryInput) {
2029
+ target.dispatchEvent(new Event('input', { bubbles: true }));
2030
+ }
2031
+ target.dispatchEvent(new Event('change', { bubbles: true }));
2032
+ if (target instanceof HTMLInputElement &&
2033
+ (target.type === 'tel' ||
2034
+ target.closest('.iti, .intl-tel-input, [data-intl-tel-input-id], [class*="phone"], [class*="Phone"]'))) {
2035
+ target.dispatchEvent(new Event('countrychange', { bubbles: true }));
2036
+ }
2037
+ }
2038
+ function commitRoot(start) {
2039
+ let best = start.parentElement ?? start;
2040
+ let current = start.parentElement;
2041
+ let depth = 0;
2042
+ while (current && depth < 6) {
2043
+ if (current.tagName.toLowerCase() === 'form')
2044
+ break;
2045
+ const className = (current.getAttribute('class') ?? '').toLowerCase();
2046
+ const controlCount = current.querySelectorAll('input, textarea, select, [role="combobox"], [aria-haspopup="listbox"]').length;
2047
+ const looksLikeField = current.querySelector('label, legend') !== null ||
2048
+ className.includes('field') ||
2049
+ className.includes('form-group') ||
2050
+ className.includes('form-control') ||
2051
+ className.includes('select') ||
2052
+ className.includes('combo') ||
2053
+ className.includes('phone') ||
2054
+ className.includes('iti') ||
2055
+ className.includes('input');
2056
+ if (looksLikeField && controlCount > 0 && controlCount <= 10)
2057
+ best = current;
2058
+ current = current.parentElement;
2059
+ depth++;
2060
+ }
2061
+ return best;
2062
+ }
2063
+ const targets = [];
2064
+ const seen = new Set();
2065
+ const add = (candidate) => {
2066
+ if (!(candidate instanceof HTMLElement))
2067
+ return;
2068
+ if (seen.has(candidate))
2069
+ return;
2070
+ seen.add(candidate);
2071
+ targets.push(candidate);
2072
+ };
2073
+ add(el);
2074
+ const root = commitRoot(el);
2075
+ for (const candidate of Array.from(root.querySelectorAll('input, textarea, select, [role="combobox"], [aria-haspopup="listbox"]'))) {
2076
+ if (candidate.closest('[role="listbox"], [role="menu"]') &&
2077
+ !candidate.matches('[role="combobox"], input, select, textarea')) {
2078
+ continue;
2079
+ }
2080
+ add(candidate);
2081
+ }
2082
+ for (const target of targets)
2083
+ dispatchCommit(target);
2084
+ return targets.length > 0;
2085
+ });
2086
+ }
2087
+ catch {
2088
+ return false;
2089
+ }
2090
+ }
1989
2091
  /**
1990
2092
  * Read form-level invalid state for a combobox trigger.
1991
2093
  *
@@ -3310,6 +3412,22 @@ async function attemptNativeBatchFill(page, fields) {
3310
3412
  target.dispatchEvent(new Event('input', { bubbles: true }));
3311
3413
  target.dispatchEvent(new Event('change', { bubbles: true }));
3312
3414
  }
3415
+ function clearReactTracker(target, nextChecked) {
3416
+ const tracker = target._valueTracker;
3417
+ if (!tracker || typeof tracker.setValue !== 'function')
3418
+ return false;
3419
+ try {
3420
+ if (target instanceof HTMLInputElement && (target.type === 'checkbox' || target.type === 'radio')) {
3421
+ tracker.setValue(String(!(nextChecked ?? target.checked)));
3422
+ }
3423
+ else {
3424
+ tracker.setValue('');
3425
+ }
3426
+ return true;
3427
+ }
3428
+ catch { /* ignore */ }
3429
+ return false;
3430
+ }
3313
3431
  function setInputLikeValue(target, next) {
3314
3432
  const proto = target instanceof HTMLInputElement ? HTMLInputElement.prototype : HTMLTextAreaElement.prototype;
3315
3433
  const descriptor = Object.getOwnPropertyDescriptor(proto, 'value');
@@ -3318,6 +3436,18 @@ async function attemptNativeBatchFill(page, fields) {
3318
3436
  else
3319
3437
  target.value = next;
3320
3438
  }
3439
+ function setInputCheckedReactAware(target, next) {
3440
+ const hadReactTracker = clearReactTracker(target, next);
3441
+ if (!hadReactTracker && target.checked === next)
3442
+ return true;
3443
+ const descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'checked');
3444
+ if (descriptor?.set)
3445
+ descriptor.set.call(target, next);
3446
+ else
3447
+ target.checked = next;
3448
+ dispatch(target);
3449
+ return target.checked === next;
3450
+ }
3321
3451
  function currentValue(el) {
3322
3452
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement)
3323
3453
  return el.value;
@@ -3470,6 +3600,7 @@ async function attemptNativeBatchFill(page, fields) {
3470
3600
  const inputType = control instanceof HTMLInputElement ? control.type : 'textarea';
3471
3601
  const v = canonicalizeHtmlInputValueInPage(inputType, value);
3472
3602
  control.focus();
3603
+ clearReactTracker(control);
3473
3604
  setInputLikeValue(control, v);
3474
3605
  dispatch(control);
3475
3606
  return matches(currentValue(control), v, false);
@@ -3478,6 +3609,7 @@ async function attemptNativeBatchFill(page, fields) {
3478
3609
  if (!isPlainContentEditableBatch(control))
3479
3610
  return false;
3480
3611
  control.focus();
3612
+ clearReactTracker(control);
3481
3613
  control.textContent = value;
3482
3614
  dispatch(control);
3483
3615
  return matches(currentValue(control), value, false);
@@ -3556,19 +3688,13 @@ async function attemptNativeBatchFill(page, fields) {
3556
3688
  if (!matches(choiceLabel(option), value, exact))
3557
3689
  continue;
3558
3690
  if (option instanceof HTMLInputElement) {
3559
- if (!option.checked)
3560
- option.click();
3561
- if (!option.checked) {
3562
- option.checked = true;
3563
- dispatch(option);
3564
- }
3565
- return option.checked;
3691
+ return setInputCheckedReactAware(option, true);
3566
3692
  }
3567
3693
  if (option instanceof HTMLLabelElement) {
3568
- option.click();
3569
3694
  const control = option.control;
3570
3695
  if (control instanceof HTMLInputElement)
3571
- return control.checked;
3696
+ return setInputCheckedReactAware(control, true);
3697
+ option.click();
3572
3698
  return true;
3573
3699
  }
3574
3700
  // role=radio / role=checkbox: click and verify aria-checked
@@ -3609,13 +3735,8 @@ async function attemptNativeBatchFill(page, fields) {
3609
3735
  continue;
3610
3736
  if (!matches(explicitLabelText(input), label, exact))
3611
3737
  continue;
3612
- if (input.checked !== checked) {
3613
- input.click();
3614
- if (input.checked !== checked) {
3615
- input.checked = checked;
3616
- dispatch(input);
3617
- }
3618
- }
3738
+ if (!setInputCheckedReactAware(input, checked))
3739
+ return false;
3619
3740
  return input.checked === checked;
3620
3741
  }
3621
3742
  const labels = Array.from(document.querySelectorAll('label'));
@@ -3629,12 +3750,8 @@ async function attemptNativeBatchFill(page, fields) {
3629
3750
  continue;
3630
3751
  if (controlType && control.type !== controlType)
3631
3752
  continue;
3632
- if (control.checked !== checked)
3633
- labelEl.click();
3634
- if (control.checked !== checked) {
3635
- control.checked = checked;
3636
- dispatch(control);
3637
- }
3753
+ if (!setInputCheckedReactAware(control, checked))
3754
+ return false;
3638
3755
  return control.checked === checked;
3639
3756
  }
3640
3757
  return false;
@@ -4336,6 +4453,33 @@ async function chooseValueFromLabeledGroup(page, fieldLabel, value, exact) {
4336
4453
  return `${ariaPressed}|${ariaChecked}|${ariaSelected}|${dataState}|${dataSelected}|${className}|${checked}`;
4337
4454
  }).join('||');
4338
4455
  }
4456
+ function dispatch(target) {
4457
+ target.dispatchEvent(new Event('input', { bubbles: true }));
4458
+ target.dispatchEvent(new Event('change', { bubbles: true }));
4459
+ }
4460
+ function clearReactTracker(target, next) {
4461
+ const tracker = target._valueTracker;
4462
+ if (tracker && typeof tracker.setValue === 'function') {
4463
+ try {
4464
+ tracker.setValue(String(!next));
4465
+ return true;
4466
+ }
4467
+ catch { /* ignore */ }
4468
+ }
4469
+ return false;
4470
+ }
4471
+ function setInputCheckedReactAware(target, next) {
4472
+ const hadReactTracker = clearReactTracker(target, next);
4473
+ if (!hadReactTracker && target.checked === next)
4474
+ return true;
4475
+ const descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'checked');
4476
+ if (descriptor?.set)
4477
+ descriptor.set.call(target, next);
4478
+ else
4479
+ target.checked = next;
4480
+ dispatch(target);
4481
+ return target.checked === next;
4482
+ }
4339
4483
  for (const candidate of candidates) {
4340
4484
  const options = Array.from(candidate.root.querySelectorAll('input[type="radio"], input[type="checkbox"], [role="radio"], [role="checkbox"], label, button'));
4341
4485
  for (const option of options) {
@@ -4344,15 +4488,14 @@ async function chooseValueFromLabeledGroup(page, fieldLabel, value, exact) {
4344
4488
  if (!matchesChoice(choiceLabel(option)))
4345
4489
  continue;
4346
4490
  if (option instanceof HTMLInputElement) {
4347
- option.click();
4348
- return option.checked;
4491
+ return setInputCheckedReactAware(option, true);
4349
4492
  }
4350
4493
  if (option instanceof HTMLLabelElement) {
4351
4494
  const labelBeforeSig = selectionSignature(options);
4352
- option.click();
4353
4495
  const control = option.control;
4354
4496
  if (control instanceof HTMLInputElement)
4355
- return control.checked;
4497
+ return setInputCheckedReactAware(control, true);
4498
+ option.click();
4356
4499
  // No backing input — verify via group signature change so we don't
4357
4500
  // silently no-op on label wrappers whose target is unparented.
4358
4501
  const labelAfterSig = selectionSignature(options);
@@ -4771,6 +4914,7 @@ export async function pickListboxOption(page, label, opts) {
4771
4914
  // commit path, which ALL tested libraries honor. No-op for native
4772
4915
  // <select> / plain ARIA listboxes — see isAutocompleteCombobox.
4773
4916
  await pressEnterToCommitListbox(page, openedHandle);
4917
+ await dispatchListboxCommitEvents(openedHandle);
4774
4918
  if (!opts?.fieldLabel ||
4775
4919
  await confirmListboxSelection(page, opts.fieldLabel, label, exact, anchor, openedHandle, selectedOptionText, {
4776
4920
  editable: openedEditable,
@@ -4879,6 +5023,7 @@ export async function pickListboxOption(page, label, opts) {
4879
5023
  if (keyboardSelection) {
4880
5024
  selectedOptionText = keyboardSelection;
4881
5025
  attemptedSelection = true;
5026
+ await dispatchListboxCommitEvents(openedHandle);
4882
5027
  if (!opts?.fieldLabel ||
4883
5028
  await confirmListboxSelection(page, opts.fieldLabel, label, exact, anchor, openedHandle, selectedOptionText, {
4884
5029
  editable: openedEditable,
@@ -5014,6 +5159,33 @@ export async function setCheckedControl(page, label, opts) {
5014
5159
  }
5015
5160
  return el instanceof HTMLElement ? el : null;
5016
5161
  }
5162
+ function dispatch(target) {
5163
+ target.dispatchEvent(new Event('input', { bubbles: true }));
5164
+ target.dispatchEvent(new Event('change', { bubbles: true }));
5165
+ }
5166
+ function clearReactTracker(target, next) {
5167
+ const tracker = target._valueTracker;
5168
+ if (tracker && typeof tracker.setValue === 'function') {
5169
+ try {
5170
+ tracker.setValue(String(!next));
5171
+ return true;
5172
+ }
5173
+ catch { /* ignore */ }
5174
+ }
5175
+ return false;
5176
+ }
5177
+ function setInputCheckedReactAware(target, next) {
5178
+ const hadReactTracker = clearReactTracker(target, next);
5179
+ if (!hadReactTracker && target.checked === next)
5180
+ return true;
5181
+ const descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'checked');
5182
+ if (descriptor?.set)
5183
+ descriptor.set.call(target, next);
5184
+ else
5185
+ target.checked = next;
5186
+ dispatch(target);
5187
+ return target.checked === next;
5188
+ }
5017
5189
  const selector = 'input[type="checkbox"], input[type="radio"], [role="checkbox"], [role="radio"], [role="switch"]';
5018
5190
  const candidates = Array.from(document.querySelectorAll(selector)).filter((el) => el instanceof HTMLElement && visible(el));
5019
5191
  const target = candidates.find((el) => {
@@ -5033,14 +5205,12 @@ export async function setCheckedControl(page, label, opts) {
5033
5205
  if (kind === 'radio' && payload.checked === false) {
5034
5206
  return { matched: true, success: before === false, reason: 'radio-uncheck', kind, name };
5035
5207
  }
5036
- if (before !== payload.checked) {
5208
+ if (before !== payload.checked && !(target instanceof HTMLInputElement)) {
5037
5209
  clickTarget(target)?.click();
5038
5210
  }
5039
5211
  let after = readChecked(target);
5040
- if (after !== payload.checked && target instanceof HTMLInputElement) {
5041
- target.checked = payload.checked;
5042
- target.dispatchEvent(new Event('input', { bubbles: true }));
5043
- target.dispatchEvent(new Event('change', { bubbles: true }));
5212
+ if (target instanceof HTMLInputElement) {
5213
+ setInputCheckedReactAware(target, payload.checked);
5044
5214
  after = target.checked;
5045
5215
  }
5046
5216
  return {