n8n-nodes-duckduckgo-search 32.2.0 → 32.3.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DuckDuckGo Search Node for n8n
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/n8n-nodes-duckduckgo-search.svg?v=32.2.0)](https://www.npmjs.com/package/n8n-nodes-duckduckgo-search)
3
+ [![npm version](https://img.shields.io/npm/v/n8n-nodes-duckduckgo-search.svg?v=32.3.0)](https://www.npmjs.com/package/n8n-nodes-duckduckgo-search)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
6
  A professional-grade n8n community node for DuckDuckGo search with enterprise-level error handling, validation, and quality assurance. Search the web, find images, discover news, and explore videos with privacy-focused, reliable results.
@@ -243,12 +243,13 @@ class DuckDuckGo {
243
243
  const performSearch = async () => {
244
244
  var _a, _b, _c, _d;
245
245
  const timeout = 45000;
246
+ const needleOptions = (0, constants_1.getNeedleOptions)();
246
247
  if (operation === types_1.DuckDuckGoOperation.Search) {
247
248
  const searchResult = await (0, reliability_1.executeWithRetry)(() => (0, reliability_1.withTimeout)((0, duck_duck_scrape_1.search)(query, {
248
249
  safeSearch: getSafeSearchType(safeSearch),
249
250
  locale: region || locale,
250
251
  time: getSearchTimeType(timePeriod),
251
- }), timeout, 'Web Search'), `Web Search: "${query}"`, reliability_1.DEFAULT_RETRY_CONFIG);
252
+ }, needleOptions), timeout, 'Web Search'), `Web Search: "${query}"`, reliability_1.DEFAULT_RETRY_CONFIG);
252
253
  if ((_a = searchResult === null || searchResult === void 0 ? void 0 : searchResult.results) === null || _a === void 0 ? void 0 : _a.length) {
253
254
  results = (0, processors_1.processWebSearchResults)(searchResult.results, itemIndex, searchResult).slice(0, maxResults);
254
255
  }
@@ -269,7 +270,7 @@ class DuckDuckGo {
269
270
  const searchResult = await (0, reliability_1.executeWithRetry)(() => (0, reliability_1.withTimeout)((0, duck_duck_scrape_1.searchImages)(query, {
270
271
  safeSearch: getSafeSearchType(safeSearch),
271
272
  locale: region || locale,
272
- }), timeout, 'Image Search'), `Image Search: "${query}"`, reliability_1.DEFAULT_RETRY_CONFIG);
273
+ }, needleOptions), timeout, 'Image Search'), `Image Search: "${query}"`, reliability_1.DEFAULT_RETRY_CONFIG);
273
274
  if ((_b = searchResult === null || searchResult === void 0 ? void 0 : searchResult.results) === null || _b === void 0 ? void 0 : _b.length) {
274
275
  results = (0, processors_1.processImageSearchResults)(searchResult.results, itemIndex).slice(0, maxResults);
275
276
  }
@@ -291,7 +292,7 @@ class DuckDuckGo {
291
292
  safeSearch: getSafeSearchType(safeSearch),
292
293
  locale: region || locale,
293
294
  time: getSearchTimeType(timePeriod),
294
- }), timeout, 'News Search'), `News Search: "${query}"`, reliability_1.DEFAULT_RETRY_CONFIG);
295
+ }, needleOptions), timeout, 'News Search'), `News Search: "${query}"`, reliability_1.DEFAULT_RETRY_CONFIG);
295
296
  if ((_c = searchResult === null || searchResult === void 0 ? void 0 : searchResult.results) === null || _c === void 0 ? void 0 : _c.length) {
296
297
  results = (0, processors_1.processNewsSearchResults)(searchResult.results, itemIndex).slice(0, maxResults);
297
298
  }
@@ -312,7 +313,7 @@ class DuckDuckGo {
312
313
  const searchResult = await (0, reliability_1.executeWithRetry)(() => (0, reliability_1.withTimeout)((0, duck_duck_scrape_1.searchVideos)(query, {
313
314
  safeSearch: getSafeSearchType(safeSearch),
314
315
  locale: region || locale,
315
- }), timeout, 'Video Search'), `Video Search: "${query}"`, reliability_1.DEFAULT_RETRY_CONFIG);
316
+ }, needleOptions), timeout, 'Video Search'), `Video Search: "${query}"`, reliability_1.DEFAULT_RETRY_CONFIG);
316
317
  if ((_d = searchResult === null || searchResult === void 0 ? void 0 : searchResult.results) === null || _d === void 0 ? void 0 : _d.length) {
317
318
  results = (0, processors_1.processVideoSearchResults)(searchResult.results, itemIndex).slice(0, maxResults);
318
319
  }
