UNPKG

n8n-nodes-duckduckgo-search

Version:

Integrate DuckDuckGo search seamlessly into your n8n workflows with advanced pagination and human-like behavior. Get more results without hitting rate limits.

983 lines (982 loc) 89.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DuckDuckGo = void 0; const n8n_workflow_1 = require("n8n-workflow"); const duck_duck_scrape_1 = require("duck-duck-scrape"); const types_1 = require("./types"); const processors_1 = require("./processors"); const constants_1 = require("./constants"); const utils_1 = require("./utils"); const cache_1 = require("./cache"); const telemetry_1 = require("./telemetry"); const LOCALE_OPTIONS = [ { name: 'English (US)', value: 'en-us' }, { name: 'English (UK)', value: 'uk-en' }, { name: 'Spanish (Spain)', value: 'es-es' }, { name: 'French (France)', value: 'fr-fr' }, { name: 'German (Germany)', value: 'de-de' }, { name: 'Italian (Italy)', value: 'it-it' }, { name: 'Japanese (Japan)', value: 'jp-jp' }, { name: 'Russian (Russia)', value: 'ru-ru' }, { name: 'Chinese (China)', value: 'zh-cn' }, { name: 'Portuguese (Brazil)', value: 'br-pt' }, { name: 'Dutch (Netherlands)', value: 'nl-nl' }, { name: 'Polish (Poland)', value: 'pl-pl' }, { name: 'Swedish (Sweden)', value: 'se-sv' }, { name: 'Korean (Korea)', value: 'kr-ko' }, { name: 'Turkish (Turkey)', value: 'tr-tr' }, { name: 'Arabic (Saudi Arabia)', value: 'sa-ar' }, { name: 'Hebrew (Israel)', value: 'il-he' }, { name: 'Persian (Iran)', value: 'ir-fa' }, ]; function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function getRandomUserAgent() { const userAgents = [ 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1', 'Mozilla/5.0 (Linux; Android 11; SM-G998U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.98 Mobile Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 12_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/117.0', ]; return userAgents[Math.floor(Math.random() * userAgents.length)]; } class DuckDuckGo { constructor() { this.description = { displayName: constants_1.NODE_INFO.DISPLAY_NAME, name: constants_1.NODE_INFO.NAME, icon: 'file:duckduckgo.svg', group: ['transform'], version: constants_1.NODE_INFO.VERSION, subtitle: '={{$parameter["operation"]}}', description: constants_1.NODE_INFO.DESCRIPTION, defaults: { name: constants_1.NODE_INFO.DISPLAY_NAME, }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'duckDuckGoApi', required: false, displayOptions: { show: { useApiKey: [true], }, }, }, ], properties: [ { displayName: 'Operation', name: 'operation', type: 'options', default: types_1.DuckDuckGoOperation.Search, noDataExpression: true, required: true, description: 'Type of search to perform with DuckDuckGo', options: [ { name: 'Web Search', value: types_1.DuckDuckGoOperation.Search, description: 'Search websites and web content', action: 'Search the web', }, { name: 'Image Search', value: types_1.DuckDuckGoOperation.SearchImages, description: 'Find images across the web', action: 'Search for images', }, { name: 'News Search', value: types_1.DuckDuckGoOperation.SearchNews, description: 'Discover news articles and updates', action: 'Search for news', }, { name: 'Video Search', value: types_1.DuckDuckGoOperation.SearchVideos, description: 'Find videos from various sources', action: 'Search for videos', } ], }, { displayName: 'Locale', name: 'locale', type: 'options', default: 'en-us', description: 'Specify the search language/region locale', options: LOCALE_OPTIONS, }, { displayName: 'Use API Key', name: 'useApiKey', type: 'boolean', default: false, description: 'Whether to use API key authentication for DuckDuckGo API access', }, { displayName: 'Search Query', name: 'query', type: 'string', required: true, default: '', description: 'The search terms to look for on the web', placeholder: 'Enter your search query', displayOptions: { show: { operation: [ types_1.DuckDuckGoOperation.Search, ], }, }, typeOptions: { minLength: 1, }, }, { displayName: 'Web Search Options', name: 'webSearchOptions', type: 'collection', placeholder: 'Add Option', default: { maxResults: constants_1.DEFAULT_PARAMETERS.MAX_RESULTS, region: constants_1.DEFAULT_PARAMETERS.REGION, safeSearch: constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH, }, displayOptions: { show: { operation: [ types_1.DuckDuckGoOperation.Search, ], }, }, options: [ { displayName: 'Maximum Results', name: 'maxResults', type: 'number', default: constants_1.DEFAULT_PARAMETERS.MAX_RESULTS, description: 'Maximum number of search results to return', typeOptions: { minValue: 1, maxValue: 100, }, }, { displayName: 'Region', name: 'region', type: 'options', default: constants_1.DEFAULT_PARAMETERS.REGION, description: 'The region to target for web search results', options: constants_1.REGIONS, }, { displayName: 'Safe Search', name: 'safeSearch', type: 'options', default: constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH, description: 'Content filtering level for web search', options: [ { name: 'Strict', value: types_1.SafeSearchLevel.Strict, description: 'Filter explicit content', }, { name: 'Moderate', value: types_1.SafeSearchLevel.Moderate, description: 'Default filtering level', }, { name: 'Off', value: types_1.SafeSearchLevel.Off, description: 'No content filtering', } ], }, { displayName: 'Time Period', name: 'timePeriod', type: 'options', default: types_1.TimePeriod.AllTime, description: 'Time range for web search results', options: [ { name: 'All Time', value: types_1.TimePeriod.AllTime, description: 'No time restriction', }, { name: 'Past Day', value: types_1.TimePeriod.PastDay, description: 'Last 24 hours', }, { name: 'Past Week', value: types_1.TimePeriod.PastWeek, description: 'Last 7 days', }, { name: 'Past Month', value: types_1.TimePeriod.PastMonth, description: 'Last 30 days', }, { name: 'Past Year', value: types_1.TimePeriod.PastYear, description: 'Last 365 days', }, ], }, { displayName: 'Return Raw Results', name: 'returnRawResults', type: 'boolean', default: false, description: 'Whether to return the raw API response instead of processed results', }, ], }, { displayName: 'Image Search Query', name: 'imageQuery', type: 'string', required: true, default: '', description: 'The search terms to find images for', placeholder: 'Enter image search query', displayOptions: { show: { operation: [ types_1.DuckDuckGoOperation.SearchImages, ], }, }, typeOptions: { minLength: 1, }, }, { displayName: 'Image Search Options', name: 'imageSearchOptions', type: 'collection', placeholder: 'Add Option', default: { maxResults: constants_1.DEFAULT_PARAMETERS.MAX_RESULTS, region: constants_1.DEFAULT_PARAMETERS.REGION, safeSearch: constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH, }, displayOptions: { show: { operation: [ types_1.DuckDuckGoOperation.SearchImages, ], }, }, options: [ { displayName: 'Maximum Results', name: 'maxResults', type: 'number', default: constants_1.DEFAULT_PARAMETERS.MAX_RESULTS, description: 'Maximum number of image results to return', typeOptions: { minValue: 1, maxValue: 100, }, }, { displayName: 'Region', name: 'region', type: 'options', default: constants_1.DEFAULT_PARAMETERS.REGION, description: 'The region to target for image search', options: constants_1.REGIONS, }, { displayName: 'Safe Search', name: 'safeSearch', type: 'options', default: constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH, description: 'Content filtering level for images', options: [ { name: 'Strict', value: types_1.SafeSearchLevel.Strict, description: 'Filter explicit images', }, { name: 'Moderate', value: types_1.SafeSearchLevel.Moderate, description: 'Default filtering level', }, { name: 'Off', value: types_1.SafeSearchLevel.Off, description: 'No image filtering', } ], }, { displayName: 'Return Raw Results', name: 'returnRawResults', type: 'boolean', default: false, description: 'Whether to return the raw API response instead of processed results', }, ], }, { displayName: 'News Search Query', name: 'newsQuery', type: 'string', required: true, default: '', description: 'The search terms to find news articles for', placeholder: 'Enter news search query', displayOptions: { show: { operation: [ types_1.DuckDuckGoOperation.SearchNews, ], }, }, typeOptions: { minLength: 1, }, }, { displayName: 'News Search Options', name: 'newsSearchOptions', type: 'collection', placeholder: 'Add Option', default: { maxResults: constants_1.DEFAULT_PARAMETERS.MAX_RESULTS, region: constants_1.DEFAULT_PARAMETERS.REGION, safeSearch: constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH, }, displayOptions: { show: { operation: [ types_1.DuckDuckGoOperation.SearchNews, ], }, }, options: [ { displayName: 'Maximum Results', name: 'maxResults', type: 'number', default: constants_1.DEFAULT_PARAMETERS.MAX_RESULTS, description: 'Maximum number of news articles to return', typeOptions: { minValue: 1, maxValue: 100, }, }, { displayName: 'Region', name: 'region', type: 'options', default: constants_1.DEFAULT_PARAMETERS.REGION, description: 'The region to target for news', options: constants_1.REGIONS, }, { displayName: 'Safe Search', name: 'safeSearch', type: 'options', default: constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH, description: 'Content filtering level for news articles', options: [ { name: 'Strict', value: types_1.SafeSearchLevel.Strict, description: 'Filter explicit content', }, { name: 'Moderate', value: types_1.SafeSearchLevel.Moderate, description: 'Default filtering level', }, { name: 'Off', value: types_1.SafeSearchLevel.Off, description: 'No content filtering', } ], }, { displayName: 'Time Period', name: 'timePeriod', type: 'options', default: types_1.TimePeriod.PastWeek, description: 'Time range for news articles', options: [ { name: 'All Time', value: types_1.TimePeriod.AllTime, description: 'No time restriction', }, { name: 'Past Day', value: types_1.TimePeriod.PastDay, description: 'Last 24 hours', }, { name: 'Past Week', value: types_1.TimePeriod.PastWeek, description: 'Last 7 days', }, { name: 'Past Month', value: types_1.TimePeriod.PastMonth, description: 'Last 30 days', }, { name: 'Past Year', value: types_1.TimePeriod.PastYear, description: 'Last 365 days', }, ], }, { displayName: 'Return Raw Results', name: 'returnRawResults', type: 'boolean', default: false, description: 'Whether to return the raw API response instead of processed results', }, ], }, { displayName: 'Video Search Query', name: 'videoQuery', type: 'string', required: true, default: '', description: 'The search terms to find videos for', placeholder: 'Enter video search query', displayOptions: { show: { operation: [ types_1.DuckDuckGoOperation.SearchVideos, ], }, }, typeOptions: { minLength: 1, }, }, { displayName: 'Video Search Options', name: 'videoSearchOptions', type: 'collection', placeholder: 'Add Option', default: { maxResults: constants_1.DEFAULT_PARAMETERS.MAX_RESULTS, region: constants_1.DEFAULT_PARAMETERS.REGION, safeSearch: constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH, }, displayOptions: { show: { operation: [ types_1.DuckDuckGoOperation.SearchVideos, ], }, }, options: [ { displayName: 'Maximum Results', name: 'maxResults', type: 'number', default: constants_1.DEFAULT_PARAMETERS.MAX_RESULTS, description: 'Maximum number of videos to return', typeOptions: { minValue: 1, maxValue: 100, }, }, { displayName: 'Region', name: 'region', type: 'options', default: constants_1.DEFAULT_PARAMETERS.REGION, description: 'The region to target for videos', options: constants_1.REGIONS, }, { displayName: 'Safe Search', name: 'safeSearch', type: 'options', default: constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH, description: 'Content filtering level for videos', options: [ { name: 'Strict', value: types_1.SafeSearchLevel.Strict, description: 'Filter explicit videos', }, { name: 'Moderate', value: types_1.SafeSearchLevel.Moderate, description: 'Default filtering level', }, { name: 'Off', value: types_1.SafeSearchLevel.Off, description: 'No video filtering', } ], }, { displayName: 'Return Raw Results', name: 'returnRawResults', type: 'boolean', default: false, description: 'Whether to return the raw API response instead of processed results', }, ], }, { displayName: 'Error Handling', name: 'errorHandling', type: 'options', default: 'continueOnFail', description: 'How errors should be handled', options: [ { name: 'Break on Error', value: 'breakOnError', description: 'Stop executing when an error occurs', }, { name: 'Continue on Error', value: 'continueOnFail', description: 'Continue execution even when errors occur', }, ], }, { displayName: 'Debug Mode', name: 'debugMode', type: 'boolean', default: false, description: 'When enabled, includes detailed request and response information for troubleshooting', }, { displayName: 'Cache Settings', name: 'cacheSettings', type: 'collection', placeholder: 'Add Cache Setting', default: { cacheTTL: 300, enableCache: true, }, options: [ { displayName: 'Enable Cache', name: 'enableCache', type: 'boolean', default: true, description: 'Whether to cache search results to improve performance for repeated queries', }, { displayName: 'Cache TTL', name: 'cacheTTL', type: 'number', default: 300, description: 'Time in seconds to keep results in cache (Time-To-Live)', typeOptions: { minValue: 60, maxValue: 86400, }, }, ], }, { displayName: 'Enable Telemetry', name: 'enableTelemetry', type: 'boolean', default: false, description: 'Whether to send anonymous usage data to help improve the node (no personal data is collected)', }, ], }; } async webSearchWithSuperPagination(query, options) { var _a, _b; const { parseHtmlResults } = await Promise.resolve().then(() => __importStar(require('./htmlParser'))); const pageSizeJson = 10; const pageSizeHtml = 10; const results = []; let vqd; let hasMoreJson = true; let hasMoreHtml = true; let offsetHtml = 0; let pageJson = 0; try { const baseOpts = { safeSearch: options.safeSearch, locale: options.locale, api: '/d.js', o: 'json', v: 'l', }; if (options.timePeriod) { baseOpts.timePeriod = options.timePeriod; } const first = await (0, duck_duck_scrape_1.search)(query, baseOpts); if ((_a = first.results) === null || _a === void 0 ? void 0 : _a.length) { results.push(...first.results); } vqd = first.vqd; await sleep(300); } catch (error) { hasMoreJson = false; } while (results.length < options.maxResults && hasMoreJson && vqd) { pageJson++; if (pageJson > 5) break; const opts = { safeSearch: options.safeSearch, locale: options.locale, api: '/d.js', o: 'json', v: 'l', s: (pageJson * pageSizeJson).toString(), dc: ((pageJson * pageSizeJson) + 1).toString(), vqd, }; try { const nextPage = await (0, duck_duck_scrape_1.search)(query, opts); if (!((_b = nextPage.results) === null || _b === void 0 ? void 0 : _b.length)) { hasMoreJson = false; break; } results.push(...nextPage.results); } catch (error) { hasMoreJson = false; break; } await sleep(300); } while (results.length < options.maxResults && hasMoreHtml) { const htmlUrl = `https://duckduckgo.com/html/?q=${encodeURIComponent(query)}&s=${offsetHtml}`; try { const html = await this.helpers.httpRequest({ url: htmlUrl, method: 'GET', headers: { 'User-Agent': getRandomUserAgent(), }, }); const page = parseHtmlResults(html); if (!page.length) { hasMoreHtml = false; break; } const convertedResults = page.map(item => ({ title: item.title, url: item.url, description: item.snippet, rawDescription: item.snippet, hostname: new URL(item.url).hostname, icon: '', })); results.push(...convertedResults); if (page.length < pageSizeHtml) { hasMoreHtml = false; } offsetHtml += pageSizeHtml; } catch (error) { hasMoreHtml = false; break; } await sleep(300); } return results.slice(0, options.maxResults); } async execute() { 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, _8; const items = this.getInputData(); const returnData = []; const debugMode = this.getNodeParameter('debugMode', 0, false); const enableTelemetry = this.getNodeParameter('enableTelemetry', 0, false); const cacheSettings = this.getNodeParameter('cacheSettings', 0, { enableCache: true, cacheTTL: 300, }); const useApiKey = this.getNodeParameter('useApiKey', 0, false); let apiKey; if (useApiKey) { try { const credentials = await this.getCredentials('duckDuckGoApi'); apiKey = credentials.apiKey; } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'API key is required when "Use API Key" is enabled!'); } } const enableCache = (cacheSettings === null || cacheSettings === void 0 ? void 0 : cacheSettings.enableCache) !== false; const cacheTTL = (cacheSettings === null || cacheSettings === void 0 ? void 0 : cacheSettings.cacheTTL) || 300; const globalLocale = this.getNodeParameter('locale', 0, 'en-us'); for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { try { const operation = this.getNodeParameter('operation', itemIndex); let results = []; if (operation === types_1.DuckDuckGoOperation.Search) { const query = this.getNodeParameter('query', itemIndex); const options = this.getNodeParameter('webSearchOptions', itemIndex, {}); const maxResults = (_a = options.maxResults) !== null && _a !== void 0 ? _a : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS; const searchOptions = { maxResults: maxResults, safeSearch: (_b = options.safeSearch) !== null && _b !== void 0 ? _b : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH, locale: (_d = (_c = options.region) !== null && _c !== void 0 ? _c : globalLocale) !== null && _d !== void 0 ? _d : constants_1.DEFAULT_PARAMETERS.REGION, timePeriod: (_e = options.timePeriod) !== null && _e !== void 0 ? _e : constants_1.DEFAULT_PARAMETERS.TIME_PERIOD, }; if (apiKey) { searchOptions.headers = { Authorization: `Bearer ${apiKey}`, }; } const cacheKey = JSON.stringify({ operation, query, options: searchOptions, }); const startTime = Date.now(); const telemetryData = { operation, query, searchOptions, }; if (enableTelemetry) { await (0, telemetry_1.reportEvent)(this, 'search_started', telemetryData); } let result; if (enableCache) { const cachedResult = (0, cache_1.getCached)(cacheKey); if (cachedResult) { if (debugMode) { const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.INFO, `Cache hit for web search: ${query}`, operation, { query, options: searchOptions, cacheKey }); console.log(JSON.stringify(logEntry)); } result = cachedResult; } } try { if (!result) { if (debugMode) { const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.INFO, `Executing web search for: ${query}`, operation, { query, options: searchOptions, cacheEnabled: enableCache }); console.log(JSON.stringify(logEntry)); } result = await (0, duck_duck_scrape_1.search)(query, searchOptions); if (maxResults > constants_1.DEFAULT_PARAMETERS.MAX_RESULTS && result.results && result.results.length > 0) { try { const rawResults = await this.webSearchWithSuperPagination.call(this, query, { maxResults: maxResults, safeSearch: (_f = options.safeSearch) !== null && _f !== void 0 ? _f : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH, locale: (_h = (_g = options.region) !== null && _g !== void 0 ? _g : globalLocale) !== null && _h !== void 0 ? _h : constants_1.DEFAULT_PARAMETERS.REGION, timePeriod: (_j = options.timePeriod) !== null && _j !== void 0 ? _j : constants_1.DEFAULT_PARAMETERS.TIME_PERIOD, }); result.results = rawResults; } catch (fallbackError) { if (debugMode) { const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.ERROR, `Enhanced pagination error: ${fallbackError.message}. Falling back to standard pagination.`, operation, { query, options: searchOptions }); console.error(JSON.stringify(logEntry)); } let page = 2; const maxPages = Math.ceil(maxResults / 10); const allResults = [...result.results]; const maxPageLimit = 5; const effectiveMaxPages = Math.min(maxPages, maxPageLimit); while (allResults.length < maxResults && page <= effectiveMaxPages) { if (debugMode) { const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.INFO, `Fetching additional results (page ${page}) for: ${query}`, operation, { query, options: searchOptions, page }); console.log(JSON.stringify(logEntry)); } try { if (result.vqd) { const startPosition = (page - 1) * 30; const nextPageOptions = { ...searchOptions, s: startPosition.toString(), dc: (startPosition + 1).toString(), v: 'l', o: 'json', api: '/d.js', vqd: result.vqd, }; const nextPageResult = await (0, duck_duck_scrape_1.search)(query, nextPageOptions); if (nextPageResult.results && nextPageResult.results.length > 0) { allResults.push(...nextPageResult.results); } else { break; } } else { break; } } catch (pageError) { if (debugMode) { const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.ERROR, `Error fetching additional results: ${pageError.message}`, operation, { query, options: searchOptions, page }); console.error(JSON.stringify(logEntry)); } break; } page++; } result.results = allResults; } } if (enableCache && result) { (0, cache_1.setCache)(cacheKey, result, cacheTTL); if (debugMode) { const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.INFO, `Cached web search result for: ${query}`, operation, { query, options: searchOptions, cacheTTL, cacheKey }); console.log(JSON.stringify(logEntry)); } } } if (!result || !result.results || !result.results.length) { if (debugMode) { const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.INFO, `No results found for web search query: ${query}`, operation, { query, options: searchOptions }); console.log(JSON.stringify(logEntry)); } results = [{ json: { success: true, query, count: 0, results: [], ...(debugMode ? { requestOptions: searchOptions, fromCache: result !== undefined } : {}), }, pairedItem: { item: itemIndex, }, }]; if (enableTelemetry) { await (0, telemetry_1.reportEvent)(this, 'search_completed', { operation, query, durationMs: Date.now() - startTime, resultCount: 0, fromCache: !!result, }); } } else { if (options.returnRawResults || debugMode) { results = [{ json: { success: true, query, result, ...(debugMode ? { requestOptions: searchOptions, fromCache: result !== undefined } : {}), }, pairedItem: { item: itemIndex, }, }]; } else { const maxResults = (_k = options.maxResults) !== null && _k !== void 0 ? _k : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS; results = (0, processors_1.processWebSearchResults)(result.results, itemIndex).slice(0, maxResults); if (debugMode && results.length > 0) { results[0].json.fromCache = result !== undefined; } } const resultCount = Array.isArray(result.results) ? result.results.length : 0; if (enableTelemetry) { await (0, telemetry_1.reportEvent)(this, 'search_completed', { operation, query, durationMs: Date.now() - startTime, resultCount, fromCache: result !== undefined && !result.fromCache, }); } } } catch (error) { const errorMessage = (0, utils_1.parseApiError)(error instanceof Error ? error : new Error(String(error)), 'web search'); if (debugMode) { const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.ERROR, `Web search error: ${errorMessage}`, operation, { query, options: searchOptions }, error instanceof Error ? error : new Error(String(error))); console.error(JSON.stringify(logEntry)); } results = [{ json: { success: false, error: errorMessage, query, ...(debugMode ? { errorDetails: error instanceof Error ? error.stack : String(error), requestOptions: searchOptions, } : {}), }, pairedItem: { item: itemIndex, }, }]; if (enableTelemetry) { await (0, telemetry_1.reportEvent)(this, 'search_failed', { operation, query, durationMs: Date.now() - startTime, error: errorMessage, errorType: ((_l = error === null || error === void 0 ? void 0 : error.constructor) === null || _l === void 0 ? void 0 : _l.name) || 'Error', }); } } } else if (operation === types_1.DuckDuckGoOperation.SearchImages) { const imageQuery = this.getNodeParameter('imageQuery', itemIndex); const imageSearchOptions = this.getNodeParameter('imageSearchOptions', itemIndex, {}); const maxResults = (_m = imageSearchOptions.maxResults) !== null && _m !== void 0 ? _m : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS; const searchOptions = { maxResults: maxResults, safeSearch: (_o = imageSearchOptions.safeSearch) !== null && _o !== void 0 ? _o : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH, locale: (_q = (_p = imageSearchOptions.region) !== null && _p !== void 0 ? _p : globalLocale) !== null && _q !== void 0 ? _q : constants_1.DEFAULT_PARAMETERS.REGION, }; if (apiKey) { searchOptions.headers = { Authorization: `Bearer ${apiKey}`, }; } const cacheKey = JSON.stringify({ operation,