@weavetab/mcp 2.5.0-beta.5 → 2.5.1

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.
Files changed (160) hide show
  1. package/AGENTS.md +49 -49
  2. package/ARCHITECTURE.md +590 -590
  3. package/README.md +216 -100
  4. package/SECURITY.md +207 -207
  5. package/TROUBLESHOOTING.md +373 -365
  6. package/dist/anticaptcha/detector.js +395 -395
  7. package/dist/anticaptcha/solver.js +130 -130
  8. package/dist/cdp/bridge.js +73 -73
  9. package/dist/cdp/connector.js +46 -22
  10. package/dist/cdp/extension.js +34 -34
  11. package/dist/cdp/helpers.js +174 -174
  12. package/dist/cdp/pruner.js +195 -195
  13. package/dist/cdp/ref_cache.js +66 -66
  14. package/dist/cdp/security.js +10 -10
  15. package/dist/cdp/verify.js +57 -57
  16. package/dist/cdp/walker.js +563 -525
  17. package/dist/cli/plugin.js +26 -3
  18. package/dist/config/loader.d.ts +0 -3
  19. package/dist/config/loader.js +0 -9
  20. package/dist/index.js +0 -4
  21. package/dist/intelligence/intent.js +81 -81
  22. package/dist/intelligence/sessionMemory.js +2 -2
  23. package/dist/overlay/ghost.js +22 -22
  24. package/dist/overlay/injector.js +89 -89
  25. package/dist/plugin/extension_bridge.js +54 -54
  26. package/dist/plugin/interface.d.ts +7 -0
  27. package/dist/plugin/loader.js +3 -4
  28. package/dist/plugin/schema.d.ts +11 -0
  29. package/dist/plugin/schema.js +59 -0
  30. package/dist/security/guard.js +18 -18
  31. package/dist/sensors/actionability.js +167 -167
  32. package/dist/sensors/guard.js +29 -29
  33. package/dist/sensors/network.js +15 -17
  34. package/dist/server.d.ts +1 -1
  35. package/dist/server.js +173 -144
  36. package/dist/state/delta.js +6 -7
  37. package/dist/tools/automation.js +85 -85
  38. package/dist/tools/canvas.js +48 -25
  39. package/dist/tools/checkpoint.js +94 -35
  40. package/dist/tools/click.js +52 -41
  41. package/dist/tools/clipboard.js +80 -74
  42. package/dist/tools/design_extract.js +441 -441
  43. package/dist/tools/dialog.js +13 -13
  44. package/dist/tools/emulation.d.ts +2 -0
  45. package/dist/tools/emulation.js +7 -0
  46. package/dist/tools/fill.js +344 -347
  47. package/dist/tools/find.js +78 -52
  48. package/dist/tools/highlight.js +67 -67
  49. package/dist/tools/inspect.js +54 -25
  50. package/dist/tools/key.js +42 -35
  51. package/dist/tools/navigate.d.ts +1 -1
  52. package/dist/tools/navigate.js +35 -35
  53. package/dist/tools/office/parse.js +62 -18
  54. package/dist/tools/pdf.d.ts +9 -0
  55. package/dist/tools/pdf.js +7 -4
  56. package/dist/tools/performance.d.ts +11 -4
  57. package/dist/tools/performance.js +94 -42
  58. package/dist/tools/performance_test.d.ts +2 -2
  59. package/dist/tools/performance_test.js +69 -69
  60. package/dist/tools/pointer.js +116 -116
  61. package/dist/tools/read.js +99 -67
  62. package/dist/tools/recording.js +9 -0
  63. package/dist/tools/scrape.js +146 -146
  64. package/dist/tools/screenshot.d.ts +2 -0
  65. package/dist/tools/screenshot.js +46 -4
  66. package/dist/tools/scroll.d.ts +3 -1
  67. package/dist/tools/scroll.js +13 -15
  68. package/dist/tools/select.d.ts +3 -0
  69. package/dist/tools/select.js +131 -124
  70. package/dist/tools/snapshot.js +8 -6
  71. package/dist/tools/storage.d.ts +5 -1
  72. package/dist/tools/storage.js +16 -0
  73. package/dist/tools/tabs.d.ts +7 -5
  74. package/dist/tools/tabs.js +79 -26
  75. package/dist/tools/thought.js +8 -8
  76. package/dist/tools/type.js +274 -274
  77. package/dist/tools/type_secret.js +23 -23
  78. package/dist/tools/upload.js +3 -3
  79. package/dist/tools/video_compiler.js +9 -0
  80. package/dist/tools/wait.js +35 -35
  81. package/dist/version.d.ts +1 -1
  82. package/dist/version.js +1 -1
  83. package/docs/DOCUMENTATION.md +583 -585
  84. package/docs/PLUGINS.md +139 -121
  85. package/docs/TOOLS.md +3 -4
  86. package/docs/architecture/blind-injection.md +83 -83
  87. package/docs/architecture/overview.md +49 -49
  88. package/docs/configuration/configuration.md +39 -27
  89. package/docs/configuration/folders.md +62 -55
  90. package/docs/desktop-automation/electron-apps.md +225 -214
  91. package/docs/get-started/installation.md +1 -1
  92. package/docs/get-started/introduction.md +60 -60
  93. package/docs/get-started/quickstart.md +2 -2
  94. package/docs/get-started/setup-wizard.md +1 -1
  95. package/docs/intelligence/site-profiles.md +151 -151
  96. package/docs/tools/tools.md +582 -631
  97. package/docs/troubleshooting/trouble-loop.md +31 -31
  98. package/ext-dist/background.js +415 -415
  99. package/ext-dist/content.js +2527 -2527
  100. package/ext-dist/dashboard.html +583 -583
  101. package/ext-dist/hud/hud.js +3 -3
  102. package/ext-dist/hud/index.html +20 -20
  103. package/ext-dist/manifest.json +73 -73
  104. package/ext-dist/styles.css +223 -223
  105. package/mcp.json +796 -805
  106. package/package.json +8 -8
  107. package/server.json +23 -23
  108. package/smithery.yaml +7 -11
  109. package/dist/cdp/walker.test.d.ts +0 -2
  110. package/dist/cdp/walker.test.js +0 -172
  111. package/dist/intelligence/sessionMemory.test.d.ts +0 -2
  112. package/dist/intelligence/sessionMemory.test.js +0 -62
  113. package/dist/intelligence/trail.test.d.ts +0 -2
  114. package/dist/intelligence/trail.test.js +0 -110
  115. package/dist/state/cache.test.d.ts +0 -2
  116. package/dist/state/cache.test.js +0 -76
  117. package/dist/state/delta.test.d.ts +0 -2
  118. package/dist/state/delta.test.js +0 -279
  119. package/dist/state/groups.d.ts +0 -15
  120. package/dist/state/groups.js +0 -80
  121. package/dist/tools/accessibility.d.ts +0 -14
  122. package/dist/tools/accessibility.js +0 -32
  123. package/dist/tools/ask.d.ts +0 -14
  124. package/dist/tools/ask.js +0 -49
  125. package/dist/tools/click_and_wait.d.ts +0 -33
  126. package/dist/tools/click_and_wait.js +0 -149
  127. package/dist/tools/drag.d.ts +0 -15
  128. package/dist/tools/drag.js +0 -29
  129. package/dist/tools/geo.d.ts +0 -19
  130. package/dist/tools/geo.js +0 -29
  131. package/dist/tools/github/analyze.d.ts +0 -4
  132. package/dist/tools/github/analyze.js +0 -113
  133. package/dist/tools/github/cache.d.ts +0 -21
  134. package/dist/tools/github/cache.js +0 -28
  135. package/dist/tools/github/index.d.ts +0 -19
  136. package/dist/tools/github/index.js +0 -40
  137. package/dist/tools/github/issues.d.ts +0 -9
  138. package/dist/tools/github/issues.js +0 -88
  139. package/dist/tools/github/pr.d.ts +0 -5
  140. package/dist/tools/github/pr.js +0 -88
  141. package/dist/tools/github/rateLimiter.d.ts +0 -4
  142. package/dist/tools/github/rateLimiter.js +0 -46
  143. package/dist/tools/github/read.d.ts +0 -34
  144. package/dist/tools/github/read.js +0 -74
  145. package/dist/tools/github/repoContext.d.ts +0 -20
  146. package/dist/tools/github/repoContext.js +0 -129
  147. package/dist/tools/hover.d.ts +0 -19
  148. package/dist/tools/hover.js +0 -57
  149. package/dist/tools/integration.test.d.ts +0 -2
  150. package/dist/tools/integration.test.js +0 -96
  151. package/dist/tools/macro_compiler.d.ts +0 -7
  152. package/dist/tools/macro_compiler.js +0 -82
  153. package/dist/tools/mouse.d.ts +0 -21
  154. package/dist/tools/mouse.js +0 -62
  155. package/dist/tools/response.d.ts +0 -129
  156. package/dist/tools/response.js +0 -5
  157. package/dist/tools/wait_for.d.ts +0 -23
  158. package/dist/tools/wait_for.js +0 -30
  159. package/dist/tools/windows.d.ts +0 -22
  160. package/dist/tools/windows.js +0 -57
