@ceki/n8n-nodes-ceki 0.2.30 → 0.2.31

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/dist/index.js CHANGED
@@ -151,18 +151,18 @@ var CekiClient = class {
151
151
  return;
152
152
  }
153
153
  await new Promise((r) => {
154
- const t = setTimeout(() => {
155
- r();
156
- }, 5e3);
154
+ const onAbort = () => r();
155
+ const abortTimer = AbortSignal.timeout(5e3);
156
+ abortTimer.addEventListener("abort", onAbort, { once: true });
157
157
  ws.onclose = () => {
158
158
  this._connected = false;
159
- clearTimeout(t);
159
+ abortTimer.removeEventListener("abort", onAbort);
160
160
  r();
161
161
  };
162
162
  try {
163
163
  ws.close();
164
164
  } catch {
165
- clearTimeout(t);
165
+ abortTimer.removeEventListener("abort", onAbort);
166
166
  r();
167
167
  }
168
168
  });
@@ -591,7 +591,7 @@ var ContractClient = class {
591
591
  };
592
592
 
593
593
  // nodes/BrowserCeki/BrowserCeki.node.ts
594
- async function waitForSelector(browser, selector, timeoutMs, intervalMs = 500) {
594
+ async function waitForSelector(node, browser, selector, timeoutMs, intervalMs = 500) {
595
595
  const expr = `!!document.querySelector(${JSON.stringify(selector)})`;
596
596
  const deadline = Date.now() + timeoutMs;
597
597
  let lastErr = null;
@@ -604,7 +604,8 @@ async function waitForSelector(browser, selector, timeoutMs, intervalMs = 500) {
604
604
  }
605
605
  await sleep(intervalMs);
606
606
  }
607
- throw new Error(
607
+ throw new import_n8n_workflow.NodeOperationError(
608
+ node,
608
609
  `waitForSelector("${selector}") timed out after ${timeoutMs}ms${lastErr ? ": " + lastErr.message : ""}`
609
610
  );
610
611
  }
@@ -646,6 +647,18 @@ var BrowserCeki = class {
646
647
  { name: "Captcha-Protected Scrape", value: "captchaScrape" },
647
648
  { name: "Click", value: "click" },
648
649
  { name: "Close", value: "close" },
650
+ // Contract operations
651
+ { name: "Contract: Assign Executor", value: "contract_assign" },
652
+ { name: "Contract: Call Human", value: "contract_callHuman" },
653
+ { name: "Contract: Comment", value: "contract_comment" },
654
+ { name: "Contract: Create Task", value: "contract_create" },
655
+ { name: "Contract: Get Task", value: "contract_get" },
656
+ { name: "Contract: List My Contracts", value: "contract_list" },
657
+ { name: "Contract: List Tasks in Contract", value: "contract_tasks" },
658
+ { name: "Contract: My Assigned Events", value: "contract_myEvents" },
659
+ { name: "Contract: Poll Notifications", value: "contract_poll" },
660
+ { name: "Contract: Progress Report", value: "contract_progress" },
661
+ { name: "Contract: Update Status", value: "contract_setStatus" },
649
662
  { name: "Full: Rent \u2192 Navigate \u2192 Screenshot", value: "full" },
650
663
  { name: "My Sessions", value: "my_sessions" },
651
664
  { name: "Navigate", value: "navigate" },
@@ -659,19 +672,7 @@ var BrowserCeki = class {
659
672
  { name: "Type", value: "type" },
660
673
  { name: "Upload", value: "upload" },
661
674
  { name: "Wait", value: "wait" },
662
- { name: "Wait for Selector", value: "waitForSelector" },
663
- // Contract operations
664
- { name: "Contract: Assign Executor", value: "contract_assign" },
665
- { name: "Contract: Call Human", value: "contract_callHuman" },
666
- { name: "Contract: Comment", value: "contract_comment" },
667
- { name: "Contract: Create Task", value: "contract_create" },
668
- { name: "Contract: Get Task", value: "contract_get" },
669
- { name: "Contract: List My Contracts", value: "contract_list" },
670
- { name: "Contract: List Tasks in Contract", value: "contract_tasks" },
671
- { name: "Contract: My Assigned Events", value: "contract_myEvents" },
672
- { name: "Contract: Poll Notifications", value: "contract_poll" },
673
- { name: "Contract: Progress Report", value: "contract_progress" },
674
- { name: "Contract: Update Status", value: "contract_setStatus" }
675
+ { name: "Wait for Selector", value: "waitForSelector" }
675
676
  ]
676
677
  },
677
678
  // === Rent: rental parameters ===
@@ -692,7 +693,7 @@ var BrowserCeki = class {
692
693
  displayOptions: { show: { operation: ["search", "captchaScrape", "screenshotGeo"] } }
693
694
  },
694
695
  {
695
- displayName: "Max $/min",
696
+ displayName: "Max $/Min",
696
697
  name: "maxPrice",
697
698
  type: "number",
698
699
  typeOptions: { numberPrecision: 4 },
@@ -712,8 +713,8 @@ var BrowserCeki = class {
712
713
  type: "options",
713
714
  default: "incognito",
714
715
  options: [
715
- { name: "main", value: "main" },
716
- { name: "incognito", value: "incognito" }
716
+ { name: "Incognito", value: "incognito" },
717
+ { name: "Main", value: "main" }
717
718
  ],
718
719
  displayOptions: { show: { operation: ["rent", "full"] } }
719
720
  },
@@ -752,7 +753,7 @@ var BrowserCeki = class {
752
753
  displayOptions: { show: { operation: ["captchaScrape"] } }
753
754
  },
754
755
  {
755
- displayName: "Max $/min",
756
+ displayName: "Max $/Min",
756
757
  name: "maxPrice",
757
758
  type: "number",
758
759
  typeOptions: { numberPrecision: 4 },
@@ -769,7 +770,7 @@ var BrowserCeki = class {
769
770
  displayOptions: { show: { operation: ["captchaScrape"] } }
770
771
  },
771
772
  {
772
- displayName: "Wait Timeout (ms)",
773
+ displayName: "Wait Timeout (Ms)",
773
774
  name: "waitTimeout",
774
775
  type: "number",
775
776
  default: 3e4,
@@ -823,7 +824,7 @@ var BrowserCeki = class {
823
824
  displayOptions: { show: { operation: ["screenshotGeo"] } }
824
825
  },
825
826
  {
826
- displayName: "Max $/min",
827
+ displayName: "Max $/Min",
827
828
  name: "maxPrice",
828
829
  type: "number",
829
830
  default: 0.02,
@@ -987,13 +988,13 @@ var BrowserCeki = class {
987
988
  type: "options",
988
989
  default: "png",
989
990
  options: [
990
- { name: "PNG (binary)", value: "png" },
991
- { name: "Base64", value: "base64" }
991
+ { name: "Base64", value: "base64" },
992
+ { name: "PNG (Binary)", value: "png" }
992
993
  ],
993
994
  displayOptions: { show: { operation: ["screenshot"] } }
994
995
  },
995
996
  {
996
- displayName: "Full page",
997
+ displayName: "Full Page",
997
998
  name: "fullPage",
998
999
  type: "boolean",
999
1000
  default: false,
@@ -1018,7 +1019,7 @@ var BrowserCeki = class {
1018
1019
  displayOptions: { show: { operation: ["waitForSelector"] } }
1019
1020
  },
1020
1021
  {
1021
- displayName: "Timeout (ms)",
1022
+ displayName: "Timeout (Ms)",
1022
1023
  name: "waitTimeout",
1023
1024
  type: "number",
1024
1025
  default: 3e4,
@@ -1048,6 +1049,7 @@ var BrowserCeki = class {
1048
1049
  const out = [];
1049
1050
  const creds = await this.getCredentials("cekiApi");
1050
1051
  const token = creds.token;
1052
+ const node = this.getNode();
1051
1053
  const touchedSessions = /* @__PURE__ */ new Set();
1052
1054
  const _stopSession = async (sid) => {
1053
1055
  await new Promise((resolve) => {
@@ -1090,12 +1092,11 @@ var BrowserCeki = class {
1090
1092
  geo: geo || void 0,
1091
1093
  max_price_per_min: maxPrice
1092
1094
  });
1093
- if (!list.length) throw new import_n8n_workflow.NodeOperationError(this.getNode(), "No browsers found by filters");
1095
+ if (!list.length) throw new import_n8n_workflow.NodeOperationError(node, "No browsers found by filters");
1094
1096
  return list[0].schedule_id;
1095
1097
  };
1096
- let _execErr = null;
1097
- try {
1098
- for (let i = 0; i < items.length; i++) {
1098
+ for (let i = 0; i < items.length; i++) {
1099
+ try {
1099
1100
  const op = this.getNodeParameter("operation", i);
1100
1101
  const client = new CekiClient(token);
1101
1102
  let browser;
@@ -1112,7 +1113,7 @@ var BrowserCeki = class {
1112
1113
  pairedItem: { item: i }
1113
1114
  });
1114
1115
  } catch (e) {
1115
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Rent failed" });
1116
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Rent failed" });
1116
1117
  } finally {
1117
1118
  await client.disconnect();
1118
1119
  }
@@ -1128,7 +1129,7 @@ var BrowserCeki = class {
1128
1129
  });
1129
1130
  out.push({ json: { browsers: list, count: list.length }, pairedItem: { item: i } });
1130
1131
  } catch (e) {
1131
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Search failed" });
1132
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Search failed" });
1132
1133
  } finally {
1133
1134
  await client.disconnect();
1134
1135
  }
@@ -1139,12 +1140,12 @@ var BrowserCeki = class {
1139
1140
  const resp = await fetch("https://api.ceki.me/api/agent/sessions", {
1140
1141
  headers: { Authorization: `Bearer ${token}` }
1141
1142
  });
1142
- if (!resp.ok) throw new import_n8n_workflow.NodeApiError(this.getNode(), void 0, { message: `HTTP ${resp.status}`, httpCode: `${resp.status}` });
1143
+ if (!resp.ok) throw new import_n8n_workflow.NodeApiError(node, void 0, { itemIndex: i, message: `HTTP ${resp.status}`, httpCode: `${resp.status}` });
1143
1144
  const body = await resp.json();
1144
1145
  const sessions = body.data ?? [];
1145
1146
  out.push({ json: { sessions, count: sessions.length }, pairedItem: { item: i } });
1146
1147
  } catch (e) {
1147
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "My sessions failed" });
1148
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "My sessions failed" });
1148
1149
  }
1149
1150
  continue;
1150
1151
  }
@@ -1159,7 +1160,7 @@ var BrowserCeki = class {
1159
1160
  ws.close();
1160
1161
  } catch {
1161
1162
  }
1162
- reject(new import_n8n_workflow.NodeApiError(this.getNode(), void 0, { message: "Stop session timed out" }));
1163
+ reject(new import_n8n_workflow.NodeApiError(node, void 0, { itemIndex: i, message: "Stop session timed out" }));
1163
1164
  }, { once: true });
1164
1165
  ws.onopen = () => {
1165
1166
  ws.send(JSON.stringify({ type: "stop", session_id: sessionId2, reason: "n8n stop_session" }));
@@ -1175,12 +1176,12 @@ var BrowserCeki = class {
1175
1176
  }
1176
1177
  };
1177
1178
  ws.onerror = () => {
1178
- reject(new import_n8n_workflow.NodeApiError(this.getNode(), void 0, { message: "WebSocket connection failed" }));
1179
+ reject(new import_n8n_workflow.NodeApiError(node, void 0, { itemIndex: i, message: "WebSocket connection failed" }));
1179
1180
  };
1180
1181
  });