@@ -25,6 +25,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  const DuckDuckGo_node_1 = require("../DuckDuckGo.node");
27
27
  const duckDuckScrape = __importStar(require("duck-duck-scrape"));
28
+ jest.mock('../adaptiveRateLimiter', () => ({
29
+ getAdaptiveRateLimiter: jest.fn().mockReturnValue({
30
+ wait: jest.fn().mockResolvedValue(undefined),
31
+ reportSuccess: jest.fn(),
32
+ reportRateLimitFailure: jest.fn(),
33
+ reportOtherFailure: jest.fn(),
34
+ }),
35
+ resetAdaptiveRateLimiter: jest.fn(),
36
+ }));
28
37
  jest.mock('duck-duck-scrape', () => ({
29
38
  search: jest.fn(),
30
39
  searchNews: jest.fn(),
@@ -95,7 +104,9 @@ describe('DuckDuckGo Node', () => {
95
104
  setupNodeParameters('search', 'test query');
96
105
  duckDuckScrape.search.mockResolvedValue(mockResults);
97
106
  const result = await duckDuckGoNode.execute.call(mockExecuteFunction);
98
- expect(duckDuckScrape.search).toHaveBeenCalledWith('test query', expect.any(Object));
107
+ expect(duckDuckScrape.search).toHaveBeenCalledWith('test query', expect.any(Object), expect.objectContaining({
108
+ headers: expect.any(Object),
109
+ }));
99
110
  expect(result).toHaveLength(1);
100
111
  expect(result[0]).toHaveLength(2);
101
112
  expect(result[0][0].json).toHaveProperty('title', 'Result 1');
@@ -131,7 +142,9 @@ describe('DuckDuckGo Node', () => {
131
142
  setupNodeParameters('searchImages', 'cat');
132
143
  duckDuckScrape.searchImages.mockResolvedValue(mockResults);
133
144
  const result = await duckDuckGoNode.execute.call(mockExecuteFunction);
134
- expect(duckDuckScrape.searchImages).toHaveBeenCalledWith('cat', expect.any(Object));
145
+ expect(duckDuckScrape.searchImages).toHaveBeenCalledWith('cat', expect.any(Object), expect.objectContaining({
146
+ headers: expect.any(Object),
147
+ }));
135
148
  expect(result[0][0].json).toHaveProperty('sourceType', 'image');
136
149
  expect(result[0][0].json).toHaveProperty('imageUrl', 'https://example.com/img1.jpg');
137
150
  });
@@ -146,7 +159,9 @@ describe('DuckDuckGo Node', () => {
146
159
  setupNodeParameters('searchNews', 'tech news');
147
160
  duckDuckScrape.searchNews.mockResolvedValue(mockResults);
148
161
  const result = await duckDuckGoNode.execute.call(mockExecuteFunction);
149
- expect(duckDuckScrape.searchNews).toHaveBeenCalledWith('tech news', expect.any(Object));
162
+ expect(duckDuckScrape.searchNews).toHaveBeenCalledWith('tech news', expect.any(Object), expect.objectContaining({
163
+ headers: expect.any(Object),
164
+ }));
150
165
  expect(result[0][0].json).toHaveProperty('sourceType', 'news');
151
166
  });
152
167
  });
@@ -160,7 +175,9 @@ describe('DuckDuckGo Node', () => {
160
175
  setupNodeParameters('searchVideos', 'tutorial');
161
176
  duckDuckScrape.searchVideos.mockResolvedValue(mockResults);
162
177
  const result = await duckDuckGoNode.execute.call(mockExecuteFunction);
163
- expect(duckDuckScrape.searchVideos).toHaveBeenCalledWith('tutorial', expect.any(Object));
178
+ expect(duckDuckScrape.searchVideos).toHaveBeenCalledWith('tutorial', expect.any(Object), expect.objectContaining({
179
+ headers: expect.any(Object),
180
+ }));
164
181
  expect(result[0][0].json).toHaveProperty('sourceType', 'video');
165
182
  });
166
183
  });
@@ -171,7 +188,7 @@ describe('DuckDuckGo Node', () => {
171
188
  await duckDuckGoNode.execute.call(mockExecuteFunction);
172
189
  expect(duckDuckScrape.search).toHaveBeenCalledWith('test', expect.objectContaining({
173
190
  locale: 'fr-fr',
174
- }));
191
+ }), expect.any(Object));
175
192
  });