@@ -22,12 +22,12 @@ import { verifyActionability, quickHitCheck } from "../sensors/actionability.js"
22
22
  export async function detectFramework(session) {
23
23
  try {
24
24
  const { result } = await session.Runtime.evaluate({
25
- expression: `(function() {
26
- const root = document.documentElement;
27
- if (root.__reactFiber || root._reactRootContainer || window.React || window.react) return 'react';
28
- if (root.__vue__ || document.querySelector('[data-v-]') || window.Vue || window.vue) return 'vue';
29
- if (root.ng || document.querySelector('[ng-version]') || window.angular || window.ng) return 'angular';
30
- return 'vanilla';
25
+ expression: `(function() {
26
+ const root = document.documentElement;
27
+ if (root.__reactFiber || root._reactRootContainer || window.React || window.react) return 'react';
28
+ if (root.__vue__ || document.querySelector('[data-v-]') || window.Vue || window.vue) return 'vue';
29
+ if (root.ng || document.querySelector('[ng-version]') || window.angular || window.ng) return 'angular';
30
+ return 'vanilla';
31
31
  })()`,
32
32
  returnByValue: true,
33
33
  });
@@ -40,14 +40,14 @@ export async function detectFramework(session) {
40
40
  async function focusEditable(session, objectId, clearFirst, fast = false) {
41
41
  const { Input } = session;
42
42
  await session.Runtime.callFunctionOn({
43
- functionDeclaration: `async function() {
44
- if (this.scrollIntoViewIfNeeded) this.scrollIntoViewIfNeeded();
45
- else {
46
- this.scrollIntoView({behavior:'smooth',block:'center'});
47
- await new Promise(r => setTimeout(r, ${fast ? 50 : 400}));
48
- }
49
- this.focus();
50
- this.click();
43
+ functionDeclaration: `async function() {
44
+ if (this.scrollIntoViewIfNeeded) this.scrollIntoViewIfNeeded();
45
+ else {
46
+ this.scrollIntoView({behavior:'smooth',block:'center'});
47
+ await new Promise(r => setTimeout(r, ${fast ? 50 : 400}));
48
+ }
49
+ this.focus();
50
+ this.click();
51
51
  }`,
52
52
  objectId,
53
53
  silent: true,
@@ -55,15 +55,15 @@ async function focusEditable(session, objectId, clearFirst, fast = false) {
55
55
  });
56
56
  if (clearFirst) {
57
57
  await session.Runtime.callFunctionOn({
58
- functionDeclaration: `function() {
59
- if (typeof this.select === 'function') this.select();
60
- else if (this.isContentEditable) {
61
- this.textContent = '';
62
- this.dispatchEvent(new Event('input', { bubbles: true }));
63
- } else {
64
- this.value = '';
65
- this.dispatchEvent(new Event('input', { bubbles: true }));
66
- }
58
+ functionDeclaration: `function() {
59
+ if (typeof this.select === 'function') this.select();
60
+ else if (this.isContentEditable) {
61
+ this.textContent = '';
62
+ this.dispatchEvent(new Event('input', { bubbles: true }));
63
+ } else {
64
+ this.value = '';
65
+ this.dispatchEvent(new Event('input', { bubbles: true }));
66
+ }
67
67
  }`,
68
68
  objectId,
69
69
  silent: true,
@@ -90,34 +90,34 @@ export async function frameworkAwareType(session, objectId, text, framework) {
90
90
  // for — the old `.simulated = true` trick only worked in React <16 and is a
91
91
  // no-op in modern React (Event properties are frozen after construction).
92
92
  await Runtime.callFunctionOn({
93
- functionDeclaration: `function(text) {
94
- const nativeInputSetter = Object.getOwnPropertyDescriptor(
95
- window.HTMLInputElement.prototype, 'value'
96
- )?.set;
97
- const nativeTextAreaSetter = Object.getOwnPropertyDescriptor(
98
- window.HTMLTextAreaElement.prototype, 'value'
99
- )?.set;
100
-
101
- if (this.tagName === 'INPUT' && nativeInputSetter) {
102
- nativeInputSetter.call(this, text);
103
- } else if (this.tagName === 'TEXTAREA' && nativeTextAreaSetter) {
104
- nativeTextAreaSetter.call(this, text);
105
- } else if (this.isContentEditable) {
106
- this.textContent = text;
107
- } else {
108
- this.value = text;
109
- }
110
-
111
- // Single InputEvent with inputType:'insertText' — React 16/17/18 SyntheticEvent
112
- // source. Do NOT also dispatch a plain Event('input') here; that would
113
- // fire the onChange handler a second time and duplicate the value.
114
- this.dispatchEvent(new InputEvent('input', {
115
- bubbles: true,
116
- cancelable: true,
117
- inputType: 'insertText',
118
- data: text,
119
- }));
120
- this.dispatchEvent(new Event('change', { bubbles: true, cancelable: true }));
93
+ functionDeclaration: `function(text) {
94
+ const nativeInputSetter = Object.getOwnPropertyDescriptor(
95
+ window.HTMLInputElement.prototype, 'value'
96
+ )?.set;
97
+ const nativeTextAreaSetter = Object.getOwnPropertyDescriptor(
98
+ window.HTMLTextAreaElement.prototype, 'value'
99
+ )?.set;
100
+
101
+ if (this.tagName === 'INPUT' && nativeInputSetter) {
102
+ nativeInputSetter.call(this, text);
103
+ } else if (this.tagName === 'TEXTAREA' && nativeTextAreaSetter) {
104
+ nativeTextAreaSetter.call(this, text);
105
+ } else if (this.isContentEditable) {
106
+ this.textContent = text;
107
+ } else {
108
+ this.value = text;
109
+ }
110
+
111
+ // Single InputEvent with inputType:'insertText' — React 16/17/18 SyntheticEvent
112
+ // source. Do NOT also dispatch a plain Event('input') here; that would
113
+ // fire the onChange handler a second time and duplicate the value.
114
+ this.dispatchEvent(new InputEvent('input', {
115
+ bubbles: true,
116
+ cancelable: true,
117
+ inputType: 'insertText',
118
+ data: text,
119
+ }));
120
+ this.dispatchEvent(new Event('change', { bubbles: true, cancelable: true }));
121
121
  }`,
122
122
  objectId,
123
123
  arguments: [{ value: text }],
@@ -165,15 +165,15 @@ async function resolveTypingTarget(session, map, args, _targetId) {
165
165
  if (!element && id && !id.startsWith("w:")) {
166
166
  try {
167
167
  const checkEl = await session.Runtime.evaluate({
168
- expression: `(function(){
169
- const el = document.querySelector(${JSON.stringify(id)}) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(${JSON.stringify(id)}) : null);
170
- if (!el) return null;
171
- const r = el.getBoundingClientRect();
172
- return {
173
- label: el.getAttribute('aria-label') || el.getAttribute('placeholder') || el.getAttribute('name') || 'selector-match',
174
- x: Math.round(r.left + r.width / 2),
175
- y: Math.round(r.top + r.height / 2)
176
- };
168
+ expression: `(function(){
169
+ const el = document.querySelector(${JSON.stringify(id)}) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(${JSON.stringify(id)}) : null);
170
+ if (!el) return null;
171
+ const r = el.getBoundingClientRect();
172
+ return {
173
+ label: el.getAttribute('aria-label') || el.getAttribute('placeholder') || el.getAttribute('name') || 'selector-match',
174
+ x: Math.round(r.left + r.width / 2),
175
+ y: Math.round(r.top + r.height / 2)
176
+ };
177
177
  })()`,
178
178
  returnByValue: true
179
179
  });
@@ -248,48 +248,48 @@ async function resolveTypingTarget(session, map, args, _targetId) {
248
248
  return null;
249
249
  }
250
250
  const active = await session.Runtime.evaluate({
251
- expression: `(function(){
252
- const pick = (el) => {
253
- if (!el) return null;
254
- const r = el.getBoundingClientRect();
255
- if (r.width < 20 || r.height < 10) return null;
256
- if (r.bottom < 0 || r.top > innerHeight) return null;
257
- return {
258
- label: el.getAttribute('aria-label') || el.getAttribute('placeholder') || el.getAttribute('name') || (el.textContent||'').trim().slice(0,40) || 'input',
259
- selector: ['textarea[name="q"]','input[name="q"]','[aria-label="Search"]','[title="Search"]','input[type="search"]','textarea','input[type="text"]','input:not([type])','[role="combobox"]','[role="textbox"]','[contenteditable="true"]','[contenteditable=""]'].find(function(s){try{return el.matches(s)}catch(e){return false}}) || '',
260
- x: Math.round(r.left + r.width/2),
261
- y: Math.round(r.top + r.height/2)
262
- };
263
- };
264
- var el = document.activeElement;
265
- var isEditable = el && el !== document.body && (
266
- el.isContentEditable ||
267
- el.tagName === 'TEXTAREA' ||
268
- (el.tagName === 'INPUT' && !['button','submit','checkbox','radio','hidden','file'].includes((el.type||'').toLowerCase())) ||
269
- el.getAttribute('role') === 'textbox' || el.getAttribute('role') === 'searchbox' || el.getAttribute('role') === 'combobox'
270
- );
271
- if (isEditable) {
272
- var fromActive = pick(el);
273
- if (fromActive) return JSON.stringify(fromActive);
274
- }
275
-
276
- var sel = 'textarea[name="q"], input[name="q"], [aria-label="Search"], [title="Search"]';
277
- var q = document.querySelector(sel);
278
- var fromQ = pick(q);
279
- if (fromQ) return JSON.stringify(fromQ);
280
-
281
- var sels = ['[contenteditable="true"]','[contenteditable=""]','textarea','input[type="search"]','input[type="text"]','input:not([type])','[role="textbox"]','[role="combobox"]','input[name="q"]'];
282
- var best = null, bestArea = 0;
283
- for (var si = 0; si < sels.length; si++) {
284
- var nodes = document.querySelectorAll(sels[si]);
285
- for (var ni = 0; ni < nodes.length; ni++) {
286
- var r = nodes[ni].getBoundingClientRect();
287
- var area = r.width * r.height;
288
- if (area > bestArea && r.top >= 0 && r.top < innerHeight) { bestArea = area; best = nodes[ni]; }
289
- }
290
- }
291
- if (!best) return null;
292
- return JSON.stringify(pick(best));
251
+ expression: `(function(){
252
+ const pick = (el) => {
253
+ if (!el) return null;
254
+ const r = el.getBoundingClientRect();
255
+ if (r.width < 20 || r.height < 10) return null;
256
+ if (r.bottom < 0 || r.top > innerHeight) return null;
257
+ return {
258
+ label: el.getAttribute('aria-label') || el.getAttribute('placeholder') || el.getAttribute('name') || (el.textContent||'').trim().slice(0,40) || 'input',
259
+ selector: ['textarea[name="q"]','input[name="q"]','[aria-label="Search"]','[title="Search"]','input[type="search"]','textarea','input[type="text"]','input:not([type])','[role="combobox"]','[role="textbox"]','[contenteditable="true"]','[contenteditable=""]'].find(function(s){try{return el.matches(s)}catch(e){return false}}) || '',
260
+ x: Math.round(r.left + r.width/2),
261
+ y: Math.round(r.top + r.height/2)
262
+ };
263
+ };
264
+ var el = document.activeElement;
265
+ var isEditable = el && el !== document.body && (
266
+ el.isContentEditable ||
267
+ el.tagName === 'TEXTAREA' ||
268
+ (el.tagName === 'INPUT' && !['button','submit','checkbox','radio','hidden','file'].includes((el.type||'').toLowerCase())) ||
269
+ el.getAttribute('role') === 'textbox' || el.getAttribute('role') === 'searchbox' || el.getAttribute('role') === 'combobox'
270
+ );
271
+ if (isEditable) {
272
+ var fromActive = pick(el);
273
+ if (fromActive) return JSON.stringify(fromActive);
274
+ }
275
+
276
+ var sel = 'textarea[name="q"], input[name="q"], [aria-label="Search"], [title="Search"]';
277
+ var q = document.querySelector(sel);
278
+ var fromQ = pick(q);
279
+ if (fromQ) return JSON.stringify(fromQ);
280
+
281
+ var sels = ['[contenteditable="true"]','[contenteditable=""]','textarea','input[type="search"]','input[type="text"]','input:not([type])','[role="textbox"]','[role="combobox"]','input[name="q"]'];
282
+ var best = null, bestArea = 0;
283
+ for (var si = 0; si < sels.length; si++) {
284
+ var nodes = document.querySelectorAll(sels[si]);
285
+ for (var ni = 0; ni < nodes.length; ni++) {
286
+ var r = nodes[ni].getBoundingClientRect();
287
+ var area = r.width * r.height;
288
+ if (area > bestArea && r.top >= 0 && r.top < innerHeight) { bestArea = area; best = nodes[ni]; }
289
+ }
290
+ }
291
+ if (!best) return null;
292
+ return JSON.stringify(pick(best));
293
293
  })()`,
294
294
  returnByValue: true,
295
295
  }).catch(() => null);
@@ -434,21 +434,21 @@ export async function weaveType(session, args, config) {
434
434
  }
435
435
  if (target && target.selector) {
436
436
  const isEditableRes = await session.Runtime.evaluate({
437
- expression: `(function(sel) {
438
- const el = document.querySelector(sel);
439
- if (!el) return true; // optimistic: let it try
440
- const tag = el.tagName.toLowerCase();
441
- const t = (el.getAttribute('type') || '').toLowerCase();
442
- const role = (el.getAttribute('role') || '').toLowerCase();
443
- // Standard editable
444
- if (el.isContentEditable) return true;
445
- if (tag === 'textarea') return true;
446
- if (tag === 'input' && !['button','submit','checkbox','radio','hidden','file','image','reset'].includes(t)) return true;
447
- // ARIA editable roles — many custom widgets use these
448
- if (role === 'textbox' || role === 'combobox' || role === 'searchbox' || role === 'spinbutton') return true;
449
- // Custom div/span inputs often have no role — check for contenteditable ancestors
450
- if (el.closest('[contenteditable]')) return true;
451
- return false;
437
+ expression: `(function(sel) {
438
+ const el = document.querySelector(sel);
439
+ if (!el) return true; // optimistic: let it try
440
+ const tag = el.tagName.toLowerCase();
441
+ const t = (el.getAttribute('type') || '').toLowerCase();
442
+ const role = (el.getAttribute('role') || '').toLowerCase();
443
+ // Standard editable
444
+ if (el.isContentEditable) return true;
445
+ if (tag === 'textarea') return true;
446
+ if (tag === 'input' && !['button','submit','checkbox','radio','hidden','file','image','reset'].includes(t)) return true;
447
+ // ARIA editable roles — many custom widgets use these
448
+ if (role === 'textbox' || role === 'combobox' || role === 'searchbox' || role === 'spinbutton') return true;
449
+ // Custom div/span inputs often have no role — check for contenteditable ancestors
450
+ if (el.closest('[contenteditable]')) return true;
451
+ return false;
452
452
  })(${JSON.stringify(target.selector)})`,
453
453
  returnByValue: true
454
454
  }).catch(() => null);
@@ -483,14 +483,14 @@ export async function weaveType(session, args, config) {
483
483
  let frameUrl = null;
484
484
  try {
485
485
  const res = await session.Runtime.evaluate({
486
- expression: `(function(sel) {
487
- const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
488
- if (!el) return null;
489
- return {
490
- coords: window.__Weavetab_getCoords(sel),
491
- inIframe: el.ownerDocument.defaultView !== window,
492
- frameUrl: el.ownerDocument.defaultView.location.href
493
- };
486
+ expression: `(function(sel) {
487
+ const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
488
+ if (!el) return null;
489
+ return {
490
+ coords: window.__Weavetab_getCoords(sel),
491
+ inIframe: el.ownerDocument.defaultView !== window,
492
+ frameUrl: el.ownerDocument.defaultView.location.href
493
+ };
494
494
  })(${JSON.stringify(selector)})`,
495
495
  returnByValue: true
496
496
  });
@@ -550,17 +550,17 @@ export async function weaveType(session, args, config) {
550
550
  // Detect password / secret fields early to protect HUD, logs, and register secret
551
551
  const isSecretParam = !!args.isSecret || !!args.secret;
552
552
  const isPassword = isSecretParam || await session.Runtime.evaluate({
553
- expression: `(function(sel) {
554
- const el = (sel === 'document.activeElement') ? document.activeElement : (document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null));
555
- if (!el) return false;
556
- const t = (el.getAttribute('type') || '').toLowerCase();
557
- const ac = (el.getAttribute('autocomplete') || '').toLowerCase();
558
- const name = (el.getAttribute('name') || '').toLowerCase();
559
- const id = (el.getAttribute('id') || '').toLowerCase();
560
- const aria = (el.getAttribute('aria-label') || '').toLowerCase();
561
- const isSecAttr = el.hasAttribute('data-wt-secret');
562
- const secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
563
- return isSecAttr || t === 'password' || ac.includes('password') || secretRegex.test(name) || secretRegex.test(id) || secretRegex.test(aria);
553
+ expression: `(function(sel) {
554
+ const el = (sel === 'document.activeElement') ? document.activeElement : (document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null));
555
+ if (!el) return false;
556
+ const t = (el.getAttribute('type') || '').toLowerCase();
557
+ const ac = (el.getAttribute('autocomplete') || '').toLowerCase();
558
+ const name = (el.getAttribute('name') || '').toLowerCase();
559
+ const id = (el.getAttribute('id') || '').toLowerCase();
560
+ const aria = (el.getAttribute('aria-label') || '').toLowerCase();
561
+ const isSecAttr = el.hasAttribute('data-wt-secret');
562
+ const secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
563
+ return isSecAttr || t === 'password' || ac.includes('password') || secretRegex.test(name) || secretRegex.test(id) || secretRegex.test(aria);
564
564
  })(${JSON.stringify(selector)})`,
565
565
  returnByValue: true
566
566
  }).then(r => r.result?.value === true).catch(() => false);
@@ -602,10 +602,10 @@ export async function weaveType(session, args, config) {
602
602
  }
603
603
  // Check if element is ALREADY focused before clicking it to prevent unfocusing or toggling
604
604
  const alreadyFocusedRes = await session.Runtime.evaluate({
605
- expression: `(function(sel) {
606
- if (!sel || sel === 'document.activeElement') return true;
607
- const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
608
- return el ? (document.activeElement === el || el.contains(document.activeElement)) : false;
605
+ expression: `(function(sel) {
606
+ if (!sel || sel === 'document.activeElement') return true;
607
+ const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
608
+ return el ? (document.activeElement === el || el.contains(document.activeElement)) : false;
609
609
  })(${JSON.stringify(selector)})`,
610
610
  returnByValue: true
611
611
  }).catch(() => null);
@@ -618,10 +618,10 @@ export async function weaveType(session, args, config) {
618
618
  else if (isAlreadyFocused) {
619
619
  // Ensure focus without hardware click re-trigger
620
620
  await session.Runtime.evaluate({
621
- expression: `(function(sel) {
622
- if (!sel || sel === 'document.activeElement') return;
623
- const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
624
- if (el && typeof el.focus === 'function') el.focus();
621
+ expression: `(function(sel) {
622
+ if (!sel || sel === 'document.activeElement') return;
623
+ const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
624
+ if (el && typeof el.focus === 'function') el.focus();
625
625
  })(${JSON.stringify(selector)})`,
626
626
  returnByValue: false
627
627
  }).catch(() => { });
@@ -630,20 +630,20 @@ export async function weaveType(session, args, config) {
630
630
  const shouldPaste = args.paste || text.length > 8;
631
631
  if (clearFirst === true && !shouldPaste) {
632
632
  await session.Runtime.evaluate({
633
- expression: `(function(sel) {
634
- const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
635
- if (!el) return;
636
- if (el.isContentEditable) {
637
- el.textContent = '';
638
- } else {
639
- el.value = '';
640
- const nativeSetter = el.tagName === 'TEXTAREA'
641
- ? Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set
642
- : Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
643
- if (nativeSetter) nativeSetter.call(el, '');
644
- }
645
- el.dispatchEvent(new Event('input',{bubbles:true}));
646
- el.dispatchEvent(new Event('change',{bubbles:true}));
633
+ expression: `(function(sel) {
634
+ const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
635
+ if (!el) return;
636
+ if (el.isContentEditable) {
637
+ el.textContent = '';
638
+ } else {
639
+ el.value = '';
640
+ const nativeSetter = el.tagName === 'TEXTAREA'
641
+ ? Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set
642
+ : Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
643
+ if (nativeSetter) nativeSetter.call(el, '');
644
+ }
645
+ el.dispatchEvent(new Event('input',{bubbles:true}));
646
+ el.dispatchEvent(new Event('change',{bubbles:true}));
647
647
  })(${JSON.stringify(selector)})`,
648
648
  returnByValue: false,
649
649
  }).catch(() => { });
@@ -652,9 +652,9 @@ export async function weaveType(session, args, config) {
652
652
  }
653
653
  // Verify focus landed correctly
654
654
  const focusCheck = await session.Runtime.evaluate({
655
- expression: `(function(sel) {
656
- const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
657
- return document.activeElement === el || (el ? el.contains(document.activeElement) : false);
655
+ expression: `(function(sel) {
656
+ const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
657
+ return document.activeElement === el || (el ? el.contains(document.activeElement) : false);
658
658
  })(${JSON.stringify(selector)})`,
659
659
  returnByValue: true
660
660
  }).catch(() => null);
@@ -703,12 +703,12 @@ export async function weaveType(session, args, config) {
703
703
  // fallback to Input.insertText which targets the currently-focused element.
704
704
  if (selector) {
705
705
  const pasteCheck = await session.Runtime.evaluate({
706
- expression: `(function(sel) {
707
- var el = (sel === 'document.activeElement') ? document.activeElement : (document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null));
708
- if (!el) return null;
709
- var isSec = el.hasAttribute('data-wt-secret') || (el.tagName === 'INPUT' && el.type === 'password');
710
- if (isSec) return (el.value && el.value.length > 0) ? '__HAS_CONTENT__' : '';
711
- return el.value !== undefined ? el.value : (el.textContent || null);
706
+ expression: `(function(sel) {
707
+ var el = (sel === 'document.activeElement') ? document.activeElement : (document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null));
708
+ if (!el) return null;
709
+ var isSec = el.hasAttribute('data-wt-secret') || (el.tagName === 'INPUT' && el.type === 'password');
710
+ if (isSec) return (el.value && el.value.length > 0) ? '__HAS_CONTENT__' : '';
711
+ return el.value !== undefined ? el.value : (el.textContent || null);
712
712
  })(${JSON.stringify(selector)})`,
713
713
  returnByValue: true,
714
714
  }).catch(() => null);
@@ -739,33 +739,33 @@ export async function weaveType(session, args, config) {
739
739
  else {
740
740
  // Selector-based fallback — same native-setter logic, single InputEvent only
741
741
  await session.Runtime.evaluate({
742
- expression: `(function(sel, txt) {
743
- const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
744
- if (!el) return;
745
-
746
- const nativeInputSetter = Object.getOwnPropertyDescriptor(
747
- window.HTMLInputElement.prototype, 'value'
748
- )?.set;
749
- const nativeTextAreaSetter = Object.getOwnPropertyDescriptor(
750
- window.HTMLTextAreaElement.prototype, 'value'
751
- )?.set;
752
-
753
- if (el.tagName === 'INPUT' && nativeInputSetter) {
754
- nativeInputSetter.call(el, txt);
755
- } else if (el.tagName === 'TEXTAREA' && nativeTextAreaSetter) {
756
- nativeTextAreaSetter.call(el, txt);
757
- } else if (el.isContentEditable) {
758
- el.textContent = txt;
759
- } else {
760
- el.value = txt;
761
- }
762
-
763
- // Single InputEvent — React 16/17/18 SyntheticEvent source.
764
- // Do NOT also dispatch plain Event('input') — that fires onChange twice.
765
- el.dispatchEvent(new InputEvent('input', {
766
- bubbles: true, cancelable: true, inputType: 'insertText', data: txt
767
- }));
768
- el.dispatchEvent(new Event('change', { bubbles: true, cancelable: true }));
742
+ expression: `(function(sel, txt) {
743
+ const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
744
+ if (!el) return;
745
+
746
+ const nativeInputSetter = Object.getOwnPropertyDescriptor(
747
+ window.HTMLInputElement.prototype, 'value'
748
+ )?.set;
749
+ const nativeTextAreaSetter = Object.getOwnPropertyDescriptor(
750
+ window.HTMLTextAreaElement.prototype, 'value'
751
+ )?.set;
752
+
753
+ if (el.tagName === 'INPUT' && nativeInputSetter) {
754
+ nativeInputSetter.call(el, txt);
755
+ } else if (el.tagName === 'TEXTAREA' && nativeTextAreaSetter) {
756
+ nativeTextAreaSetter.call(el, txt);
757
+ } else if (el.isContentEditable) {
758
+ el.textContent = txt;
759
+ } else {
760
+ el.value = txt;
761
+ }
762
+
763
+ // Single InputEvent — React 16/17/18 SyntheticEvent source.
764
+ // Do NOT also dispatch plain Event('input') — that fires onChange twice.
765
+ el.dispatchEvent(new InputEvent('input', {
766
+ bubbles: true, cancelable: true, inputType: 'insertText', data: txt
767
+ }));
768
+ el.dispatchEvent(new Event('change', { bubbles: true, cancelable: true }));
769
769
  })(${JSON.stringify(selector)}, ${JSON.stringify(text)})`,
770
770
  returnByValue: false,
771
771
  awaitPromise: true,
@@ -783,11 +783,11 @@ export async function weaveType(session, args, config) {
783
783
  // Firing again here would trigger a second onChange and duplicate the value.
784
784
  if (!nativeSetterUsed) {
785
785
  await session.Runtime.evaluate({
786
- expression: `(function(sel) {
787
- const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
788
- if (!el) return;
789
- el.dispatchEvent(new Event('input', { bubbles: true }));
790
- el.dispatchEvent(new Event('change', { bubbles: true }));
786
+ expression: `(function(sel) {
787
+ const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
788
+ if (!el) return;
789
+ el.dispatchEvent(new Event('input', { bubbles: true }));
790
+ el.dispatchEvent(new Event('change', { bubbles: true }));
791
791
  })(${JSON.stringify(selector)})`,
792
792
  returnByValue: false,
793
793
  }).catch(() => { });
@@ -830,21 +830,21 @@ export async function weaveType(session, args, config) {
830
830
  const isActiveElementRef = selector === "document.activeElement";
831
831
  const checkFieldValue = async () => {
832
832
  const expression = isActiveElementRef
833
- ? `(function(expected) {
834
- var el = document.activeElement;
835
- if (!el) return false;
836
- var val = el.value !== undefined ? el.value : (el.textContent || '');
837
- return val === expected || (typeof val === 'string' && val.trim() === expected.trim())
838
- || (typeof val === 'string' && typeof expected === 'string' &&
839
- val.replace(/\\D/g, '') === expected.replace(/\\D/g, '') && expected.length > 3);
833
+ ? `(function(expected) {
834
+ var el = document.activeElement;
835
+ if (!el) return false;
836
+ var val = el.value !== undefined ? el.value : (el.textContent || '');
837
+ return val === expected || (typeof val === 'string' && val.trim() === expected.trim())
838
+ || (typeof val === 'string' && typeof expected === 'string' &&
839
+ val.replace(/\\D/g, '') === expected.replace(/\\D/g, '') && expected.length > 3);
840
840
  })(${JSON.stringify(text)})`
841
- : `(function(sel, expected) {
842
- var el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
843
- if (!el) return false;
844
- var val = el.value !== undefined ? el.value : (el.textContent || '');
845
- return val === expected || (typeof val === 'string' && val.trim() === expected.trim())
846
- || (typeof val === 'string' && typeof expected === 'string' &&
847
- val.replace(/\\D/g, '') === expected.replace(/\\D/g, '') && expected.length > 3);
841
+ : `(function(sel, expected) {
842
+ var el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
843
+ if (!el) return false;
844
+ var val = el.value !== undefined ? el.value : (el.textContent || '');
845
+ return val === expected || (typeof val === 'string' && val.trim() === expected.trim())
846
+ || (typeof val === 'string' && typeof expected === 'string' &&
847
+ val.replace(/\\D/g, '') === expected.replace(/\\D/g, '') && expected.length > 3);
848
848
  })(${JSON.stringify(selector)}, ${JSON.stringify(text)})`;
849
849
  const { result } = await session.Runtime.evaluate({ expression, returnByValue: true }).catch(() => ({ result: { value: false } }));
850
850
  return result?.value === true;
@@ -890,12 +890,12 @@ export async function weaveType(session, args, config) {
890
890
  let detectedFramework = profileFramework;
891
891
  if (!detectedFramework) {
892
892
  const fwCheck = await session.Runtime.evaluate({
893
- expression: `(function(){
894
- var r=document.documentElement;
895
- if(r.__reactFiber||r._reactRootContainer) return 'react';
896
- if(r.__vue__||document.querySelector('[data-v-]')) return 'vue';
897
- if(r.ng||document.querySelector('[ng-version]')) return 'angular';
898
- return '';
893
+ expression: `(function(){
894
+ var r=document.documentElement;
895
+ if(r.__reactFiber||r._reactRootContainer) return 'react';
896
+ if(r.__vue__||document.querySelector('[data-v-]')) return 'vue';
897
+ if(r.ng||document.querySelector('[ng-version]')) return 'angular';
898
+ return '';
899
899
  })()`,
900
900
  returnByValue: true,
901
901
  }).catch(() => null);
@@ -907,42 +907,42 @@ export async function weaveType(session, args, config) {
907
907
  if (isSpa) {
908
908
  const { omniEvaluate } = await import("../cdp/omni_broker.js");
909
909
  // Single Runtime.evaluate — nativeInputValueSetter + InputEvent dispatch
910
- const rawRes = await omniEvaluate(session, targetId, `(function(sel, value) {
911
- try {
912
- var el = document.querySelector(sel) ||
913
- (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
914
- if (!el) return JSON.stringify({ ok: false, strategy: 'NOT_FOUND', exists: false });
915
-
916
- // Strategy A: React's internal nativeInputValueSetter override
917
- var nativeSetter =
918
- Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set ||
919
- Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set;
920
-
921
- if (nativeSetter) {
922
- nativeSetter.call(el, value);
923
- el.dispatchEvent(new Event('input', { bubbles: true }));
924
- el.dispatchEvent(new Event('change', { bubbles: true }));
925
- el.dispatchEvent(new InputEvent('input', {
926
- bubbles: true, data: value, inputType: 'insertText'
927
- }));
928
- return JSON.stringify({ ok: el.value === value, strategy: 'native-setter', value: el.value, exists: true });
929
- }
930
-
931
- // Strategy B: contenteditable with InputEvent
932
- if (el.isContentEditable) {
933
- el.textContent = value;
934
- el.dispatchEvent(new InputEvent('input', { bubbles: true, data: value }));
935
- return JSON.stringify({ ok: el.textContent === value, strategy: 'contenteditable', value: el.textContent, exists: true });
936
- }
937
-
938
- // Strategy C: direct value + dispatchEvent fallback
939
- el.value = value;
940
- el.dispatchEvent(new Event('input', { bubbles: true }));
941
- el.dispatchEvent(new Event('change', { bubbles: true }));
942
- return JSON.stringify({ ok: el.value === value, strategy: 'direct-event', value: el.value, exists: true });
943
- } catch(e) {
944
- return JSON.stringify({ ok: false, strategy: 'ERROR', error: e.message, exists: false });
945
- }
910
+ const rawRes = await omniEvaluate(session, targetId, `(function(sel, value) {
911
+ try {
912
+ var el = document.querySelector(sel) ||
913
+ (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
914
+ if (!el) return JSON.stringify({ ok: false, strategy: 'NOT_FOUND', exists: false });
915
+
916
+ // Strategy A: React's internal nativeInputValueSetter override
917
+ var nativeSetter =
918
+ Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set ||
919
+ Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set;
920
+
921
+ if (nativeSetter) {
922
+ nativeSetter.call(el, value);
923
+ el.dispatchEvent(new Event('input', { bubbles: true }));
924
+ el.dispatchEvent(new Event('change', { bubbles: true }));
925
+ el.dispatchEvent(new InputEvent('input', {
926
+ bubbles: true, data: value, inputType: 'insertText'
927
+ }));
928
+ return JSON.stringify({ ok: el.value === value, strategy: 'native-setter', value: el.value, exists: true });
929
+ }
930
+
931
+ // Strategy B: contenteditable with InputEvent
932
+ if (el.isContentEditable) {
933
+ el.textContent = value;
934
+ el.dispatchEvent(new InputEvent('input', { bubbles: true, data: value }));
935
+ return JSON.stringify({ ok: el.textContent === value, strategy: 'contenteditable', value: el.textContent, exists: true });
936
+ }
937
+
938
+ // Strategy C: direct value + dispatchEvent fallback
939
+ el.value = value;
940
+ el.dispatchEvent(new Event('input', { bubbles: true }));
941
+ el.dispatchEvent(new Event('change', { bubbles: true }));
942
+ return JSON.stringify({ ok: el.value === value, strategy: 'direct-event', value: el.value, exists: true });
943
+ } catch(e) {
944
+ return JSON.stringify({ ok: false, strategy: 'ERROR', error: e.message, exists: false });
945
+ }
946
946
  })(${JSON.stringify(selector)}, ${JSON.stringify(text)})`);
947
947
  if (rawRes) {
948
948
  try {
@@ -1019,18 +1019,18 @@ export async function weaveType(session, args, config) {
1019
1019
  if (selector) {
1020
1020
  try {
1021
1021
  const { result: stateResult } = await session.Runtime.evaluate({
1022
- expression: `(function(sel) {
1023
- var el = document.querySelector(sel);
1024
- if (!el) return null;
1025
- var isSecret = el.hasAttribute('data-wt-secret') || (el.tagName === 'INPUT' && el.type === 'password');
1026
- return {
1027
- tag: el.tagName.toLowerCase(),
1028
- visible: el.offsetParent !== null,
1029
- disabled: el.disabled,
1030
- value: isSecret ? '[REDACTED]' : el.value,
1031
- label: el.getAttribute('aria-label') || el.getAttribute('placeholder') || el.getAttribute('name') || (el.textContent||'').trim().slice(0,40),
1032
- textContent: isSecret ? '[REDACTED]' : (el.isContentEditable ? el.textContent?.slice(0, 100) : undefined)
1033
- };
1022
+ expression: `(function(sel) {
1023
+ var el = document.querySelector(sel);
1024
+ if (!el) return null;
1025
+ var isSecret = el.hasAttribute('data-wt-secret') || (el.tagName === 'INPUT' && el.type === 'password');
1026
+ return {
1027
+ tag: el.tagName.toLowerCase(),
1028
+ visible: el.offsetParent !== null,
1029
+ disabled: el.disabled,
1030
+ value: isSecret ? '[REDACTED]' : el.value,
1031
+ label: el.getAttribute('aria-label') || el.getAttribute('placeholder') || el.getAttribute('name') || (el.textContent||'').trim().slice(0,40),
1032
+ textContent: isSecret ? '[REDACTED]' : (el.isContentEditable ? el.textContent?.slice(0, 100) : undefined)
1033
+ };
1034
1034
  })(${JSON.stringify(selector)})`,
1035
1035
  returnByValue: true,
1036
1036
  });
@@ -1060,13 +1060,13 @@ export async function weaveType(session, args, config) {
1060
1060
  if (!isPassword && selector && selector !== 'document.activeElement') {
1061
1061
  try {
1062
1062
  const { result: vlRes } = await session.Runtime.evaluate({
1063
- expression: `(function(sel) {
1064
- var el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
1065
- if (!el) return -1;
1066
- var isSec = el.hasAttribute('data-wt-secret') || (el.tagName === 'INPUT' && el.type === 'password');
1067
- if (isSec) return -1;
1068
- var v = el.value !== undefined ? el.value : (el.textContent || '');
1069
- return (v || '').length;
1063
+ expression: `(function(sel) {
1064
+ var el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
1065
+ if (!el) return -1;
1066
+ var isSec = el.hasAttribute('data-wt-secret') || (el.tagName === 'INPUT' && el.type === 'password');
1067
+ if (isSec) return -1;
1068
+ var v = el.value !== undefined ? el.value : (el.textContent || '');
1069
+ return (v || '').length;
1070
1070
  })(${JSON.stringify(selector)})`,
1071
1071
  returnByValue: true,
1072
1072
  });