n8n-nodes-nvk-call-api 0.0.5 → 0.0.7

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.
@@ -29,10 +29,28 @@ exports.nvkBrowserApiDescription = {
29
29
  name: 'Docker',
30
30
  value: 'docker',
31
31
  },
32
+ {
33
+ name: 'Tunnel (Custom URL)',
34
+ value: 'tunnel',
35
+ },
32
36
  ],
33
37
  default: 'local',
34
38
  description: 'Choose the environment to connect to',
35
39
  },
40
+ {
41
+ displayName: 'Tunnel URL',
42
+ name: 'tunnelUrl',
43
+ type: 'string',
44
+ displayOptions: {
45
+ show: {
46
+ environment: ['tunnel'],
47
+ },
48
+ },
49
+ default: '',
50
+ placeholder: 'https://abc123.ngrok.io',
51
+ description: 'Enter your tunnel URL (ngrok, Cloudflare Tunnel, etc.)',
52
+ required: true,
53
+ },
36
54
  {
37
55
  displayName: 'Resource',
38
56
  name: 'resource',
@@ -47,6 +65,10 @@ exports.nvkBrowserApiDescription = {
47
65
  name: 'Page Interaction',
48
66
  value: 'pageInteraction',
49
67
  },
68
+ {
69
+ name: 'Browser Automation',
70
+ value: 'browserAutomation',
71
+ },
50
72
  ],
51
73
  default: 'profileManagement',
52
74
  },
@@ -125,9 +147,166 @@ exports.nvkBrowserApiDescription = {
125
147
  value: 'browserHttpRequest',
126
148
  action: 'Make HTTP requests using Browser',
127
149
  },
150
+ {
151
+ name: 'Get Recaptcha Token',
152
+ value: 'getRecaptchaToken',
153
+ action: 'Get Recaptcha Token',
154
+ },
128
155
  ],
129
156
  default: 'moveAndClick',
130
157
  },