176
193
  it('should limit results to maxResults', async () => {
177
194
  setupNodeParameters('search', 'test', { maxResults: 2 });
@@ -185,4 +202,18 @@ describe('DuckDuckGo Node', () => {
185
202
  expect(result[0]).toHaveLength(2);
186
203
  });
187
204
  });
205
+ describe('Needle Options', () => {
206
+ it('should pass custom headers with User-Agent to search functions', async () => {
207
+ setupNodeParameters('search', 'test');
208
+ duckDuckScrape.search.mockResolvedValue({ results: [] });
209
+ await duckDuckGoNode.execute.call(mockExecuteFunction);
210
+ const callArgs = duckDuckScrape.search.mock.calls[0];
211
+ const needleOptions = callArgs[2];
212
+ expect(needleOptions).toHaveProperty('headers');
213
+ expect(needleOptions.headers).toHaveProperty('user-agent');
214
+ expect(needleOptions.headers).toHaveProperty('sec-ch-ua');
215
+ expect(needleOptions.headers).toHaveProperty('accept');
216
+ expect(needleOptions).toHaveProperty('response_timeout', 30000);
217
+ });
218
+ });
188
219
  });
@@ -3,14 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resetOperationRateLimiter = exports.resetAdaptiveRateLimiter = exports.getAdaptiveRateLimiter = exports.AdaptiveRateLimiter = exports.OPERATION_RATE_LIMITS = void 0;
4
4
  exports.OPERATION_RATE_LIMITS = {
5
5
  search: {
6
- initialDelay: 3000,
7
- minDelay: 2500,
8
- maxDelay: 10000,
6
+ initialDelay: 5000,
7
+ minDelay: 4000,
8
+ maxDelay: 15000,
9
9
  },
10
10
  searchImages: {
11
- initialDelay: 3000,
12
- minDelay: 2500,
13
- maxDelay: 10000,
11
+ initialDelay: 5000,
12
+ minDelay: 4000,
13
+ maxDelay: 15000,
14
14
  },
15
15
  searchNews: {
16
16
  initialDelay: 2000,
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getRandomUserAgent = exports.USER_AGENTS = exports.NODE_INFO = exports.DEFAULT_PARAMETERS = exports.REGIONS = void 0;
3
+ exports.getNeedleOptions = exports.getRandomUserAgent = exports.USER_AGENTS = exports.NODE_INFO = exports.DEFAULT_PARAMETERS = exports.REGIONS = void 0;
4
4
  exports.REGIONS = [
5
5
  { name: 'Argentina', value: 'ar-es' },
6
6
  { name: 'Australia', value: 'au-en' },
@@ -83,3 +83,30 @@ function getRandomUserAgent() {
83
83
  return exports.USER_AGENTS[Math.floor(Math.random() * exports.USER_AGENTS.length)];
84
84
  }
85
85
  exports.getRandomUserAgent = getRandomUserAgent;
86
+ function getNeedleOptions() {
87
+ const userAgent = getRandomUserAgent();
88
+ const chromeVersionMatch = userAgent.match(/Chrome\/(\d+)/);
89
+ const chromeVersion = chromeVersionMatch ? chromeVersionMatch[1] : '121';
90
+ return {
91
+ headers: {
92
+ 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
93
+ 'accept-language': 'en-US,en;q=0.9',
94
+ 'cache-control': 'no-cache',
95
+ 'pragma': 'no-cache',
96
+ 'sec-ch-ua': `"Not A(Brand";v="8", "Chromium";v="${chromeVersion}", "Google Chrome";v="${chromeVersion}"`,
97
+ 'sec-ch-ua-mobile': '?0',
98
+ 'sec-ch-ua-platform': '"Windows"',
99
+ 'sec-fetch-dest': 'document',
100
+ 'sec-fetch-mode': 'navigate',
101
+ 'sec-fetch-site': 'none',
102
+ 'sec-fetch-user': '?1',
103
+ 'sec-gpc': '1',
104
+ 'upgrade-insecure-requests': '1',
105
+ 'user-agent': userAgent,
106
+ },
107
+ follow_max: 5,
108
+ response_timeout: 30000,
109
+ read_timeout: 30000,
110
+ };
111
+ }
112
+ exports.getNeedleOptions = getNeedleOptions;
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-duckduckgo-search",
3
- "version": "32.2.0",
3
+ "version": "32.3.0",
4
4
  "description": "Professional-grade n8n community node for DuckDuckGo search with comprehensive error handling, validation, and quality filtering.",
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": "32.2.0",
3
+ "version": "32.3.0",
4
4
  "description": "Professional-grade n8n community node for DuckDuckGo search with comprehensive error handling, validation, and quality filtering.",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",