@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.
@@ -148,18 +148,18 @@ var CekiClient = class {
148
148
  return;
149
149
  }
150
150
  await new Promise((r) => {
151
- const t = setTimeout(() => {
152
- r();
153
- }, 5e3);
151
+ const onAbort = () => r();
152
+ const abortTimer = AbortSignal.timeout(5e3);
153
+ abortTimer.addEventListener("abort", onAbort, { once: true });
154
154
  ws.onclose = () => {
155
155
  this._connected = false;
156
- clearTimeout(t);
156
+ abortTimer.removeEventListener("abort", onAbort);
157
157
  r();
158
158
  };
159
159
  try {
160
160
  ws.close();
161
161
  } catch {
162
- clearTimeout(t);
162
+ abortTimer.removeEventListener("abort", onAbort);
163
163
  r();
164
164
  }
165
165
  });
@@ -588,7 +588,7 @@ var ContractClient = class {
588
588
  };
589
589
 
590
590
  // nodes/BrowserCeki/BrowserCeki.node.ts
591
- async function waitForSelector(browser, selector, timeoutMs, intervalMs = 500) {
591
+ async function waitForSelector(node, browser, selector, timeoutMs, intervalMs = 500) {
592
592
  const expr = `!!document.querySelector(${JSON.stringify(selector)})`;
593
593
  const deadline = Date.now() + timeoutMs;
594
594
  let lastErr = null;
@@ -601,7 +601,8 @@ async function waitForSelector(browser, selector, timeoutMs, intervalMs = 500) {
601
601
  }
602
602
  await sleep(intervalMs);
603
603
  }
604
- throw new Error(
604
+ throw new import_n8n_workflow.NodeOperationError(
605
+ node,
605
606
  `waitForSelector("${selector}") timed out after ${timeoutMs}ms${lastErr ? ": " + lastErr.message : ""}`
606
607
  );
607
608
  }
@@ -643,6 +644,18 @@ var BrowserCeki = class {
643
644
  { name: "Captcha-Protected Scrape", value: "captchaScrape" },
644
645
  { name: "Click", value: "click" },
645
646
  { name: "Close", value: "close" },
647
+ // Contract operations
648
+ { name: "Contract: Assign Executor", value: "contract_assign" },
649
+ { name: "Contract: Call Human", value: "contract_callHuman" },
650
+ { name: "Contract: Comment", value: "contract_comment" },
651
+ { name: "Contract: Create Task", value: "contract_create" },
652
+ { name: "Contract: Get Task", value: "contract_get" },
653
+ { name: "Contract: List My Contracts", value: "contract_list" },
654
+ { name: "Contract: List Tasks in Contract", value: "contract_tasks" },
655
+ { name: "Contract: My Assigned Events", value: "contract_myEvents" },
656
+ { name: "Contract: Poll Notifications", value: "contract_poll" },
657
+ { name: "Contract: Progress Report", value: "contract_progress" },
658
+ { name: "Contract: Update Status", value: "contract_setStatus" },
646
659
  { name: "Full: Rent \u2192 Navigate \u2192 Screenshot", value: "full" },
647
660
  { name: "My Sessions", value: "my_sessions" },
648
661
  { name: "Navigate", value: "navigate" },
@@ -656,19 +669,7 @@ var BrowserCeki = class {
656
669
  { name: "Type", value: "type" },
657
670
  { name: "Upload", value: "upload" },
658
671
  { name: "Wait", value: "wait" },
659
- { name: "Wait for Selector", value: "waitForSelector" },
660
- // Contract operations
661
- { name: "Contract: Assign Executor", value: "contract_assign" },
662
- { name: "Contract: Call Human", value: "contract_callHuman" },
663
- { name: "Contract: Comment", value: "contract_comment" },
664
- { name: "Contract: Create Task", value: "contract_create" },
665
- { name: "Contract: Get Task", value: "contract_get" },
666
- { name: "Contract: List My Contracts", value: "contract_list" },
667
- { name: "Contract: List Tasks in Contract", value: "contract_tasks" },
668
- { name: "Contract: My Assigned Events", value: "contract_myEvents" },
669
- { name: "Contract: Poll Notifications", value: "contract_poll" },
670
- { name: "Contract: Progress Report", value: "contract_progress" },
671
- { name: "Contract: Update Status", value: "contract_setStatus" }
672
+ { name: "Wait for Selector", value: "waitForSelector" }
672
673
  ]
673
674
  },
674
675
  // === Rent: rental parameters ===
@@ -689,7 +690,7 @@ var BrowserCeki = class {
689
690
  displayOptions: { show: { operation: ["search", "captchaScrape", "screenshotGeo"] } }
690
691
  },
691
692
  {
692
- displayName: "Max $/min",
693
+ displayName: "Max $/Min",
693
694
  name: "maxPrice",
694
695
  type: "number",
695
696
  typeOptions: { numberPrecision: 4 },
@@ -709,8 +710,8 @@ var BrowserCeki = class {
709
710
  type: "options",
710
711
  default: "incognito",
711
712
  options: [
712
- { name: "main", value: "main" },
713
- { name: "incognito", value: "incognito" }
713
+ { name: "Incognito", value: "incognito" },
714
+ { name: "Main", value: "main" }
714
715
  ],
715
716
  displayOptions: { show: { operation: ["rent", "full"] } }
716
717
  },
@@ -749,7 +750,7 @@ var BrowserCeki = class {
749
750
  displayOptions: { show: { operation: ["captchaScrape"] } }
750
751
  },
751
752
  {
752
- displayName: "Max $/min",
753
+ displayName: "Max $/Min",
753
754
  name: "maxPrice",
754
755
  type: "number",
755
756
  typeOptions: { numberPrecision: 4 },
@@ -766,7 +767,7 @@ var BrowserCeki = class {
766
767
  displayOptions: { show: { operation: ["captchaScrape"] } }
767
768
  },
768
769
  {
769
- displayName: "Wait Timeout (ms)",
770
+ displayName: "Wait Timeout (Ms)",
770
771
  name: "waitTimeout",
771
772
  type: "number",
772
773
  default: 3e4,
@@ -820,7 +821,7 @@ var BrowserCeki = class {
820
821
  displayOptions: { show: { operation: ["screenshotGeo"] } }
821
822
  },
822
823
  {
823
- displayName: "Max $/min",
824
+ displayName: "Max $/Min",
824
825
  name: "maxPrice",
825
826
  type: "number",
826
827
  default: 0.02,
@@ -984,13 +985,13 @@ var BrowserCeki = class {
984
985
  type: "options",
985
986
  default: "png",
986
987
  options: [
987
- { name: "PNG (binary)", value: "png" },
988
- { name: "Base64", value: "base64" }
988
+ { name: "Base64", value: "base64" },
989
+ { name: "PNG (Binary)", value: "png" }
989
990
  ],
990
991
  displayOptions: { show: { operation: ["screenshot"] } }
991
992
  },
992
993
  {
993
- displayName: "Full page",
994
+ displayName: "Full Page",
994
995
  name: "fullPage",
995
996
  type: "boolean",
996
997
  default: false,
@@ -1015,7 +1016,7 @@ var BrowserCeki = class {
1015
1016
  displayOptions: { show: { operation: ["waitForSelector"] } }
1016
1017
  },
1017
1018
  {
1018
- displayName: "Timeout (ms)",
1019
+ displayName: "Timeout (Ms)",
1019
1020
  name: "waitTimeout",
1020
1021
  type: "number",
1021
1022
  default: 3e4,
@@ -1045,6 +1046,7 @@ var BrowserCeki = class {
1045
1046
  const out = [];
1046
1047
  const creds = await this.getCredentials("cekiApi");
1047
1048
  const token = creds.token;
1049
+ const node = this.getNode();
1048
1050
  const touchedSessions = /* @__PURE__ */ new Set();
1049
1051
  const _stopSession = async (sid) => {
1050
1052
  await new Promise((resolve) => {
@@ -1087,12 +1089,11 @@ var BrowserCeki = class {
1087
1089
  geo: geo || void 0,
1088
1090
  max_price_per_min: maxPrice
1089
1091
  });
1090
- if (!list.length) throw new import_n8n_workflow.NodeOperationError(this.getNode(), "No browsers found by filters");
1092
+ if (!list.length) throw new import_n8n_workflow.NodeOperationError(node, "No browsers found by filters");
1091
1093
  return list[0].schedule_id;
1092
1094
  };
1093
- let _execErr = null;
1094
- try {
1095
- for (let i = 0; i < items.length; i++) {
1095
+ for (let i = 0; i < items.length; i++) {
1096
+ try {
1096
1097
  const op = this.getNodeParameter("operation", i);
1097
1098
  const client = new CekiClient(token);
1098
1099
  let browser;
@@ -1109,7 +1110,7 @@ var BrowserCeki = class {
1109
1110
  pairedItem: { item: i }
1110
1111
  });
1111
1112
  } catch (e) {
1112
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Rent failed" });
1113
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Rent failed" });
1113
1114
  } finally {
1114
1115
  await client.disconnect();
1115
1116
  }
@@ -1125,7 +1126,7 @@ var BrowserCeki = class {
1125
1126
  });
1126
1127
  out.push({ json: { browsers: list, count: list.length }, pairedItem: { item: i } });
1127
1128
  } catch (e) {
1128
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Search failed" });
1129
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Search failed" });
1129
1130
  } finally {
1130
1131
  await client.disconnect();
1131
1132
  }
@@ -1136,12 +1137,12 @@ var BrowserCeki = class {
1136
1137
  const resp = await fetch("https://api.ceki.me/api/agent/sessions", {
1137
1138
  headers: { Authorization: `Bearer ${token}` }
1138
1139
  });
1139
- if (!resp.ok) throw new import_n8n_workflow.NodeApiError(this.getNode(), void 0, { message: `HTTP ${resp.status}`, httpCode: `${resp.status}` });
1140
+ if (!resp.ok) throw new import_n8n_workflow.NodeApiError(node, void 0, { itemIndex: i, message: `HTTP ${resp.status}`, httpCode: `${resp.status}` });
1140
1141
  const body = await resp.json();
1141
1142
  const sessions = body.data ?? [];
1142
1143
  out.push({ json: { sessions, count: sessions.length }, pairedItem: { item: i } });
1143
1144
  } catch (e) {
1144
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "My sessions failed" });
1145
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "My sessions failed" });
1145
1146
  }
1146
1147
  continue;
1147
1148
  }
@@ -1156,7 +1157,7 @@ var BrowserCeki = class {
1156
1157
  ws.close();
1157
1158
  } catch {
1158
1159
  }
1159
- reject(new import_n8n_workflow.NodeApiError(this.getNode(), void 0, { message: "Stop session timed out" }));
1160
+ reject(new import_n8n_workflow.NodeApiError(node, void 0, { itemIndex: i, message: "Stop session timed out" }));
1160
1161
  }, { once: true });
1161
1162
  ws.onopen = () => {
1162
1163
  ws.send(JSON.stringify({ type: "stop", session_id: sessionId2, reason: "n8n stop_session" }));
@@ -1172,12 +1173,12 @@ var BrowserCeki = class {
1172
1173
  }
1173
1174
  };
1174
1175
  ws.onerror = () => {
1175
- reject(new import_n8n_workflow.NodeApiError(this.getNode(), void 0, { message: "WebSocket connection failed" }));
1176
+ reject(new import_n8n_workflow.NodeApiError(node, void 0, { itemIndex: i, message: "WebSocket connection failed" }));
1176
1177
  };
1177
1178
  });
