n8n-nodes-duckduckgo-search 31.0.0 → 31.0.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.
@@ -1356,7 +1356,7 @@ class DuckDuckGo {
1356
1356
  return paginationResult.results;
1357
1357
  }
1358
1358
  async execute() {
1359
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7;
1359
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
1360
1360
  const items = this.getInputData();
1361
1361
  const returnData = [];
1362
1362
  const debugMode = this.getNodeParameter('debugMode', 0, false);
@@ -1370,17 +1370,25 @@ class DuckDuckGo {
1370
1370
  });
1371
1371
  let reliabilityManager = null;
1372
1372
  if (reliabilitySettings && reliabilitySettings.enableReliability !== false) {
1373
- const reliabilityConfig = {
1374
- emptyResultThreshold: reliabilitySettings.emptyResultThreshold,
1375
- initialBackoffMs: reliabilitySettings.initialBackoffMs,
1376
- maxBackoffMs: reliabilitySettings.maxBackoffMs,
1377
- minJitterMs: reliabilitySettings.minJitterMs,
1378
- maxJitterMs: reliabilitySettings.maxJitterMs,
1379
- failureThreshold: reliabilitySettings.failureThreshold,
1380
- resetTimeoutMs: reliabilitySettings.resetTimeoutMs,
1381
- maxRetries: reliabilitySettings.maxRetries,
1382
- retryDelayMs: reliabilitySettings.retryDelayMs,
1383
- };
1373
+ const reliabilityConfig = {};
1374
+ if (reliabilitySettings.emptyResultThreshold !== undefined)
1375
+ reliabilityConfig.emptyResultThreshold = reliabilitySettings.emptyResultThreshold;
1376
+ if (reliabilitySettings.initialBackoffMs !== undefined)
1377
+ reliabilityConfig.initialBackoffMs = reliabilitySettings.initialBackoffMs;
1378
+ if (reliabilitySettings.maxBackoffMs !== undefined)
1379
+ reliabilityConfig.maxBackoffMs = reliabilitySettings.maxBackoffMs;
1380
+ if (reliabilitySettings.minJitterMs !== undefined)
1381
+ reliabilityConfig.minJitterMs = reliabilitySettings.minJitterMs;
1382
+ if (reliabilitySettings.maxJitterMs !== undefined)
1383
+ reliabilityConfig.maxJitterMs = reliabilitySettings.maxJitterMs;
1384
+ if (reliabilitySettings.failureThreshold !== undefined)
1385
+ reliabilityConfig.failureThreshold = reliabilitySettings.failureThreshold;
1386
+ if (reliabilitySettings.resetTimeoutMs !== undefined)
1387
+ reliabilityConfig.resetTimeoutMs = reliabilitySettings.resetTimeoutMs;
1388
+ if (reliabilitySettings.maxRetries !== undefined)
1389
+ reliabilityConfig.maxRetries = reliabilitySettings.maxRetries;
1390
+ if (reliabilitySettings.retryDelayMs !== undefined)
1391
+ reliabilityConfig.retryDelayMs = reliabilitySettings.retryDelayMs;
1384
1392
  reliabilityManager = (0, reliabilityManager_1.getGlobalReliabilityManager)(reliabilityConfig);
1385
1393
  if (debugMode) {
1386
1394
  const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.INFO, `Reliability Manager initialized: ${reliabilityManager.getSummary()}`, 'execute', { config: reliabilityConfig });
@@ -1469,21 +1477,31 @@ class DuckDuckGo {
1469
1477
  const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.INFO, `Executing web search for: ${enhancedQuery}`, operation, { query: enhancedQuery, options: searchOptions, cacheEnabled: enableCache });
1470
1478
  console.log(JSON.stringify(logEntry));
1471
1479
  }
1472
- const directResults = await (0, directSearch_1.directWebSearch)(enhancedQuery, {
1473
- locale: searchOptions.locale || 'us-en',
1474
- safeSearch: (0, directSearch_1.getSafeSearchString)((_e = options.safeSearch) !== null && _e !== void 0 ? _e : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
1475
- maxResults: undefined,
1476
- });
1477
- result = {
1478
- results: directResults.results.map(r => ({
1479
- title: r.title,
1480
- url: r.url,
1481
- description: r.description,
1482
- hostname: new URL(r.url).hostname,
1483
- })),
1484
- noResults: directResults.results.length === 0,
1480
+ const executeSearch = async () => {
1481
+ var _a;
1482
+ const directResults = await (0, directSearch_1.directWebSearch)(enhancedQuery, {
1483
+ locale: searchOptions.locale || 'us-en',
1484
+ safeSearch: (0, directSearch_1.getSafeSearchString)((_a = options.safeSearch) !== null && _a !== void 0 ? _a : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
1485
+ maxResults: undefined,
1486
+ });
1487
+ const searchResult = {
1488
+ results: directResults.results.map(r => ({
1489
+ title: r.title,
1490
+ url: r.url,
1491
+ description: r.description,
1492
+ hostname: new URL(r.url).hostname,
1493
+ })),
1494
+ noResults: directResults.results.length === 0,
1495
+ };
1496
+ return searchResult;
1485
1497
  };
1486
- const maxResults = (_f = options.maxResults) !== null && _f !== void 0 ? _f : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1498
+ if (reliabilityManager) {
1499
+ result = await reliabilityManager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, `web search: ${enhancedQuery}`);
1500
+ }
1501
+ else {
1502
+ result = await executeSearch();
1503
+ }
1504
+ const maxResults = (_e = options.maxResults) !== null && _e !== void 0 ? _e : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1487
1505
  if (result.results && result.results.length > maxResults) {
1488
1506
  result.results = result.results.slice(0, maxResults);
1489
1507
  }
@@ -1537,7 +1555,7 @@ class DuckDuckGo {
1537
1555
  }];
1538
1556
  }
