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
JavaScript
"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,