1181
1182
  out.push({ json: { stopped: true, session_id: sessionId2 }, pairedItem: { item: i } });
1182
1183
  } catch (e) {
1183
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Stop session failed" });
1184
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Stop session failed" });
1184
1185
  }
1185
1186
  continue;
1186
1187
  }
@@ -1193,7 +1194,7 @@ var BrowserCeki = class {
1193
1194
  try {
1194
1195
  sid2 = await resolveSid(i, client);
1195
1196
  } catch (e) {
1196
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Full: resolve schedule failed" });
1197
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Full: resolve schedule failed" });
1197
1198
  }
1198
1199
  }
1199
1200
  const mode = this.getNodeParameter("mode", i);
@@ -1204,7 +1205,7 @@ var BrowserCeki = class {
1204
1205
  try {
1205
1206
  if (demoMode) {
1206
1207
  session_id = "demo-" + Date.now();
1207
- const demoPng = Buffer.from("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", "base64");
1208
+ const demoPng = Buffer.from("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", "base64");
1208
1209
  binary = await this.helpers.prepareBinaryData(demoPng, "screenshot.png", "image/png");
1209
1210
  } else {
1210
1211
  try {
@@ -1217,7 +1218,7 @@ var BrowserCeki = class {
1217
1218
  binary = await this.helpers.prepareBinaryData(Buffer.from(data, "base64"), "screenshot.png", "image/png");
1218
1219
  await browser.close();
1219
1220
  } catch (e) {
1220
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Full op failed" });
1221
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Full op failed" });
1221
1222
  }
1222
1223
  }
1223
1224
  out.push({
@@ -1249,13 +1250,13 @@ var BrowserCeki = class {
1249
1250
  let scheduleId = 0;
1250
1251
  try {
1251
1252
  const list = await client.search({ geo: geo || void 0, max_price_per_min: maxPrice });
1252
- if (!list.length) throw new import_n8n_workflow.NodeOperationError(this.getNode(), "No browsers in geo " + (geo || "*"));
1253
+ if (!list.length) throw new import_n8n_workflow.NodeOperationError(node, "No browsers in geo " + (geo || "*"), { itemIndex: i });
1253
1254
  scheduleId = list[0].schedule_id;
1254
1255
  browser = await client.rent(scheduleId);
1255
1256
  touchedSessions.add(browser.sessionId);
1256
1257
  await browser.navigate(url);
1257
1258
  if (waitSelector) {
1258
- await waitForSelector(browser, waitSelector, waitTimeout);
1259
+ await waitForSelector(node, browser, waitSelector, waitTimeout);
1259
1260
  }
1260
1261
  const shot = await browser.screenshot({ format: "base64", fullPage });
1261
1262
  const data = shot.data ?? (shot instanceof Buffer ? shot.toString("base64") : "");
@@ -1274,8 +1275,7 @@ var BrowserCeki = class {
1274
1275
  }
1275
1276
  out.push({ json, binary: { data: binary }, pairedItem: { item: i } });
1276
1277
  } catch (e) {
1277
- if (e instanceof import_n8n_workflow.NodeOperationError || e instanceof import_n8n_workflow.NodeApiError) throw e;
1278
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Captcha-protected scrape failed" });
1278
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Captcha-protected scrape failed" });
1279
1279
  } finally {
1280
1280
  if (browser) {
1281
1281
  try {
@@ -1297,7 +1297,7 @@ var BrowserCeki = class {
1297
1297
  const maxPrice = this.getNodeParameter("maxPrice", i);
1298
1298
  try {
1299
1299
  const list = await client.search({ geo, max_price_per_min: maxPrice });
1300
- if (!list.length) throw new import_n8n_workflow.NodeOperationError(this.getNode(), "No browsers in geo " + geo);
1300
+ if (!list.length) throw new import_n8n_workflow.NodeOperationError(node, "No browsers in geo " + geo, { itemIndex: i });
1301
1301
  browser = await client.rent(list[0].schedule_id);
1302
1302
  touchedSessions.add(browser.sessionId);
1303
1303
  await browser.navigate(url);
@@ -1313,8 +1313,7 @@ var BrowserCeki = class {
1313
1313
  pairedItem: { item: i }
1314
1314
  });
1315
1315
  } catch (e) {
1316
- if (e instanceof import_n8n_workflow.NodeOperationError || e instanceof import_n8n_workflow.NodeApiError) throw e;
1317
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Screenshot in geo " + geo + " failed" });
1316
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Screenshot in geo " + geo + " failed" });
1318
1317
  } finally {
1319
1318
  if (browser) {
1320
1319
  try {
@@ -1403,15 +1402,14 @@ var BrowserCeki = class {
1403
1402
  result = await contractClient.poll();
1404
1403
  break;
1405
1404
  default:
1406
- throw new import_n8n_workflow.NodeOperationError(this.getNode(), "Unknown contract operation: " + pop);
1405
+ throw new import_n8n_workflow.NodeOperationError(node, "Unknown contract operation: " + pop, { itemIndex: i });
1407
1406
  }
1408
1407
  out.push({
1409
1408
  json: result,
1410
1409
  pairedItem: { item: i }
1411
1410
  });
1412
1411
  } catch (e) {
1413
- if (e instanceof import_n8n_workflow.NodeOperationError || e instanceof import_n8n_workflow.NodeApiError) throw e;
1414
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Contract " + pop + " failed" });
1412
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Contract " + pop + " failed" });
1415
1413
  }
1416
1414
  continue;
1417
1415
  }
@@ -1419,7 +1417,7 @@ var BrowserCeki = class {
1419
1417
  try {
1420
1418
  browser = await client.resume(sessionId);
1421
1419
  } catch (e) {
1422
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: `Resume session "${sessionId}" failed. Session may have expired or still be in grace.` });
1420
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: `Resume session "${sessionId}" failed. Session may have expired or still be in grace.` });
1423
1421
  }
1424
1422
  const sid = browser.sessionId;
1425
1423
  switch (op) {
@@ -1429,7 +1427,7 @@ var BrowserCeki = class {
1429
1427
  await browser.navigate(url);
1430
1428
  out.push({ json: { session_id: sid, url }, pairedItem: { item: i } });
1431
1429
  } catch (e) {
1432
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Navigate failed" });
1430
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Navigate failed" });
1433
1431
  }
1434
1432
  break;
1435
1433
  }
@@ -1440,7 +1438,7 @@ var BrowserCeki = class {
1440
1438
  await browser.click(x, y);
1441
1439
  out.push({ json: { session_id: sid, clicked: [x, y] }, pairedItem: { item: i } });
1442
1440
  } catch (e) {
1443
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Click failed" });
1441
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Click failed" });
1444
1442
  }
1445
1443
  break;
1446
1444
  }
@@ -1450,7 +1448,7 @@ var BrowserCeki = class {
1450
1448
  await browser.type(text);
1451
1449
  out.push({ json: { session_id: sid, typed: text }, pairedItem: { item: i } });
1452
1450
  } catch (e) {
1453
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Type failed" });
1451
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Type failed" });
1454
1452
  }
1455
1453
  break;
1456
1454
  }
@@ -1460,7 +1458,7 @@ var BrowserCeki = class {
1460
1458
  await browser.scroll(deltaY);
1461
1459
  out.push({ json: { session_id: sid, scrolled: deltaY }, pairedItem: { item: i } });
1462
1460
  } catch (e) {
1463
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Scroll failed" });
1461
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Scroll failed" });
1464
1462
  }
1465
1463
  break;
1466
1464
  }
@@ -1477,7 +1475,7 @@ var BrowserCeki = class {
1477
1475
  );
1478
1476
  out.push({ json: { session_id: sid }, binary: { data: binary }, pairedItem: { item: i } });
1479
1477
  } catch (e) {
1480
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Screenshot failed" });
1478
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Screenshot failed" });
1481
1479
  }
1482
1480
  break;
1483
1481
  }
@@ -1489,7 +1487,7 @@ var BrowserCeki = class {
1489
1487
  pairedItem: { item: i }
1490
1488
  });
1491
1489
  } catch (e) {
1492
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Snapshot failed" });
1490
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Snapshot failed" });
1493
1491
  }