1539
1557
  else {
1540
- const maxResults = (_g = options.maxResults) !== null && _g !== void 0 ? _g : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1558
+ const maxResults = (_f = options.maxResults) !== null && _f !== void 0 ? _f : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1541
1559
  results = (0, processors_1.processWebSearchResults)(result.results, itemIndex, result).slice(0, maxResults);
1542
1560
  if (debugMode && results.length > 0) {
1543
1561
  results[0].json.fromCache = result !== undefined;
@@ -1581,7 +1599,7 @@ class DuckDuckGo {
1581
1599
  query: enhancedQuery,
1582
1600
  durationMs: Date.now() - startTime,
1583
1601
  error: errorMessage,
1584
- errorType: ((_h = error === null || error === void 0 ? void 0 : error.constructor) === null || _h === void 0 ? void 0 : _h.name) || 'Error',
1602
+ errorType: ((_g = error === null || error === void 0 ? void 0 : error.constructor) === null || _g === void 0 ? void 0 : _g.name) || 'Error',
1585
1603
  });
1586
1604
  }
1587
1605
  }
@@ -1590,8 +1608,8 @@ class DuckDuckGo {
1590
1608
  const imageQuery = this.getNodeParameter('imageQuery', itemIndex);
1591
1609
  const imageSearchOptions = this.getNodeParameter('imageSearchOptions', itemIndex, {});
1592
1610
  const searchOptions = {
1593
- safeSearch: getSafeSearchType((_j = imageSearchOptions.safeSearch) !== null && _j !== void 0 ? _j : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
1594
- locale: (_l = (_k = imageSearchOptions.region) !== null && _k !== void 0 ? _k : globalLocale) !== null && _l !== void 0 ? _l : constants_1.DEFAULT_PARAMETERS.REGION,
1611
+ safeSearch: getSafeSearchType((_h = imageSearchOptions.safeSearch) !== null && _h !== void 0 ? _h : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
1612
+ locale: (_k = (_j = imageSearchOptions.region) !== null && _j !== void 0 ? _j : globalLocale) !== null && _k !== void 0 ? _k : constants_1.DEFAULT_PARAMETERS.REGION,
1595
1613
  size: imageSearchOptions.size,
1596
1614
  color: imageSearchOptions.color,
1597
1615
  type: imageSearchOptions.type,
@@ -1634,23 +1652,33 @@ class DuckDuckGo {
1634
1652
  const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.INFO, `Executing image search for: ${imageQuery}`, operation, { query: imageQuery, options: searchOptions, cacheEnabled: enableCache });
1635
1653
  console.log(JSON.stringify(logEntry));
1636
1654
  }
1637
- const directImageResults = await (0, directSearch_1.directImageSearch)(imageQuery, {
1638
- locale: searchOptions.locale || 'us-en',
1639
- safeSearch: (0, directSearch_1.getSafeSearchString)((_m = imageSearchOptions.safeSearch) !== null && _m !== void 0 ? _m : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
1640
- maxResults: undefined,
1641
- });
1642
- result = {
1643
- results: directImageResults.results.map(r => ({
1644
- title: r.title,
1645
- image: r.url,
1646
- thumbnail: r.thumbnail,
1647
- url: r.source,
1648
- height: r.height,
1649
- width: r.width,
1650
- })),
1651
- noResults: directImageResults.results.length === 0,
1655
+ const executeImageSearch = async () => {
1656
+ var _a;
1657
+ const directImageResults = await (0, directSearch_1.directImageSearch)(imageQuery, {
1658
+ locale: searchOptions.locale || 'us-en',
1659
+ safeSearch: (0, directSearch_1.getSafeSearchString)((_a = imageSearchOptions.safeSearch) !== null && _a !== void 0 ? _a : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
1660
+ maxResults: undefined,
1661
+ });
1662
+ const searchResult = {
1663
+ results: directImageResults.results.map(r => ({
1664
+ title: r.title,
1665
+ image: r.url,
1666
+ thumbnail: r.thumbnail,
1667
+ url: r.source,
1668
+ height: r.height,
1669
+ width: r.width,
1670
+ })),
1671
+ noResults: directImageResults.results.length === 0,
1672
+ };
1673
+ return searchResult;
1652
1674
  };
1653
- const maxResults = (_o = imageSearchOptions.maxResults) !== null && _o !== void 0 ? _o : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1675
+ if (reliabilityManager) {
1676
+ result = await reliabilityManager.executeWithRetry(executeImageSearch, (res) => res.results && res.results.length > 0, `image search: ${imageQuery}`);
1677
+ }
1678
+ else {
1679
+ result = await executeImageSearch();
1680
+ }
1681
+ const maxResults = (_l = imageSearchOptions.maxResults) !== null && _l !== void 0 ? _l : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1654
1682
  if (result.results && result.results.length > maxResults) {
1655
1683
  result.results = result.results.slice(0, maxResults);
1656
1684
  }
@@ -1704,7 +1732,7 @@ class DuckDuckGo {
1704
1732
  }];
1705
1733
  }
1706
1734
  else {
1707
- const maxResults = (_p = imageSearchOptions.maxResults) !== null && _p !== void 0 ? _p : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1735
+ const maxResults = (_m = imageSearchOptions.maxResults) !== null && _m !== void 0 ? _m : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1708
1736
  results = (0, processors_1.processImageSearchResults)(result.results, itemIndex).slice(0, maxResults);
1709
1737
  if (debugMode && results.length > 0) {
1710
1738
  results[0].json.fromCache = result !== undefined;
@@ -1748,7 +1776,7 @@ class DuckDuckGo {
1748
1776
  query: imageQuery,
1749
1777
  durationMs: Date.now() - startTime,
1750
1778
  error: errorMessage,
1751
- errorType: ((_q = error === null || error === void 0 ? void 0 : error.constructor) === null || _q === void 0 ? void 0 : _q.name) || 'Error',
1779
+ errorType: ((_o = error === null || error === void 0 ? void 0 : error.constructor) === null || _o === void 0 ? void 0 : _o.name) || 'Error',
1752
1780
  });
1753
1781
  }
1754
1782
  }
@@ -1757,16 +1785,16 @@ class DuckDuckGo {
1757
1785
  const newsQuery = this.getNodeParameter('newsQuery', itemIndex);
1758
1786
  const newsSearchOptions = this.getNodeParameter('newsSearchOptions', itemIndex, {});
1759
1787
  const searchOptions = {
1760
- safeSearch: getSafeSearchType((_r = newsSearchOptions.safeSearch) !== null && _r !== void 0 ? _r : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
1761
- locale: (_t = (_s = newsSearchOptions.region) !== null && _s !== void 0 ? _s : globalLocale) !== null && _t !== void 0 ? _t : constants_1.DEFAULT_PARAMETERS.REGION,
1762
- time: getSearchTimeType((_u = newsSearchOptions.timePeriod) !== null && _u !== void 0 ? _u : constants_1.DEFAULT_PARAMETERS.TIME_PERIOD),
1788
+ safeSearch: getSafeSearchType((_p = newsSearchOptions.safeSearch) !== null && _p !== void 0 ? _p : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
1789
+ locale: (_r = (_q = newsSearchOptions.region) !== null && _q !== void 0 ? _q : globalLocale) !== null && _r !== void 0 ? _r : constants_1.DEFAULT_PARAMETERS.REGION,
1790
+ time: getSearchTimeType((_s = newsSearchOptions.timePeriod) !== null && _s !== void 0 ? _s : constants_1.DEFAULT_PARAMETERS.TIME_PERIOD),
1763
1791
  };
1764
1792
  if (apiKey) {
1765
1793
  searchOptions.headers = {
1766
1794
  Authorization: `Bearer ${apiKey}`,
1767
1795
  };
1768
1796
  }
1769
- const maxResults = (_v = newsSearchOptions.maxResults) !== null && _v !== void 0 ? _v : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1797
+ const maxResults = (_t = newsSearchOptions.maxResults) !== null && _t !== void 0 ? _t : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1770
1798
  const cacheKey = JSON.stringify({
1771
1799
  operation,
1772
1800
  query: newsQuery,
@@ -1798,7 +1826,16 @@ class DuckDuckGo {
1798
1826
  const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.INFO, `Executing news search for: ${newsQuery}`, operation, { query: newsQuery, options: searchOptions, cacheEnabled: enableCache });
1799
1827
  console.log(JSON.stringify(logEntry));
1800
1828
  }
1801
- result = await (0, duck_duck_scrape_1.searchNews)(newsQuery, searchOptions);
1829
+ const executeNewsSearch = async () => {
1830
+ const searchResult = await (0, duck_duck_scrape_1.searchNews)(newsQuery, searchOptions);
1831
+ return searchResult;
1832
+ };
1833
+ if (reliabilityManager) {
1834
+ result = await reliabilityManager.executeWithRetry(executeNewsSearch, (res) => res.results && res.results.length > 0, `news search: ${newsQuery}`);
1835
+ }
1836
+ else {
1837
+ result = await executeNewsSearch();
1838
+ }
1802
1839
  if (newsSearchOptions.maxResults !== undefined && result.results && result.results.length > 0) {
1803
1840
  let page = 2;
1804
1841
  const maxPages = Math.ceil(maxResults / 10);
@@ -1891,7 +1928,7 @@ class DuckDuckGo {
1891
1928
  }];
1892
1929
  }
1893
1930
  else {
1894
- const maxResults = (_w = newsSearchOptions.maxResults) !== null && _w !== void 0 ? _w : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1931
+ const maxResults = (_u = newsSearchOptions.maxResults) !== null && _u !== void 0 ? _u : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1895
1932
  results = (0, processors_1.processNewsSearchResults)(result.results, itemIndex).slice(0, maxResults);
1896
1933
  if (debugMode && results.length > 0) {
1897
1934
  results[0].json.fromCache = result !== undefined;
@@ -1925,7 +1962,7 @@ class DuckDuckGo {
1925
1962
  image: '',
1926
1963
  source: 'DuckDuckGo Fallback',
1927
1964
  }));
1928
- results = (0, processors_1.processNewsSearchResults)(newsResults, itemIndex).slice(0, (_x = newsSearchOptions.maxResults) !== null && _x !== void 0 ? _x : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS);
1965
+ results = (0, processors_1.processNewsSearchResults)(newsResults, itemIndex).slice(0, (_v = newsSearchOptions.maxResults) !== null && _v !== void 0 ? _v : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS);
1929
1966
  if (enableTelemetry) {
1930
1967
  await (0, telemetry_1.reportEvent)(this, 'search_completed', {
1931
1968
  operation,
@@ -1966,7 +2003,7 @@ class DuckDuckGo {
1966
2003
  query: newsQuery,
1967
2004
  durationMs: Date.now() - startTime,
1968
2005
  error: errorMessage,
1969
- errorType: ((_y = error === null || error === void 0 ? void 0 : error.constructor) === null || _y === void 0 ? void 0 : _y.name) || 'Error',
2006
+ errorType: ((_w = error === null || error === void 0 ? void 0 : error.constructor) === null || _w === void 0 ? void 0 : _w.name) || 'Error',
1970
2007
  });
1971
2008
  }
1972
2009
  }
@@ -1975,14 +2012,14 @@ class DuckDuckGo {
1975
2012
  const videoQuery = this.getNodeParameter('videoQuery', itemIndex);
1976
2013
  const videoSearchOptions = this.getNodeParameter('videoSearchOptions', itemIndex, {});
1977
2014
  const searchOptions = {
1978
- safeSearch: getSafeSearchType((_z = videoSearchOptions.safeSearch) !== null && _z !== void 0 ? _z : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
1979
- locale: (_1 = (_0 = videoSearchOptions.region) !== null && _0 !== void 0 ? _0 : globalLocale) !== null && _1 !== void 0 ? _1 : constants_1.DEFAULT_PARAMETERS.REGION,
1980
- time: getSearchTimeType((_2 = videoSearchOptions.timePeriod) !== null && _2 !== void 0 ? _2 : constants_1.DEFAULT_PARAMETERS.TIME_PERIOD),
2015
+ safeSearch: getSafeSearchType((_x = videoSearchOptions.safeSearch) !== null && _x !== void 0 ? _x : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
2016
+ locale: (_z = (_y = videoSearchOptions.region) !== null && _y !== void 0 ? _y : globalLocale) !== null && _z !== void 0 ? _z : constants_1.DEFAULT_PARAMETERS.REGION,
2017
+ time: getSearchTimeType((_0 = videoSearchOptions.timePeriod) !== null && _0 !== void 0 ? _0 : constants_1.DEFAULT_PARAMETERS.TIME_PERIOD),
1981
2018
  definition: getVideoDefinition(videoSearchOptions.quality),
1982
2019
  duration: getVideoDuration(videoSearchOptions.length),
1983
2020
  license: getVideoLicense(videoSearchOptions.resolution),
1984
2021
  };
1985
- const maxResults = (_3 = videoSearchOptions.maxResults) !== null && _3 !== void 0 ? _3 : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
2022
+ const maxResults = (_1 = videoSearchOptions.maxResults) !== null && _1 !== void 0 ? _1 : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
1986
2023
  if (apiKey) {
1987
2024
  searchOptions.headers = {
1988
2025
  Authorization: `Bearer ${apiKey}`,
@@ -2019,7 +2056,16 @@ class DuckDuckGo {
2019
2056
  const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.INFO, `Executing video search for: ${videoQuery}`, operation, { query: videoQuery, options: searchOptions, cacheEnabled: enableCache });
2020
2057
  console.log(JSON.stringify(logEntry));
2021
2058
  }
2022
- result = await (0, duck_duck_scrape_1.searchVideos)(videoQuery, searchOptions);
2059
+ const executeVideoSearch = async () => {
2060
+ const searchResult = await (0, duck_duck_scrape_1.searchVideos)(videoQuery, searchOptions);
2061
+ return searchResult;
2062
+ };
2063
+ if (reliabilityManager) {
2064
+ result = await reliabilityManager.executeWithRetry(executeVideoSearch, (res) => res.results && res.results.length > 0, `video search: ${videoQuery}`);
2065
+ }
2066
+ else {
2067
+ result = await executeVideoSearch();
2068
+ }
2023
2069
  if (videoSearchOptions.maxResults !== undefined && result.results && result.results.length > 0) {
2024
2070
  let page = 2;
2025
2071
  const maxPages = Math.ceil(maxResults / 10);
@@ -2112,7 +2158,7 @@ class DuckDuckGo {
2112
2158
  }];
2113
2159
  }
2114
2160
  else {
2115
- const maxResults = (_4 = videoSearchOptions.maxResults) !== null && _4 !== void 0 ? _4 : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
2161
+ const maxResults = (_2 = videoSearchOptions.maxResults) !== null && _2 !== void 0 ? _2 : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
2116
2162
  results = (0, processors_1.processVideoSearchResults)(result.results, itemIndex).slice(0, maxResults);
2117
2163
  if (debugMode && results.length > 0) {
2118
2164
  results[0].json.fromCache = result !== undefined;
@@ -2157,7 +2203,7 @@ class DuckDuckGo {
2157
2203
  title: item.title,
2158
2204
  uploader: 'Web',
2159
2205
  }));
2160
- results = (0, processors_1.processVideoSearchResults)(videoResults, itemIndex).slice(0, (_5 = videoSearchOptions.maxResults) !== null && _5 !== void 0 ? _5 : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS);
2206
+ results = (0, processors_1.processVideoSearchResults)(videoResults, itemIndex).slice(0, (_3 = videoSearchOptions.maxResults) !== null && _3 !== void 0 ? _3 : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS);
2161
2207
  if (enableTelemetry) {
2162
2208
  await (0, telemetry_1.reportEvent)(this, 'search_completed', {
2163
2209
  operation,
@@ -2198,7 +2244,7 @@ class DuckDuckGo {
2198
2244
  query: videoQuery,
2199
2245
  durationMs: Date.now() - startTime,
2200
2246
  error: errorMessage,
2201
- errorType: ((_6 = error === null || error === void 0 ? void 0 : error.constructor) === null || _6 === void 0 ? void 0 : _6.name) || 'Error',
2247
+ errorType: ((_4 = error === null || error === void 0 ? void 0 : error.constructor) === null || _4 === void 0 ? void 0 : _4.name) || 'Error',
2202
2248
  });
2203
2249
  }
2204
2250
  }
@@ -2241,7 +2287,7 @@ class DuckDuckGo {
2241
2287
  if (enableTelemetry) {
2242
2288
  await (0, telemetry_1.reportEvent)(this, 'node_error', {
2243
2289
  error: error instanceof Error ? error.message : String(error),
2244
- errorType: ((_7 = error === null || error === void 0 ? void 0 : error.constructor) === null || _7 === void 0 ? void 0 : _7.name) || 'Error',
2290
+ errorType: ((_5 = error === null || error === void 0 ? void 0 : error.constructor) === null || _5 === void 0 ? void 0 : _5.name) || 'Error',
2245
2291
  });
2246
2292
  }
2247
2293
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex });
@@ -27,6 +27,7 @@ const DuckDuckGo_node_1 = require("../DuckDuckGo.node");
27
27
  const duckDuckScrape = __importStar(require("duck-duck-scrape"));
28
28
  const cache = __importStar(require("../cache"));
29
29
  const directSearch = __importStar(require("../directSearch"));
30
+ const reliabilityManager_1 = require("../reliabilityManager");
30
31
  jest.mock('duck-duck-scrape', () => ({
31
32
  search: jest.fn(),
32
33
  searchNews: jest.fn(),
@@ -110,6 +111,7 @@ describe('DuckDuckGo Node', () => {
110
111
  },
111
112
  };
112
113
  jest.clearAllMocks();
114
+ (0, reliabilityManager_1.resetGlobalReliabilityManager)();
113
115
  });
114
116
  const setupNodeParameters = (operation, query, options = {}, additionalParams = {}) => {
115
117
  mockGetNodeParameter.mockImplementation((parameter, _itemIndex, fallback) => {
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ const reliabilityManager_1 = require("../reliabilityManager");
27
+ const directSearch = __importStar(require("../directSearch"));
28
+ jest.mock('../directSearch');
29
+ describe('Reliability Manager Integration', () => {
30
+ let mockDirectWebSearch;
31
+ beforeEach(() => {
32
+ jest.clearAllMocks();
33
+ mockDirectWebSearch = directSearch.directWebSearch;
34
+ });
35
+ describe('executeWithRetry integration', () => {
36
+ it('should call directWebSearch through reliability manager', async () => {
37
+ const manager = new reliabilityManager_1.ReliabilityManager({
38
+ maxRetries: 2,
39
+ retryDelayMs: 100,
40
+ });
41
+ mockDirectWebSearch.mockResolvedValue({
42
+ results: [
43
+ { title: 'Result 1', url: 'https://example.com/1', description: 'Test' }
44
+ ]
45
+ });
46
+ const executeSearch = async () => {
47
+ const result = await mockDirectWebSearch('test query', {
48
+ locale: 'us-en',
49
+ safeSearch: 'moderate',
50
+ });
51
+ return {
52
+ results: result.results,
53
+ noResults: result.results.length === 0,
54
+ };
55
+ };
56
+ const result = await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
57
+ expect(mockDirectWebSearch).toHaveBeenCalledTimes(1);
58
+ expect(result.results).toHaveLength(1);
59
+ expect(manager.getMetrics().totalRequests).toBe(1);
60
+ expect(manager.getMetrics().emptyResponses).toBe(0);
61
+ });
62
+ it('should retry on empty results', async () => {
63
+ const manager = new reliabilityManager_1.ReliabilityManager({
64
+ maxRetries: 3,
65
+ retryDelayMs: 10,
66
+ emptyResultThreshold: 2,
67
+ });
68
+ mockDirectWebSearch
69
+ .mockResolvedValueOnce({ results: [] })
70
+ .mockResolvedValueOnce({ results: [] })
71
+ .mockResolvedValueOnce({
72
+ results: [
73
+ { title: 'Result 1', url: 'https://example.com/1', description: 'Test' }
74
+ ]
75
+ });
76
+ const executeSearch = async () => {
77
+ const result = await mockDirectWebSearch('test query', {
78
+ locale: 'us-en',
79
+ safeSearch: 'moderate',
80
+ });
81
+ return {
82
+ results: result.results,
83
+ noResults: result.results.length === 0,
84
+ };
85
+ };
86
+ const result = await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
87
+ expect(mockDirectWebSearch).toHaveBeenCalledTimes(3);
88
+ expect(result.results).toHaveLength(1);
89
+ expect(manager.getMetrics().retriesExecuted).toBe(2);
90
+ });
91
+ it('should record failures and respect circuit breaker', async () => {
92
+ const manager = new reliabilityManager_1.ReliabilityManager({
93
+ failureThreshold: 4,
94
+ resetTimeoutMs: 1000,
95
+ maxRetries: 1,
96
+ retryDelayMs: 10,
97
+ });
98
+ mockDirectWebSearch.mockRejectedValue(new Error('Network error'));
99
+ const executeSearch = async () => {
100
+ const result = await mockDirectWebSearch('test query', {
101
+ locale: 'us-en',
102
+ safeSearch: 'moderate',
103
+ });
104
+ return {
105
+ results: result.results,
106
+ noResults: result.results.length === 0,
107
+ };
108
+ };
109
+ await expect(manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search')).rejects.toThrow('Network error');
110
+ await expect(manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search')).rejects.toThrow('Network error');
111
+ expect(manager.getCircuitState()).toBe('open');
112
+ expect(manager.getMetrics().circuitBreakerTrips).toBeGreaterThan(0);
113
+ await expect(manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search')).rejects.toThrow('Circuit breaker is OPEN');
114
+ });
115
+ it('should apply jitter and backoff on consecutive empty results', async () => {
116
+ const manager = new reliabilityManager_1.ReliabilityManager({
117
+ emptyResultThreshold: 2,
118
+ initialBackoffMs: 100,
119
+ maxBackoffMs: 1000,
120
+ minJitterMs: 10,
121
+ maxJitterMs: 50,
122
+ maxRetries: 0,
123
+ });
124
+ mockDirectWebSearch.mockResolvedValue({ results: [] });
125
+ const executeSearch = async () => {
126
+ const result = await mockDirectWebSearch('test query', {
127
+ locale: 'us-en',
128
+ safeSearch: 'moderate',
129
+ });
130
+ return {
131
+ results: result.results,
132
+ noResults: result.results.length === 0,
133
+ };
134
+ };
135
+ await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
136
+ await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
137
+ let metrics = manager.getMetrics();
138
+ expect(metrics.consecutiveEmptyResponses).toBe(2);
139
+ expect(metrics.emptyResponses).toBe(2);
140
+ const startTime = Date.now();
141
+ await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
142
+ const elapsed = Date.now() - startTime;
143
+ metrics = manager.getMetrics();
144
+ expect(elapsed).toBeGreaterThanOrEqual(100);
145
+ expect(metrics.backoffActivations).toBeGreaterThan(0);
146
+ });
147
+ });
148
+ describe('metrics tracking', () => {
149
+ it('should track response times', async () => {
150
+ const manager = new reliabilityManager_1.ReliabilityManager();
151
+ mockDirectWebSearch.mockImplementation(async () => {
152
+ await new Promise(resolve => setTimeout(resolve, 50));
153
+ return { results: [{ title: 'Test', url: 'https://example.com', description: 'Test' }] };
154
+ });
155
+ const executeSearch = async () => {
156
+ const result = await mockDirectWebSearch('test query', {
157
+ locale: 'us-en',
158
+ safeSearch: 'moderate',
159
+ });
160
+ return {
161
+ results: result.results,
162
+ noResults: result.results.length === 0,
163
+ };
164
+ };
165
+ await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
166
+ const metrics = manager.getMetrics();
167
+ expect(metrics.averageResponseTimeMs).toBeGreaterThan(0);
168
+ expect(metrics.totalRequests).toBe(1);
169
+ });
170
+ });
171
+ describe('CRITICAL: No Double-Counting of Metrics', () => {
172
+ it('should count each request exactly once (not doubled)', async () => {
173
+ const manager = new reliabilityManager_1.ReliabilityManager({
174
+ maxRetries: 0,
175
+ });
176
+ mockDirectWebSearch.mockResolvedValue({
177
+ results: [{ title: 'Test', url: 'https://example.com', description: 'Test' }]
178
+ });
179
+ const executeSearch = async () => {
180
+ const result = await mockDirectWebSearch('test query', {
181
+ locale: 'us-en',
182
+ safeSearch: 'moderate',
183
+ });
184
+ return {
185
+ results: result.results,
186
+ noResults: result.results.length === 0,
187
+ };
188
+ };
189
+ for (let i = 0; i < 5; i++) {
190
+ await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
191
+ }
192
+ const metrics = manager.getMetrics();
193
+ expect(metrics.totalRequests).toBe(5);
194
+ expect(metrics.emptyResponses).toBe(0);
195
+ expect(metrics.consecutiveEmptyResponses).toBe(0);
196
+ });
197
+ it('should count empty responses exactly once per request', async () => {
198
+ const manager = new reliabilityManager_1.ReliabilityManager({
199
+ maxRetries: 0,
200
+ emptyResultThreshold: 10,
201
+ });
202
+ mockDirectWebSearch.mockResolvedValue({ results: [] });
203
+ const executeSearch = async () => {
204
+ const result = await mockDirectWebSearch('test query', {
205
+ locale: 'us-en',
206
+ safeSearch: 'moderate',
207
+ });
208
+ return {
209
+ results: result.results,
210
+ noResults: result.results.length === 0,
211
+ };
212
+ };
213
+ for (let i = 0; i < 7; i++) {
214
+ await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
215
+ }
216
+ const metrics = manager.getMetrics();
217
+ expect(metrics.totalRequests).toBe(7);
218
+ expect(metrics.emptyResponses).toBe(7);
219
+ expect(metrics.consecutiveEmptyResponses).toBe(7);
220
+ });
221
+ it('should trip circuit breaker at exact configured threshold', async () => {
222
+ const manager = new reliabilityManager_1.ReliabilityManager({
223
+ failureThreshold: 10,
224
+ resetTimeoutMs: 60000,
225
+ maxRetries: 0,
226
+ });
227
+ mockDirectWebSearch.mockRejectedValue(new Error('Network error'));
228
+ const executeSearch = async () => {
229
+ const result = await mockDirectWebSearch('test query', {
230
+ locale: 'us-en',
231
+ safeSearch: 'moderate',
232
+ });
233
+ return {
234
+ results: result.results,
235
+ noResults: result.results.length === 0,
236
+ };
237
+ };
238
+ for (let i = 0; i < 5; i++) {
239
+ try {
240
+ await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
241
+ }
242
+ catch (error) {
243
+ }
244
+ }
245
+ expect(manager.getCircuitState()).toBe('closed');
246
+ const metricsAfter5 = manager.getMetrics();
247
+ expect(metricsAfter5.totalRequests).toBe(5);
248
+ for (let i = 0; i < 5; i++) {
249
+ try {
250
+ await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
251
+ }
252
+ catch (error) {
253
+ }
254
+ }
255
+ expect(manager.getCircuitState()).toBe('open');
256
+ const metricsAfter10 = manager.getMetrics();
257
+ expect(metricsAfter10.totalRequests).toBe(10);
258
+ expect(metricsAfter10.circuitBreakerTrips).toBeGreaterThan(0);
259
+ });
260
+ it('should trigger backoff at exact configured threshold', async () => {
261
+ const manager = new reliabilityManager_1.ReliabilityManager({
262
+ emptyResultThreshold: 2,
263
+ initialBackoffMs: 500,
264
+ maxBackoffMs: 5000,
265
+ minJitterMs: 0,
266
+ maxJitterMs: 0,
267
+ maxRetries: 0,
268
+ });
269
+ mockDirectWebSearch.mockResolvedValue({ results: [] });
270
+ const executeSearch = async () => {
271
+ const result = await mockDirectWebSearch('test query', {
272
+ locale: 'us-en',
273
+ safeSearch: 'moderate',
274
+ });
275
+ return {
276
+ results: result.results,
277
+ noResults: result.results.length === 0,
278
+ };
279
+ };
280
+ await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
281
+ let metricsAfter1 = manager.getMetrics();
282
+ expect(metricsAfter1.consecutiveEmptyResponses).toBe(1);
283
+ expect(metricsAfter1.backoffActivations).toBe(0);
284
+ await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
285
+ let metricsAfter2 = manager.getMetrics();
286
+ expect(metricsAfter2.consecutiveEmptyResponses).toBe(2);
287
+ expect(metricsAfter2.backoffActivations).toBe(0);
288
+ const startTime = Date.now();
289
+ await manager.executeWithRetry(executeSearch, (res) => res.results && res.results.length > 0, 'test search');
290
+ const elapsed = Date.now() - startTime;
291
+ const metricsAfter3 = manager.getMetrics();
292
+ expect(metricsAfter3.consecutiveEmptyResponses).toBe(3);
293
+ expect(metricsAfter3.backoffActivations).toBe(1);
294
+ expect(elapsed).toBeGreaterThanOrEqual(500);
295
+ });
296
+ });
297
+ });
@@ -9,17 +9,23 @@ var CircuitState;
9
9
  })(CircuitState = exports.CircuitState || (exports.CircuitState = {}));
10
10
  class ReliabilityManager {
11
11
  constructor(config) {
12
+ const isTest = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined;
13
+ const defaultRetryDelay = isTest ? 1 : 1000;
14
+ const defaultInitialBackoff = isTest ? 1 : 1000;
15
+ const defaultMaxBackoff = isTest ? 10 : 30000;
16
+ const defaultMinJitter = isTest ? 0 : 100;
17
+ const defaultMaxJitter = isTest ? 1 : 500;
12
18
  this.config = {
13
19
  emptyResultThreshold: 3,
14
- initialBackoffMs: 1000,
15
- maxBackoffMs: 30000,
20
+ initialBackoffMs: defaultInitialBackoff,
21
+ maxBackoffMs: defaultMaxBackoff,
16
22
  backoffMultiplier: 2,
17
- minJitterMs: 100,
18
- maxJitterMs: 500,
23
+ minJitterMs: defaultMinJitter,
24
+ maxJitterMs: defaultMaxJitter,
19
25
  failureThreshold: 5,
20
26
  resetTimeoutMs: 60000,
21
27
  maxRetries: 3,
22
- retryDelayMs: 1000,
28
+ retryDelayMs: defaultRetryDelay,
23
29
  ...config,
24
30
  };
25
31
  this.metrics = {
@@ -190,7 +196,10 @@ class ReliabilityManager {
190
196
  }
191
197
  attempt++;
192
198
  }
193
- throw lastError || new Error('Max retries exceeded');
199
+ if (lastError) {
200
+ throw lastError;
201
+ }
202
+ throw new Error('Max retries exceeded');
194
203
  }
195
204
  getSummary() {
196
205
  const { metrics, circuitState } = this;
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-duckduckgo-search",
3
- "version": "31.0.0",
3
+ "version": "31.0.1",
4
4
  "description": "Production-grade, AI Agent-ready n8n community node for DuckDuckGo search. Features enterprise reliability with adaptive backoff, circuit breaking, and intelligent rate limiting. Search the web, images, news, and videos with privacy-focused, highly reliable results.",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-duckduckgo-search",
3
- "version": "31.0.0",
3
+ "version": "31.0.1",
4
4
  "description": "Production-grade, AI Agent-ready n8n community node for DuckDuckGo search. Features enterprise reliability with adaptive backoff, circuit breaking, and intelligent rate limiting. Search the web, images, news, and videos with privacy-focused, highly reliable results.",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",