1178
1179
  out.push({ json: { stopped: true, session_id: sessionId2 }, pairedItem: { item: i } });
1179
1180
  } catch (e) {
1180
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Stop session failed" });
1181
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Stop session failed" });
1181
1182
  }
1182
1183
  continue;
1183
1184
  }
@@ -1190,7 +1191,7 @@ var BrowserCeki = class {
1190
1191
  try {
1191
1192
  sid2 = await resolveSid(i, client);
1192
1193
  } catch (e) {
1193
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Full: resolve schedule failed" });
1194
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Full: resolve schedule failed" });
1194
1195
  }
1195
1196
  }
1196
1197
  const mode = this.getNodeParameter("mode", i);
@@ -1201,7 +1202,7 @@ var BrowserCeki = class {
1201
1202
  try {
1202
1203
  if (demoMode) {
1203
1204
  session_id = "demo-" + Date.now();
1204
- const demoPng = Buffer.from("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", "base64");
1205
+ const demoPng = Buffer.from("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", "base64");
1205
1206
  binary = await this.helpers.prepareBinaryData(demoPng, "screenshot.png", "image/png");
1206
1207
  } else {
1207
1208
  try {
@@ -1214,7 +1215,7 @@ var BrowserCeki = class {
1214
1215
  binary = await this.helpers.prepareBinaryData(Buffer.from(data, "base64"), "screenshot.png", "image/png");
1215
1216
  await browser.close();
1216
1217
  } catch (e) {
1217
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Full op failed" });
1218
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Full op failed" });
1218
1219
  }
1219
1220
  }
1220
1221
  out.push({
@@ -1246,13 +1247,13 @@ var BrowserCeki = class {
1246
1247
  let scheduleId = 0;
1247
1248
  try {
1248
1249
  const list = await client.search({ geo: geo || void 0, max_price_per_min: maxPrice });
1249
- if (!list.length) throw new import_n8n_workflow.NodeOperationError(this.getNode(), "No browsers in geo " + (geo || "*"));
1250
+ if (!list.length) throw new import_n8n_workflow.NodeOperationError(node, "No browsers in geo " + (geo || "*"), { itemIndex: i });
1250
1251
  scheduleId = list[0].schedule_id;
1251
1252
  browser = await client.rent(scheduleId);
1252
1253
  touchedSessions.add(browser.sessionId);
1253
1254
  await browser.navigate(url);
1254
1255
  if (waitSelector) {
1255
- await waitForSelector(browser, waitSelector, waitTimeout);
1256
+ await waitForSelector(node, browser, waitSelector, waitTimeout);
1256
1257
  }
1257
1258
  const shot = await browser.screenshot({ format: "base64", fullPage });
1258
1259
  const data = shot.data ?? (shot instanceof Buffer ? shot.toString("base64") : "");
@@ -1271,8 +1272,7 @@ var BrowserCeki = class {
1271
1272
  }
1272
1273
  out.push({ json, binary: { data: binary }, pairedItem: { item: i } });
1273
1274
  } catch (e) {
1274
- if (e instanceof import_n8n_workflow.NodeOperationError || e instanceof import_n8n_workflow.NodeApiError) throw e;
1275
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Captcha-protected scrape failed" });
1275
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Captcha-protected scrape failed" });
1276
1276
  } finally {
1277
1277
  if (browser) {
1278
1278
  try {
@@ -1294,7 +1294,7 @@ var BrowserCeki = class {
1294
1294
  const maxPrice = this.getNodeParameter("maxPrice", i);
1295
1295
  try {
1296
1296
  const list = await client.search({ geo, max_price_per_min: maxPrice });
1297
- if (!list.length) throw new import_n8n_workflow.NodeOperationError(this.getNode(), "No browsers in geo " + geo);
1297
+ if (!list.length) throw new import_n8n_workflow.NodeOperationError(node, "No browsers in geo " + geo, { itemIndex: i });
1298
1298
  browser = await client.rent(list[0].schedule_id);
1299
1299
  touchedSessions.add(browser.sessionId);
1300
1300
  await browser.navigate(url);
@@ -1310,8 +1310,7 @@ var BrowserCeki = class {
1310
1310
  pairedItem: { item: i }
1311
1311
  });
1312
1312
  } catch (e) {
1313
- if (e instanceof import_n8n_workflow.NodeOperationError || e instanceof import_n8n_workflow.NodeApiError) throw e;
1314
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Screenshot in geo " + geo + " failed" });
1313
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Screenshot in geo " + geo + " failed" });
1315
1314
  } finally {
1316
1315
  if (browser) {
1317
1316
  try {
@@ -1400,15 +1399,14 @@ var BrowserCeki = class {
1400
1399
  result = await contractClient.poll();
1401
1400
  break;
1402
1401
  default:
1403
- throw new import_n8n_workflow.NodeOperationError(this.getNode(), "Unknown contract operation: " + pop);
1402
+ throw new import_n8n_workflow.NodeOperationError(node, "Unknown contract operation: " + pop, { itemIndex: i });
1404
1403
  }
1405
1404
  out.push({
1406
1405
  json: result,
1407
1406
  pairedItem: { item: i }
1408
1407
  });
1409
1408
  } catch (e) {
1410
- if (e instanceof import_n8n_workflow.NodeOperationError || e instanceof import_n8n_workflow.NodeApiError) throw e;
1411
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Contract " + pop + " failed" });
1409
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Contract " + pop + " failed" });
1412
1410
  }
1413
1411
  continue;
1414
1412
  }
@@ -1416,7 +1414,7 @@ var BrowserCeki = class {
1416
1414
  try {
1417
1415
  browser = await client.resume(sessionId);
1418
1416
  } catch (e) {
1419
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: `Resume session "${sessionId}" failed. Session may have expired or still be in grace.` });
1417
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: `Resume session "${sessionId}" failed. Session may have expired or still be in grace.` });
1420
1418
  }
1421
1419
  const sid = browser.sessionId;
1422
1420
  switch (op) {
@@ -1426,7 +1424,7 @@ var BrowserCeki = class {
1426
1424
  await browser.navigate(url);
1427
1425
  out.push({ json: { session_id: sid, url }, pairedItem: { item: i } });
1428
1426
  } catch (e) {
1429
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Navigate failed" });
1427
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Navigate failed" });
1430
1428
  }
1431
1429
  break;
1432
1430
  }
@@ -1437,7 +1435,7 @@ var BrowserCeki = class {
1437
1435
  await browser.click(x, y);
1438
1436
  out.push({ json: { session_id: sid, clicked: [x, y] }, pairedItem: { item: i } });
1439
1437
  } catch (e) {
1440
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Click failed" });
1438
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Click failed" });
1441
1439
  }
1442
1440
  break;
1443
1441
  }
@@ -1447,7 +1445,7 @@ var BrowserCeki = class {
1447
1445
  await browser.type(text);
1448
1446
  out.push({ json: { session_id: sid, typed: text }, pairedItem: { item: i } });
1449
1447
  } catch (e) {
1450
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Type failed" });
1448
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Type failed" });
1451
1449
  }
1452
1450
  break;
1453
1451
  }
@@ -1457,7 +1455,7 @@ var BrowserCeki = class {
1457
1455
  await browser.scroll(deltaY);
1458
1456
  out.push({ json: { session_id: sid, scrolled: deltaY }, pairedItem: { item: i } });
1459
1457
  } catch (e) {
1460
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Scroll failed" });
1458
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Scroll failed" });
1461
1459
  }
1462
1460
  break;
1463
1461
  }
@@ -1474,7 +1472,7 @@ var BrowserCeki = class {
1474
1472
  );
1475
1473
  out.push({ json: { session_id: sid }, binary: { data: binary }, pairedItem: { item: i } });
1476
1474
  } catch (e) {
1477
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Screenshot failed" });
1475
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Screenshot failed" });
1478
1476
  }
1479
1477
  break;
1480
1478
  }
@@ -1486,7 +1484,7 @@ var BrowserCeki = class {
1486
1484
  pairedItem: { item: i }
1487
1485
  });
1488
1486
  } catch (e) {
1489
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Snapshot failed" });
1487
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Snapshot failed" });
1490
1488
  }