1494
1492
  break;
1495
1493
  }
@@ -1520,8 +1518,9 @@ var BrowserCeki = class {
1520
1518
  }
1521
1519
  if (!ok) {
1522
1520
  throw new import_n8n_workflow.NodeOperationError(
1523
- this.getNode(),
1524
- `waitForSelector("${selector}") timed out after ${timeout}ms${lastErr ? `: ${lastErr.message}` : ""}`
1521
+ node,
1522
+ `waitForSelector("${selector}") timed out after ${timeout}ms${lastErr ? `: ${lastErr.message}` : ""}`,
1523
+ { itemIndex: i }
1525
1524
  );
1526
1525
  }
1527
1526
  out.push({ json: { session_id: sid, selector, found: true }, pairedItem: { item: i } });
@@ -1532,7 +1531,7 @@ var BrowserCeki = class {
1532
1531
  const selector = this.getNodeParameter("selector", i);
1533
1532
  const bpn = this.getNodeParameter("binaryPropertyName", i);
1534
1533
  const bin = items[i].binary?.[bpn];
1535
- if (!bin) throw new import_n8n_workflow.NodeOperationError(this.getNode(), `Binary property "${bpn}" not found on input`);
1534
+ if (!bin) throw new import_n8n_workflow.NodeOperationError(node, `Binary property "${bpn}" not found on input`, { itemIndex: i });
1536
1535
  const stream = await this.helpers.getBinaryStream(bin.id);
1537
1536
  const chunks = [];
1538
1537
  for await (const c of stream) chunks.push(c);
@@ -1540,7 +1539,7 @@ var BrowserCeki = class {
1540
1539
  const res = await browser.upload(selector, buf);
1541
1540
  out.push({ json: { session_id: sid, uploaded: res }, pairedItem: { item: i } });
1542
1541
  } catch (e) {
1543
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Upload failed" });
1542
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Upload failed" });
1544
1543
  }
1545
1544
  break;
1546
1545
  }
@@ -1602,11 +1601,15 @@ var BrowserCeki = class {
1602
1601
  } catch {
1603
1602
  }
1604
1603
  }
1604
+ } catch (error) {
1605
+ if (this.continueOnFail()) {
1606
+ out.push({ json: { error: error.message }, pairedItem: { item: i } });
1607
+ continue;
1608
+ }
1609
+ throw new import_n8n_workflow.NodeApiError(this.getNode(), error, { itemIndex: i });
1605
1610
  }
1606
- } catch (e) {
1607
- _execErr = e;
1608
1611
  }
1609
- if (_execErr && touchedSessions.size) {
1612
+ if (touchedSessions.size) {
1610
1613
  for (const sid of touchedSessions) {
1611
1614
  try {
1612
1615
  await _stopSession(sid);
@@ -1614,7 +1617,6 @@ var BrowserCeki = class {
1614
1617
  }
1615
1618
  }
1616
1619
  }
1617
- if (_execErr) throw _execErr;
1618
1620
  return [out];
1619
1621
  }
1620
1622
  };