158
+ {
159
+ displayName: 'Operation',
160
+ name: 'operation',
161
+ type: 'options',
162
+ noDataExpression: true,
163
+ displayOptions: {
164
+ show: {
165
+ resource: ['browserAutomation'],
166
+ },
167
+ },
168
+ options: [
169
+ {
170
+ name: 'Run Multiple Profiles',
171
+ value: 'runMultipleProfiles',
172
+ action: 'Run Multiple Profiles',
173
+ },
174
+ ],
175
+ default: 'runMultipleProfiles',
176
+ },
177
+ // Run Multiple Profiles Fields
178
+ {
179
+ displayName: 'Data',
180
+ name: 'data',
181
+ type: 'string',
182
+ typeOptions: {
183
+ rows: 10,
184
+ },
185
+ displayOptions: {
186
+ show: {
187
+ resource: ['browserAutomation'],
188
+ operation: ['runMultipleProfiles'],
189
+ },
190
+ },
191
+ default: '',
192
+ required: true,
193
+ description: 'Enter data in format: line|profileId (one per line)',
194
+ placeholder: '1|212e2d01-0c8a-4373-9c9f-9153354de0a4\n2|212e2d01-0c8a-4373-9c9f-9153354de0a4',
195
+ },
196
+ {
197
+ displayName: 'Number of Threads',
198
+ name: 'numberOfThreads',
199
+ type: 'number',
200
+ displayOptions: {
201
+ show: {
202
+ resource: ['browserAutomation'],
203
+ operation: ['runMultipleProfiles'],
204
+ },
205
+ },
206
+ default: 1,
207
+ description: 'Number of parallel threads to run',
208
+ },
209
+ {
210
+ displayName: 'Define Columns',
211
+ name: 'defineColumns',
212
+ type: 'number',
213
+ displayOptions: {
214
+ show: {
215
+ resource: ['browserAutomation'],
216
+ operation: ['runMultipleProfiles'],
217
+ },
218
+ },
219
+ default: 3,
220
+ description: 'Number of columns in grid layout',
221
+ },
222
+ {
223
+ displayName: 'Define Rows',
224
+ name: 'defineRows',
225
+ type: 'number',
226
+ displayOptions: {
227
+ show: {
228
+ resource: ['browserAutomation'],
229
+ operation: ['runMultipleProfiles'],
230
+ },
231
+ },
232
+ default: 2,
233
+ description: 'Number of rows in grid layout',
234
+ },
235
+ {
236
+ displayName: 'Win Scale',
237
+ name: 'winScale',
238
+ type: 'number',
239
+ displayOptions: {
240
+ show: {
241
+ resource: ['browserAutomation'],
242
+ operation: ['runMultipleProfiles'],
243
+ },
244
+ },
245
+ default: 100,
246
+ description: 'Window scale percentage (100 = 100%)',
247
+ },
248
+ {
249
+ displayName: 'Webhook URL',
250
+ name: 'webhookUrl',
251
+ type: 'string',
252
+ displayOptions: {
253
+ show: {
254
+ resource: ['browserAutomation'],
255
+ operation: ['runMultipleProfiles'],
256
+ },
257
+ },
258
+ default: '',
259
+ description: 'Webhook URL to send results to (optional)',
260
+ },
261
+ {
262
+ displayName: 'Webhook Method',
263
+ name: 'webhookMethod',
264
+ type: 'options',
265
+ displayOptions: {
266
+ show: {
267
+ resource: ['browserAutomation'],
268
+ operation: ['runMultipleProfiles'],
269
+ },
270
+ },
271
+ options: [
272
+ {
273
+ name: 'GET',
274
+ value: 'GET',
275
+ },
276
+ {
277
+ name: 'POST',
278
+ value: 'POST',
279
+ },
280
+ ],
281
+ default: 'POST',
282
+ description: 'HTTP method to use for webhook call',
283
+ },
284
+ {
285
+ displayName: 'Timeout (Milliseconds)',
286
+ name: 'timeout',
287
+ type: 'number',
288
+ displayOptions: {
289
+ show: {
290
+ resource: ['browserAutomation'],
291
+ operation: ['runMultipleProfiles'],
292
+ },
293
+ },
294
+ default: 100000000,
295
+ description: 'Maximum timeout in milliseconds',
296
+ },
297
+ {
298
+ displayName: 'Wait For The Previous Session To Complete',
299
+ name: 'waitForPrevious',
300
+ type: 'boolean',
301
+ displayOptions: {
302
+ show: {
303
+ resource: ['browserAutomation'],
304
+ operation: ['runMultipleProfiles'],
305
+ },
306
+ },
307
+ default: true,
308
+ description: 'Whether to wait for previous session to complete before starting next',
309
+ },
131
310
  // Create Profile Fields
132
311
  {
133
312
  displayName: 'Profile Name',
@@ -378,7 +557,7 @@ exports.nvkBrowserApiDescription = {
378
557
  operation: ['updateProfile'],
379
558
  },
380
559
  },
381
- default: 'http:127.0.0.1:8080:user:pass hoặc socks5:127.0.0.1:1080:user:pass',
560
+ default: '',
382
561
  description: 'New proxy configuration',
383
562
  },
384
563
  {
@@ -392,7 +571,7 @@ exports.nvkBrowserApiDescription = {
392
571
  },
393
572
  },
394
573
  default: '',
395
- description: 'Updated notes',
574
+ description: 'New note',
396
575
  },
397
576
  {
398
577
  displayName: 'Extensions',
@@ -408,7 +587,7 @@ exports.nvkBrowserApiDescription = {
408
587
  },
409
588
  },
410
589
  default: '',
411
- description: 'Updated extensions',
590
+ description: 'New extensions (one per line)',
412
591
  },
413
592
  // Move and Click Fields
