n8n-nodes-nvk-browser 1.0.4 → 1.0.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.
@@ -22,7 +22,7 @@ exports.moveAndClickFields = [
22
22
  type: 'string',
23
23
  required: true,
24
24
  default: '',
25
- description: 'For Puppeteer: CSS selector, XPath (with >XPATH> prefix), or Locator.race() code block. Examples: "textarea", ">XPATH>/html/body/div", or paste the Locator.race() block (e.g., "puppeteer.Locator.race([targetPage.locator(\'selector\')]).click()"). The node will automatically extract locators from the first .click() block. For Javascript: CSS selector only.',
25
+ description: 'For Puppeteer: CSS selector, XPath (with >XPATH> prefix), or paste the full Puppeteer code from Chrome Recorder (export as "Puppeteer"). The node will automatically extract the first Locator.race().click() block. Examples: "textarea", ">XPATH>/html/body/div", or full code with puppeteer.Locator.race([...]).click(). For Javascript: CSS selector only.',
26
26
  typeOptions: {
27
27
  rows: 4,
28
28
  },
@@ -147,19 +147,16 @@ class MoveAndClick {
147
147
  // Parse selectors from the code pattern
148
148
  const locators = [];
149
149
  // Find Locator.race blocks - prioritize blocks that end with .click()
150
- // First, try to find blocks with .click()
151
- const clickBlocks = selector.match(/puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)[\s\S]*?\.click\s*\(/g);
150
+ // Improved regex to handle multiline code from Chrome Recorder
151
+ // Match pattern: puppeteer.Locator.race([...]).setTimeout(...).click(...)
152
+ const clickBlockPattern = /puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)[\s\S]*?\.click\s*\(/;
153
+ const clickBlockMatch = selector.match(clickBlockPattern);
152
154
  let raceBlockContent = null;
153
- if (clickBlocks && clickBlocks.length > 0) {
154
- // Use the first click block
155
- const firstClickBlock = clickBlocks[0];
156
- const match = firstClickBlock.match(/puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)/);
157
- if (match && match[1]) {
158
- raceBlockContent = match[1];
159
- }
155
+ if (clickBlockMatch && clickBlockMatch[1]) {
156
+ raceBlockContent = clickBlockMatch[1];
160
157
  }
161
158
  else {
162
- // Fallback: find any Locator.race block
159
+ // Fallback: find any Locator.race block (even without .click())
163
160
  const anyRaceBlockMatch = selector.match(/puppeteer\.Locator\.race\s*\(\s*\[([\s\S]*?)\]\s*\)/);
164
161
  if (anyRaceBlockMatch && anyRaceBlockMatch[1]) {
165
162
  raceBlockContent = anyRaceBlockMatch[1];
@@ -168,18 +165,14 @@ class MoveAndClick {
168
165
  if (raceBlockContent) {
169
166
  // Extract all locator calls from the race block
170
167
  // Handle both single and double quotes, and handle escaped quotes
171
- const locatorMatches = raceBlockContent.match(/(?:targetPage|page)\.locator\(['"]([^'"]*(?:\\.[^'"]*)*)['"]\)/g);
172
- if (locatorMatches) {
173
- locatorMatches.forEach(match => {
174
- // Extract selector value, handling escaped quotes
175
- const selectorMatch = match.match(/['"]([^'"]*(?:\\.[^'"]*)*)['"]/);
176
- if (selectorMatch && selectorMatch[1]) {
177
- const selectorValue = selectorMatch[1].replace(/\\(.)/g, '$1'); // Unescape
178
- if (selectorValue && typeof page.locator === 'function') {
179
- locators.push(page.locator(selectorValue));
180
- }
181
- }
182
- });
168
+ // Improved regex to handle complex selectors with parentheses and special characters
169
+ const locatorPattern = /(?:targetPage|page)\.locator\((['"])((?:(?!\1)[^\\]|\\.)*)\1\)/g;
170
+ let locatorMatch;
171
+ while ((locatorMatch = locatorPattern.exec(raceBlockContent)) !== null) {
172
+ const selectorValue = locatorMatch[2].replace(/\\(.)/g, '$1'); // Unescape
173
+ if (selectorValue && typeof page.locator === 'function') {
174
+ locators.push(page.locator(selectorValue));
175
+ }
183
176
  }
184
177
  }
185
178
  if (locators.length === 0) {
@@ -251,10 +244,11 @@ class MoveAndClick {
251
244
  if (waitForClick > 0) {
252
245
  await page.waitForTimeout(waitForClick);
253
246
  }
254
- // Parse offset from code if present
247
+ // Parse offset from code if present (handle multiline)
255
248
  let offset;
256
- const offsetMatch = selector.match(/offset:\s*\{\s*x:\s*(\d+),\s*y:\s*(\d+)\s*\}/);
257
- if (offsetMatch) {
249
+ // Match offset in click options, handling multiline and whitespace
250
+ const offsetMatch = selector.match(/\.click\s*\(\s*\{[\s\S]*?offset:\s*\{[\s\S]*?x:\s*(\d+)[\s\S]*?y:\s*(\d+)[\s\S]*?\}[\s\S]*?\}/);
251
+ if (offsetMatch && offsetMatch[1] && offsetMatch[2]) {
258
252
  offset = {
259
253
  x: parseInt(offsetMatch[1], 10),
260
254
  y: parseInt(offsetMatch[2], 10),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-nvk-browser",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "n8n nodes for managing Chrome browser profiles and page interactions",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",