1491
1489
  break;
1492
1490
  }
@@ -1517,8 +1515,9 @@ var BrowserCeki = class {
1517
1515
  }
1518
1516
  if (!ok) {
1519
1517
  throw new import_n8n_workflow.NodeOperationError(
1520
- this.getNode(),
1521
- `waitForSelector("${selector}") timed out after ${timeout}ms${lastErr ? `: ${lastErr.message}` : ""}`
1518
+ node,
1519
+ `waitForSelector("${selector}") timed out after ${timeout}ms${lastErr ? `: ${lastErr.message}` : ""}`,
1520
+ { itemIndex: i }
1522
1521
  );
1523
1522
  }
1524
1523
  out.push({ json: { session_id: sid, selector, found: true }, pairedItem: { item: i } });
@@ -1529,7 +1528,7 @@ var BrowserCeki = class {
1529
1528
  const selector = this.getNodeParameter("selector", i);
1530
1529
  const bpn = this.getNodeParameter("binaryPropertyName", i);
1531
1530
  const bin = items[i].binary?.[bpn];
1532
- if (!bin) throw new import_n8n_workflow.NodeOperationError(this.getNode(), `Binary property "${bpn}" not found on input`);
1531
+ if (!bin) throw new import_n8n_workflow.NodeOperationError(node, `Binary property "${bpn}" not found on input`, { itemIndex: i });
1533
1532
  const stream = await this.helpers.getBinaryStream(bin.id);
1534
1533
  const chunks = [];
1535
1534
  for await (const c of stream) chunks.push(c);
@@ -1537,7 +1536,7 @@ var BrowserCeki = class {
1537
1536
  const res = await browser.upload(selector, buf);
1538
1537
  out.push({ json: { session_id: sid, uploaded: res }, pairedItem: { item: i } });
1539
1538
  } catch (e) {
1540
- throw new import_n8n_workflow.NodeApiError(this.getNode(), e, { message: "Upload failed" });
1539
+ throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Upload failed" });
1541
1540
  }
1542
1541
  break;
1543
1542
  }
@@ -1599,11 +1598,15 @@ var BrowserCeki = class {
1599
1598
  } catch {
1600
1599
  }
1601
1600
  }
1601
+ } catch (error) {
1602
+ if (this.continueOnFail()) {
1603
+ out.push({ json: { error: error.message }, pairedItem: { item: i } });
1604
+ continue;
1605
+ }
1606
+ throw new import_n8n_workflow.NodeApiError(this.getNode(), error, { itemIndex: i });
1602
1607
  }
1603
- } catch (e) {
1604
- _execErr = e;
1605
1608
  }
1606
- if (_execErr && touchedSessions.size) {
1609
+ if (touchedSessions.size) {
1607
1610
  for (const sid of touchedSessions) {
1608
1611
  try {
1609
1612
  await _stopSession(sid);
@@ -1611,7 +1614,6 @@ var BrowserCeki = class {
1611
1614
  }
1612
1615
  }
1613
1616
  }
1614
- if (_execErr) throw _execErr;
1615
1617
  return [out];
1616
1618
  }
1617
1619
  };