414
593
  {
@@ -426,12 +605,9 @@ exports.nvkBrowserApiDescription = {
426
605
  description: 'ID of the profile',
427
606
  },
428
607
  {
429
- displayName: 'Selector or Puppeteer Locator Code',
608
+ displayName: 'Selector',
430
609
  name: 'selector',
431
610
  type: 'string',
432
- typeOptions: {
433
- rows: 4,
434
- },
435
611
  displayOptions: {
436
612
  show: {
437
613
  resource: ['pageInteraction'],
@@ -440,7 +616,7 @@ exports.nvkBrowserApiDescription = {
440
616
  },
441
617
  default: '',
442
618
  required: true,
443
- description: 'CSS selector or Puppeteer locator code',
619
+ description: 'CSS selector for the element',
444
620
  },
445
621
  {
446
622
  displayName: 'Click Method',
@@ -454,19 +630,19 @@ exports.nvkBrowserApiDescription = {
454
630
  },
455
631
  options: [
456
632
  {
457
- name: 'Puppeteer Locator',
633
+ name: 'Puppeteer',
458
634
  value: 'puppeteer',
459
635
  },
460
636
  {
461
- name: 'Standard',
462
- value: 'standard',
637
+ name: 'Coordinates',
638
+ value: 'coordinates',
463
639
  },
464
640
  ],
465
641
  default: 'puppeteer',
466
- description: 'Method for clicking',
642
+ description: 'Method to use for clicking',
467
643
  },
468
644
  {
469
- displayName: 'Timeout (Milliseconds)',
645
+ displayName: 'Timeout',
470
646
  name: 'timeout',
471
647
  type: 'number',
472
648
  displayOptions: {
@@ -476,7 +652,7 @@ exports.nvkBrowserApiDescription = {
476
652
  },
477
653
  },
478
654
  default: 30000,
479
- description: 'Maximum wait time',
655
+ description: 'Timeout in milliseconds',
480
656
  },
481
657
  {
482
658
  displayName: 'Tab Index',
@@ -505,7 +681,7 @@ exports.nvkBrowserApiDescription = {
505
681
  description: 'Whether to auto-start profile if not running',
506
682
  },
507
683
  {
508
- displayName: 'Wait Before Click (Milliseconds)',
684
+ displayName: 'Wait For Click',
509
685
  name: 'waitForClick',
510
686
  type: 'number',
511
687
  displayOptions: {
@@ -515,10 +691,10 @@ exports.nvkBrowserApiDescription = {
515
691
  },
516
692
  },
517
693
  default: 500,
518
- description: 'Delay before clicking',
694
+ description: 'Wait time before click in milliseconds',
519
695
  },
520
696
  {
521
- displayName: 'Mouse Button',
697
+ displayName: 'Button',
522
698
  name: 'button',
523
699
  type: 'options',
524
700
  displayOptions: {
@@ -571,7 +747,7 @@ exports.nvkBrowserApiDescription = {
571
747
  description: 'Whether to upload a binary file',
572
748
  },
573
749
  {
574
- displayName: 'Binary Property',
750
+ displayName: 'Input Data Field Name',
575
751
  name: 'binaryPropertyName',
576
752
  type: 'string',
577
753
  displayOptions: {
@@ -583,7 +759,7 @@ exports.nvkBrowserApiDescription = {
583
759
  },
584
760
  default: 'data',
585
761
  required: true,
586
- description: 'Name of the binary property containing the file',
762
+ description: 'Name of the binary property',
587
763
  },
588
764
  // Run JavaScript Fields
589
765
  {
@@ -659,7 +835,7 @@ exports.nvkBrowserApiDescription = {
659
835
  description: 'ID of the profile',
660
836
  },
661
837
  {
662
- displayName: 'Request Name or URL',
838
+ displayName: 'Request Filter',
663
839
  name: 'requestFilter',
664
840
  type: 'string',
665
841
  displayOptions: {
@@ -691,24 +867,16 @@ exports.nvkBrowserApiDescription = {
691
867
  name: 'Exact',
692
868
  value: 'exact',
693
869
  },
694
- {
695
- name: 'Starts With',
696
- value: 'startsWith',
697
- },
698
- {
699
- name: 'Ends With',
700
- value: 'endsWith',
701
- },
702
870
  {
703
871
  name: 'Regex',
704
872
  value: 'regex',
705
873
  },
706
874
  ],
707
875
  default: 'contains',
708
- description: 'How to match the request filter',
876
+ description: 'Type of matching to use',
709
877
  },
710
878
  {
711
- displayName: 'Timeout (Milliseconds)',
879
+ displayName: 'Timeout',
712
880
  name: 'timeout',
713
881
  type: 'number',
714
882
  displayOptions: {
@@ -718,7 +886,7 @@ exports.nvkBrowserApiDescription = {
718
886
  },
719
887
  },
720
888
  default: 30000,
721
- description: 'Timeout for waiting',
889
+ description: 'Timeout in milliseconds',
722
890
  },
723
891
  {
724
892
  displayName: 'Wait For Request',
@@ -862,6 +1030,99 @@ exports.nvkBrowserApiDescription = {
862
1030
  default: 'formData',
863
1031
  description: 'Type of body content',
864
1032
  },
1033
+ // Get Recaptcha Token Fields
1034
+ {
1035
+ displayName: 'Profile ID',
1036
+ name: 'profileId',
1037
+ type: 'string',
1038
+ displayOptions: {
1039
+ show: {
1040
+ resource: ['pageInteraction'],
1041
+ operation: ['getRecaptchaToken'],
1042
+ },
1043
+ },
1044
+ default: '',
1045
+ required: true,
1046
+ description: 'ID of the profile',
1047
+ },
1048
+ {
1049
+ displayName: 'Site Key',
1050
+ name: 'siteKey',
1051
+ type: 'string',
1052
+ displayOptions: {
1053
+ show: {
1054
+ resource: ['pageInteraction'],
1055
+ operation: ['getRecaptchaToken'],
1056
+ },
1057
+ },
1058
+ default: '',
1059
+ description: 'reCAPTCHA site key (auto-detect if empty)',
1060
+ },
1061
+ {
1062
+ displayName: 'Tab Index',
1063
+ name: 'tabIndex',
1064
+ type: 'number',
1065
+ displayOptions: {
1066
+ show: {
1067
+ resource: ['pageInteraction'],
1068
+ operation: ['getRecaptchaToken'],
1069
+ },
1070
+ },
1071
+ default: 0,
1072
+ description: 'Browser tab index',
1073
+ },
1074
+ {
1075
+ displayName: 'Timeout',
1076
+ name: 'timeout',
1077
+ type: 'number',
1078
+ displayOptions: {
1079
+ show: {
1080
+ resource: ['pageInteraction'],
1081
+ operation: ['getRecaptchaToken'],
1082
+ },
1083
+ },
1084
+ default: 10000,
1085
+ description: 'Timeout in milliseconds',
1086
+ },
1087
+ {
1088
+ displayName: 'Action',
1089
+ name: 'action',
1090
+ type: 'string',
1091
+ displayOptions: {
1092
+ show: {
1093
+ resource: ['pageInteraction'],
1094
+ operation: ['getRecaptchaToken'],
1095
+ },
1096
+ },
1097
+ default: 'FLOW_GENERATION',
1098
+ description: 'reCAPTCHA action parameter',
1099
+ },
1100
+ {
1101
+ displayName: 'Max Retries',
1102
+ name: 'maxRetries',
1103
+ type: 'number',
1104
+ displayOptions: {
1105
+ show: {
1106
+ resource: ['pageInteraction'],
1107
+ operation: ['getRecaptchaToken'],
1108
+ },
1109
+ },
1110
+ default: 3,
1111
+ description: 'Maximum number of retry attempts',
1112
+ },
1113
+ {
1114
+ displayName: 'Auto Start Profile',
1115
+ name: 'autoStart',
1116
+ type: 'boolean',
1117
+ displayOptions: {
1118
+ show: {
1119
+ resource: ['pageInteraction'],
1120
+ operation: ['getRecaptchaToken'],
1121
+ },
1122
+ },
1123
+ default: false,
1124
+ description: 'Whether to auto-start profile if not running',
1125
+ },
865
1126
  // n8n Binary File - Input Data Field Name
866
1127
  {
867
1128
  displayName: 'Input Data Field Name',
@@ -14,9 +14,26 @@ class NvkBrowserApi {
14
14
  const environment = this.getNodeParameter('environment', i);
15
15
  const resource = this.getNodeParameter('resource', i);
16
16
  const operation = this.getNodeParameter('operation', i);
17
- const baseUrl = environment === 'docker'
18
- ? 'http://host.docker.internal:3000'
19
- : 'http://localhost:3000';
17
+ // Handle Run Multiple Profiles - No API call, process locally in n8n
18
+ if (resource === 'browserAutomation' && operation === 'runMultipleProfiles') {
19
+ const result = await runMultipleProfiles.call(this, i);
20
+ returnData.push({
21
+ json: result,
22
+ pairedItem: { item: i },
23
+ });
24
+ continue;
25
+ }
26
+ // All other operations - Call API as before
27
+ let baseUrl;
28
+ if (environment === 'docker') {
29
+ baseUrl = 'http://host.docker.internal:3000';
30
+ }
31
+ else if (environment === 'tunnel') {
32
+ baseUrl = this.getNodeParameter('tunnelUrl', i).replace(/\/$/, '');
33
+ }
34
+ else {
35
+ baseUrl = 'http://localhost:3000';
36
+ }
20
37
  let endpoint = '';
21
38
  let body = {};
22
39
  if (resource === 'profileManagement') {
@@ -144,6 +161,23 @@ class NvkBrowserApi {
144
161
  };
145
162
  break;
146
163
  }
164
+ case 'getRecaptchaToken': {
165
+ endpoint = '/api/recaptcha/get-token';
166
+ body = {
167
+ profileId: this.getNodeParameter('profileId', i),
168
+ tabIndex: this.getNodeParameter('tabIndex', i, 0),
169
+ timeout: this.getNodeParameter('timeout', i, 10000),
170
+ action: this.getNodeParameter('action', i, 'FLOW_GENERATION'),
171
+ maxRetries: this.getNodeParameter('maxRetries', i, 3),
172
+ autoStart: this.getNodeParameter('autoStart', i, false),
173
+ };
174
+ // Site key là optional
175
+ const siteKey = this.getNodeParameter('siteKey', i, '');
176
+ if (siteKey && siteKey.trim() !== '') {
177
+ body.siteKey = siteKey.trim();
178
+ }
179
+ break;
180
+ }
147
181
  case 'browserHttpRequest': {
148
182
  endpoint = '/api/browser/request';
149
183
  const method = this.getNodeParameter('method', i, 'GET');
@@ -306,3 +340,227 @@ class NvkBrowserApi {
306
340
  }
307
341
  }
308
342
  exports.NvkBrowserApi = NvkBrowserApi;
343
+ /**
344
+ * Run Multiple Profiles - Process locally in n8n without API call
345
+ */
346
+ async function runMultipleProfiles(itemIndex) {
347
+ // Get parameters
348
+ const data = this.getNodeParameter('data', itemIndex, '');
349
+ const numberOfThreads = this.getNodeParameter('numberOfThreads', itemIndex, 1);
350
+ const defineColumns = this.getNodeParameter('defineColumns', itemIndex, 3);
351
+ const defineRows = this.getNodeParameter('defineRows', itemIndex, 2);
352
+ const winScale = this.getNodeParameter('winScale', itemIndex, 100);
353
+ const webhookUrl = this.getNodeParameter('webhookUrl', itemIndex, '');
354
+ const webhookMethod = this.getNodeParameter('webhookMethod', itemIndex, 'POST');
355
+ const timeout = this.getNodeParameter('timeout', itemIndex, 100000000);
356
+ const waitForPrevious = this.getNodeParameter('waitForPrevious', itemIndex, true);
357
+ // Parse data into lines
358
+ const lines = data.trim().split('\n').filter((line) => line.trim() !== '');
359
+ const totalLines = lines.length;
360
+ // Calculate window size and position for grid layout
361
+ const screenWidth = 1920; // Default screen width
362
+ const screenHeight = 1080; // Default screen height
363
+ const windowWidth = Math.floor((screenWidth / defineColumns) * (winScale / 100));
364
+ const windowHeight = Math.floor((screenHeight / defineRows) * (winScale / 100));
365
+ // Prepare tasks
366
+ const tasks = lines.map((line, index) => {
367
+ const parts = line.split('|').map((p) => p.trim());
368
+ // Calculate grid position
369
+ const row = Math.floor(index / defineColumns);
370
+ const col = index % defineColumns;
371
+ const x = col * windowWidth;
372
+ const y = row * windowHeight;
373
+ return {
374
+ line,
375
+ parts,
376
+ threadId: index,
377
+ winPosition: `${x},${y}`,
378
+ winSize: `${windowWidth},${windowHeight}`,
379
+ };
380
+ });
381
+ const results = [];
382
+ let completedLines = 0;
383
+ // Helper function to send individual result to webhook
384
+ const sendToWebhook = async (result, index, total) => {
385
+ if (!webhookUrl || webhookUrl.trim() === '') {
386
+ return;
387
+ }
388
+ const cleanWebhookUrl = webhookUrl.trim();
389
+ try {
390
+ const payload = {
391
+ ...result,
392
+ index: index + 1,
393
+ total,
394
+ timestamp: new Date().toISOString(),
395
+ };
396
+ console.log(`Sending result ${index + 1}/${total} to webhook (${webhookMethod}):`, cleanWebhookUrl);
397
+ if (webhookMethod === 'GET') {
398
+ // For GET, send each field as separate query parameter
399
+ const params = new URLSearchParams();
400
+ // Add basic fields
401
+ params.append('line', result.line);
402
+ params.append('threadId', String(result.threadId));
403
+ params.append('winPosition', result.winPosition);
404
+ params.append('winSize', result.winSize);
405
+ params.append('winScale', String(winScale)); // Add winScale from main params
406
+ params.append('success', String(result.success));
407
+ params.append('index', String(index + 1));
408
+ params.append('total', String(total));
409
+ params.append('timestamp', new Date().toISOString());
410
+ // Add response as JSON string (if exists)
411
+ if (result.response) {
412
+ params.append('response', JSON.stringify(result.response));
413
+ }
414
+ // Add error if exists
415
+ if (result.error) {
416
+ params.append('error', result.error);
417
+ }
418
+ const urlWithParams = `${cleanWebhookUrl}?${params.toString()}`;
419
+ await this.helpers.request({
420
+ method: 'GET',
421
+ url: urlWithParams,
422
+ json: true,
423
+ headers: {
424
+ 'User-Agent': 'n8n-nvk-browser-api',
425
+ },
426
+ timeout: 30000,
427
+ resolveWithFullResponse: false,
428
+ });
429
+ }
430
+ else {
431
+ // For POST, send full payload in body
432
+ await this.helpers.request({
433
+ method: 'POST',
434
+ url: cleanWebhookUrl,
435
+ body: payload,
436
+ json: true,
437
+ headers: {
438
+ 'Content-Type': 'application/json',
439
+ 'User-Agent': 'n8n-nvk-browser-api',
440
+ },
441
+ timeout: 30000,
442
+ resolveWithFullResponse: false,
443
+ });
444
+ }
445
+ console.log(`✓ Result ${index + 1}/${total} sent successfully`);
446
+ }
447
+ catch (error) {
448
+ const errorMessage = error instanceof Error ? error.message : String(error);
449
+ console.error(`✗ Failed to send result ${index + 1}/${total}:`, errorMessage);
450
+ }
451
+ };
452
+ // Process tasks
453
+ if (waitForPrevious) {
454
+ // Sequential processing - wait for each to complete
455
+ for (let taskIndex = 0; taskIndex < tasks.length; taskIndex++) {
456
+ const task = tasks[taskIndex];
457
+ try {
458
+ const result = await processProfileTask(task);
459
+ results.push(result);
460
+ completedLines++;
461
+ // Send to webhook immediately after processing
462
+ await sendToWebhook(result, taskIndex, tasks.length);
463
+ }
464
+ catch (error) {
465
+ const errorResult = {
466
+ line: task.line,
467
+ threadId: task.threadId,
468
+ winPosition: task.winPosition,
469
+ winSize: task.winSize,
470
+ success: false,
471
+ error: error instanceof Error ? error.message : String(error),
472
+ };
473
+ results.push(errorResult);
474
+ completedLines++;
475
+ // Send error result to webhook too
476
+ await sendToWebhook(errorResult, taskIndex, tasks.length);
477
+ }
478
+ }
479
+ }
480
+ else {
481
+ // Parallel processing with thread limit
482
+ const processInBatches = async (tasks, batchSize) => {
483
+ for (let i = 0; i < tasks.length; i += batchSize) {
484
+ const batch = tasks.slice(i, i + batchSize);
485
+ const batchPromises = batch.map(async (task, batchIndex) => {
486
+ const taskIndex = i + batchIndex;
487
+ try {
488
+ const result = await processProfileTask(task);
489
+ results.push(result);
490
+ completedLines++;
491
+ // Send to webhook immediately after processing
492
+ await sendToWebhook(result, taskIndex, tasks.length);
493
+ return result;
494
+ }
495
+ catch (error) {
496
+ const errorResult = {
497
+ line: task.line,
498
+ threadId: task.threadId,
499
+ winPosition: task.winPosition,
500
+ winSize: task.winSize,
501
+ success: false,
502
+ error: error instanceof Error ? error.message : String(error),
503
+ };
504
+ results.push(errorResult);
505
+ completedLines++;
506
+ // Send error result to webhook too
507
+ await sendToWebhook(errorResult, taskIndex, tasks.length);
508
+ return errorResult;
509
+ }
510
+ });
511
+ await Promise.all(batchPromises);
512
+ }
513
+ };
514
+ await processInBatches(tasks, numberOfThreads);
515
+ }
516
+ // Prepare final response
517
+ const finalResponse = {
518
+ totalLines,
519
+ completedLines,
520
+ results,
521
+ timeout,
522
+ defineColumn: defineColumns,
523
+ defineRow: defineRows,
524
+ message: `All ${completedLines} threads completed with grid ${defineColumns}x${defineRows}, maximum timeout ${timeout}ms`,
525
+ };
526
+ // Add webhook summary if webhook was used
527
+ if (webhookUrl && webhookUrl.trim() !== '') {
528
+ finalResponse.webhookSummary = {
529
+ enabled: true,
530
+ url: webhookUrl.trim(),
531
+ method: webhookMethod,
532
+ totalSent: completedLines,
533
+ message: `Sent ${completedLines} individual results to webhook`,
534
+ };
535
+ }
536
+ else {
537
+ finalResponse.webhookSummary = {
538
+ enabled: false,
539
+ message: 'No webhook URL provided',
540
+ };
541
+ }
542
+ return finalResponse;
543
+ }
544
+ /**
545
+ * Process a single profile task
546
+ */
547
+ async function processProfileTask(task) {
548
+ // Simulate processing - parse the data
549
+ const responseData = [];
550
+ // Parse each part from the line (separated by |)
551
+ task.parts.forEach((part, index) => {
552
+ const obj = {};
553
+ obj[`part${index + 1}`] = part;
554
+ responseData.push(obj);
555
+ });
556
+ // Simulate some delay (optional)
557
+ await new Promise((resolve) => setTimeout(resolve, 100));
558
+ return {
559
+ line: task.line,
560
+ threadId: task.threadId,
561
+ winPosition: task.winPosition,
562
+ winSize: task.winSize,
563
+ success: true,
564
+ response: responseData,
565
+ };
566
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-nvk-call-api",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "n8n node for NVK Browser automation API",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",