n8n-nodes-duckduckgo-search
Version:
A powerful and comprehensive n8n community node that seamlessly integrates DuckDuckGo search capabilities into your workflows. Search the web, find images, discover news, and explore videos - all with privacy-focused, reliable results.
2,124 lines • 118 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 directSearch_1 = require("./directSearch");
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 searchOperators_1 = require("./searchOperators");
const vqdPagination_1 = require("./vqdPagination");
const fallbackSearch_1 = require("./fallbackSearch");
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 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
'Mozilla/5.0 (Linux; Android 14; SM-G998U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0',
];
return userAgents[Math.floor(Math.random() * userAgents.length)];
}
function getSafeSearchType(value) {
switch (value) {
case 0:
return duck_duck_scrape_1.SafeSearchType.STRICT;
case -1:
return duck_duck_scrape_1.SafeSearchType.MODERATE;
case -2:
return duck_duck_scrape_1.SafeSearchType.OFF;
default:
return duck_duck_scrape_1.SafeSearchType.MODERATE;
}
}
function getSearchTimeType(value) {
switch (value) {
case 'pastDay':
case 'd':
return duck_duck_scrape_1.SearchTimeType.DAY;
case 'pastWeek':
case 'w':
return duck_duck_scrape_1.SearchTimeType.WEEK;
case 'pastMonth':
case 'm':
return duck_duck_scrape_1.SearchTimeType.MONTH;
case 'pastYear':
case 'y':
return duck_duck_scrape_1.SearchTimeType.YEAR;
case 'anyTime':
case 'a':
default:
return duck_duck_scrape_1.SearchTimeType.ALL;
}
}
function getVideoDefinition(value) {
switch (value) {
case 'high':
case 'hd':
return duck_duck_scrape_1.VideoDefinition.HIGH;
case 'standard':
case 'sd':
return duck_duck_scrape_1.VideoDefinition.STANDARD;
default:
return duck_duck_scrape_1.VideoDefinition.ANY;
}
}
function getVideoDuration(value) {
switch (value) {
case 'short':
case 'SHORT':
return duck_duck_scrape_1.VideoDuration.SHORT;
case 'medium':
case 'MEDIUM':
return duck_duck_scrape_1.VideoDuration.MEDIUM;
case 'long':
case 'LONG':
return duck_duck_scrape_1.VideoDuration.LONG;
default:
return duck_duck_scrape_1.VideoDuration.ANY;
}
}
function getVideoLicense(value) {
switch (value) {
case 'creativeCommon':
case 'cc':
return duck_duck_scrape_1.VideoLicense.CREATIVE_COMMONS;
case 'youtube':
case 'yt':
return duck_duck_scrape_1.VideoLicense.YOUTUBE;
default:
return duck_duck_scrape_1.VideoLicense.ANY;
}
}
function enhanceSearchQuery(query, searchType = 'web') {
let enhancedQuery = query.trim();
if (searchType === 'web') {
if (/^(what|how|why|when|where|who|which|can|could|should|would|will|is|are|do|does|did)\s/i.test(enhancedQuery)) {
}
else if (/\b(buy|purchase|price|cost|review|compare|best|top|vs)\b/i.test(enhancedQuery)) {
if (!enhancedQuery.includes('2025') && !enhancedQuery.includes('2024')) {
enhancedQuery += ' 2025';
}
}
else if (/\b(error|fix|solution|tutorial|guide|how to|install|setup|config)\b/i.test(enhancedQuery)) {
if (!enhancedQuery.includes('2025') && !enhancedQuery.includes('2024')) {
enhancedQuery += ' 2025';
}
}
else if (/\b(news|latest|recent|update|today|now|current)\b/i.test(enhancedQuery)) {
if (!enhancedQuery.includes('2025')) {
enhancedQuery += ' 2025';
}
}
else if (enhancedQuery.split(' ').length <= 2 && enhancedQuery.length > 2) {
if (!/\b(definition|meaning|what is)\b/i.test(enhancedQuery)) {
enhancedQuery = `"${enhancedQuery}"`;
}
}
}
return enhancedQuery;
}
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: 'Search Backend',
name: 'searchBackend',
type: 'options',
default: 'auto',
description: 'Choose which search backend to use for better reliability',
options: [
{
name: 'Auto (Recommended)',
value: 'auto',
description: 'Automatically tries multiple backends for best results',
},
{
name: 'Duck-Duck-Scrape',
value: 'duck-duck-scrape',
description: 'Primary search engine library',
},
{
name: 'SearchAPI',
value: 'search-api',
description: 'Alternative API-based search',
},
{
name: 'HTML Backend',
value: 'html',
description: 'Direct HTML parsing from html.duckduckgo.com',
},
{
name: 'Lite Backend',
value: 'lite',
description: 'Lightweight search from lite.duckduckgo.com',
},
],
},
{
displayName: 'Use Search Operators',
name: 'useSearchOperators',
type: 'boolean',
default: false,
description: 'Whether to enable advanced search operators',
},
{
displayName: 'Search Operators',
name: 'searchOperators',
type: 'collection',
placeholder: 'Add Search Operator',
default: {},
displayOptions: {
show: {
useSearchOperators: [true],
},
},
description: 'Advanced search operators to refine your search',
options: [
{
displayName: 'Site',
name: 'site',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.site.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.site.description}. Example: ${searchOperators_1.OPERATOR_INFO.site.example}`,
},
{
displayName: 'File Type',
name: 'filetype',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.filetype.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.filetype.description}. Example: ${searchOperators_1.OPERATOR_INFO.filetype.example}`,
},
{
displayName: 'In Title',
name: 'intitle',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.intitle.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.intitle.description}. Example: ${searchOperators_1.OPERATOR_INFO.intitle.example}`,
},
{
displayName: 'In URL',
name: 'inurl',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.inurl.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.inurl.description}. Example: ${searchOperators_1.OPERATOR_INFO.inurl.example}`,
},
{
displayName: 'In Body',
name: 'inbody',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.inbody.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.inbody.description}. Example: ${searchOperators_1.OPERATOR_INFO.inbody.example}`,
},
{
displayName: 'Exclude Words',
name: 'exclude',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.exclude.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.exclude.description}. Example: ${searchOperators_1.OPERATOR_INFO.exclude.example}`,
},
{
displayName: 'Exact Phrase',
name: 'exact',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.exact.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.exact.description}. Example: ${searchOperators_1.OPERATOR_INFO.exact.example}`,
},
{
displayName: 'OR Terms',
name: 'or',
type: 'string',
default: '',
placeholder: 'term1, term2, term3',
description: 'Search for any of these terms (comma-separated). Example: python, javascript, typescript',
},
{
displayName: 'Related',
name: 'related',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.related.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.related.description}. Example: ${searchOperators_1.OPERATOR_INFO.related.example}`,
},
{
displayName: 'Cache',
name: 'cache',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.cache.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.cache.description}. Example: ${searchOperators_1.OPERATOR_INFO.cache.example}`,
},
{
displayName: 'Define',
name: 'define',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.define.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.define.description}. Example: ${searchOperators_1.OPERATOR_INFO.define.example}`,
},
{
displayName: 'All In Title',
name: 'allintitle',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.allintitle.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.allintitle.description}. Example: ${searchOperators_1.OPERATOR_INFO.allintitle.example}`,
},
{
displayName: 'All In URL',
name: 'allinurl',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.allinurl.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.allinurl.description}. Example: ${searchOperators_1.OPERATOR_INFO.allinurl.example}`,
},
{
displayName: 'All In Text',
name: 'allintext',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.allintext.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.allintext.description}. Example: ${searchOperators_1.OPERATOR_INFO.allintext.example}`,
},
{
displayName: 'Location',
name: 'location',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.location.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.location.description}. Example: ${searchOperators_1.OPERATOR_INFO.location.example}`,
},
{
displayName: 'Language Code',
name: 'language',
type: 'string',
default: '',
placeholder: searchOperators_1.OPERATOR_INFO.language.placeholder,
description: `${searchOperators_1.OPERATOR_INFO.language.description}. Example: ${searchOperators_1.OPERATOR_INFO.language.example}`,
},
],
},
],
},
{
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)',
},
{
displayName: 'Proxy Settings',
name: 'proxySettings',
type: 'collection',
placeholder: 'Add Proxy Setting',
default: {},
description: 'Configure proxy for all requests',
options: [
{
displayName: 'Use Proxy',
name: 'useProxy',
type: 'boolean',
default: false,
description: 'Whether to use a proxy for requests',
},
{
displayName: 'Proxy Type',
name: 'proxyType',
type: 'options',
default: 'http',
description: 'Type of proxy to use',
options: [
{
name: 'HTTP',
value: 'http',
},
{
name: 'HTTPS',
value: 'https',
},
{
name: 'SOCKS4',
value: 'socks4',
},
{
name: 'SOCKS5',
value: 'socks5',
},
],
displayOptions: {
show: {
useProxy: [true],
},
},
},
{
displayName: 'Proxy Host',
name: 'proxyHost',
type: 'string',
default: '',
placeholder: 'proxy.example.com',
description: 'Hostname or IP address of the proxy server',
displayOptions: {
show: {
useProxy: [true],
},
},
},
{
displayName: 'Proxy Port',
name: 'proxyPort',
type: 'number',
default: 8080,
description: 'Port number of the proxy server',
typeOptions: {
minValue: 1,
maxValue: 65535,
},
displayOptions: {
show: {
useProxy: [true],
},
},
},
{
displayName: 'Proxy Authentication',
name: 'proxyAuth',
type: 'boolean',
default: false,
description: 'Whether the proxy requires authentication',
displayOptions: {
show: {
useProxy: [true],
},
},
},
{
displayName: 'Proxy Username',
name: 'proxyUsername',
type: 'string',
default: '',
description: 'Username for proxy authentication',
displayOptions: {
show: {
useProxy: [true],
proxyAuth: [true],
},
},
},
{
displayName: 'Proxy Password',
name: 'proxyPassword',
type: 'string',
typeOptions: {
password: true,
},
default: '',
description: 'Password for proxy authentication',
displayOptions: {
show: {
useProxy: [true],
proxyAuth: [true],
},
},
},
],
},
{
displayName: 'Search Filters',
name: 'searchFilters',
type: 'collection',
placeholder: 'Add Filter',
default: {},
description: 'Advanced search filters for more precise results',
displayOptions: {
show: {
operation: [
types_1.DuckDuckGoOperation.Search,
],
},
},
options: [
{
displayName: 'Region Filter',
name: 'useRegionFilter',
type: 'boolean',
default: false,
description: 'Whether to filter results by region',
},
{
displayName: 'Region',
name: 'region',
type: 'options',
default: 'wt-wt',
description: 'Region to filter results',
options: [
{ name: 'No Region', value: 'wt-wt' },
{ name: 'United States', value: 'us-en' },
{ name: 'United Kingdom', value: 'uk-en' },
{ name: 'Canada', value: 'ca-en' },
{ name: 'Australia', value: 'au-en' },
{ name: 'Germany', value: 'de-de' },
{ name: 'France', value: 'fr-fr' },
{ name: 'Spain', value: 'es-es' },
{ name: 'Italy', value: 'it-it' },
{ name: 'Netherlands', value: 'nl-nl' },
{ name: 'Brazil', value: 'br-pt' },
{ name: 'Mexico', value: 'mx-es' },
{ name: 'Russia', value: 'ru-ru' },
{ name: 'Japan', value: 'jp-jp' },
{ name: 'China', value: 'cn-zh' },
{ name: 'India', value: 'in-en' },
{ name: 'South Korea', value: 'kr-kr' },
{ name: 'Turkey', value: 'tr-tr' },
{ name: 'Saudi Arabia', value: 'sa-ar' },
{ name: 'United Arab Emirates', value: 'ae-ar' },
],
displayOptions: {
show: {
useRegionFilter: [true],
},
},
},
{
displayName: 'Language Filter',
name: 'useLanguageFilter',
type: 'boolean',
default: false,
description: 'Whether to filter results by language',
},
{
displayName: 'Language',
name: 'language',
type: 'options',
default: '',
description: 'Language to filter results',
options: [
{ name: 'All Languages', value: '' },
{ name: 'English', value: 'en' },
{ name: 'Spanish', value: 'es' },
{ name: 'French', value: 'fr' },
{ name: 'German', value: 'de' },
{ name: 'Italian', value: 'it' },
{ name: 'Portuguese', value: 'pt' },
{ name: 'Dutch', value: 'nl' },
{ name: 'Russian', value: 'ru' },
{ name: 'Japanese', value: 'ja' },
{ name: 'Korean', value: 'ko' },
{ name: 'Chinese', value: 'zh' },
{ name: 'Arabic', value: 'ar' },
{ name: 'Hindi', value: 'hi' },
{ name: 'Turkish', value: 'tr' },
],
displayOptions: {
show: {
useLanguageFilter: [true],
},
},
},
{
displayName: 'Date Filter',
name: 'useDateFilter',
type: 'boolean',
default: false,
description: 'Whether to filter results by date',
},
{
displayName: 'Date Range',
name: 'dateRangeType',
type: 'options',
default: 'month',
description: 'Time period for results',
options: [
{ name: 'Last Day', value: 'day' },
{ name: 'Last Week', value: 'week' },
{ name: 'Last Month', value: 'month' },
{ name: 'Last Year', value: 'year' },
{ name: 'Custom Range', value: 'custom' },
],
displayOptions: {
show: {
useDateFilter: [true],
},
},
},
{
displayName: 'Date From',
name: 'dateFrom',
type: 'string',
default: '',
placeholder: 'YYYY-MM-DD',
description: 'Start date for custom range',
displayOptions: {
show: {
useDateFilter: [true],
dateRangeType: ['custom'],
},
},
},
{
displayName: 'Date To',
name: 'dateTo',
type: 'string',
default: '',
placeholder: 'YYYY-MM-DD',
description: 'End date for custom range',
displayOptions: {
show: {
useDateFilter: [true],
dateRangeType: ['custom'],
},
},
},
],
},
],
};
}
async _webSearchWithSuperPagination(query, options) {
const debugMode = this.getNodeParameter('debugMode', 0, false);
const paginationResult = await (0, vqdPagination_1.paginateWithVqd)(query, {
safeSearch: getSafeSearchType(options.safeSearch),
locale: options.locale,
time: options.timePeriod,
}, {
maxResults: options.maxResults,
pageSize: vqdPagination_1.DEFAULT_PAGINATION_CONFIG.pageSize,
maxPages: Math.min(vqdPagination_1.DEFAULT_PAGINATION_CONFIG.maxPages, Math.ceil(options.maxResults / vqdPagination_1.DEFAULT_PAGINATION_CONFIG.pageSize)),
delayBetweenRequests: vqdPagination_1.DEFAULT_PAGINATION_CONFIG.delayBetweenRequests,
debugMode,
});
if (paginationResult.results.length < options.maxResults && paginationResult.totalFetched < options.maxResults) {
const { parseHtmlResults } = await Promise.resolve().then(() => __importStar(require('./htmlParser')));
const remainingNeeded = options.maxResults - paginationResult.results.length;
const htmlResults = [];
let offsetHtml = 0;
let hasMoreHtml = true;
const pageSizeHtml = 10;
while (htmlResults.length < remainingNeeded && 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: '',
}));
htmlResults.push(...convertedResults);
if (page.length < pageSizeHtml) {
hasMoreHtml = false;
}
offsetHtml += pageSizeHtml;
}
catch (error) {
hasMoreHtml = false;
if (debugMode) {
const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.ERROR, `HTML fallback error: ${error.message}`, 'webSearchWithSuperPagination', { query, error: error.message });
console.error(JSON.stringify(logEntry));
}
break;
}
await sleep(vqdPagination_1.DEFAULT_PAGINATION_CONFIG.delayBetweenRequests);
}
return [...paginationResult.results, ...htmlResults].slice(0, options.maxResults);
}
return paginationResult.results;
}
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;
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);
if (!query || query.trim() === '') {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Query is required for search operation', { itemIndex });
}
const options = this.getNodeParameter('webSearchOptions', itemIndex, {});
let enhancedQuery = query;
if (options.useSearchOperators && options.searchOperators) {
const validationErrors = (0, searchOperators_1.validateSearchOperators)(options.searchOperators);
if (validationErrors.length === 0) {
enhancedQuery = (0, searchOperators_1.buildSearchQuery)(query, options.searchOperators);
}
else {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid search operators: ${validationErrors.join(', ')}`, { itemIndex });
}
}
else {
enhancedQuery = enhanceSearchQuery(query, 'web');
}
if (debugMode) {
const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.INFO, `Enhanced search query with operators: ${enhancedQuery}`, operation, { originalQuery: query, enhancedQuery, operators: options.searchOperators });
console.log(JSON.stringify(logEntry));
}
const searchOptions = {
safeSearch: getSafeSearchType((_a = options.safeSearch) !== null && _a !== void 0 ? _a : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
locale: (_c = (_b = options.region) !== null && _b !== void 0 ? _b : globalLocale) !== null && _c !== void 0 ? _c : constants_1.DEFAULT_PARAMETERS.REGION,
time: (_d = options.timePeriod) !== null && _d !== void 0 ? _d : constants_1.DEFAULT_PARAMETERS.TIME_PERIOD,
};
if (apiKey) {
searchOptions.headers = {
Authorization: `Bearer ${apiKey}`,
};
}
const cacheKey = JSON.stringify({
operation,
query: enhancedQuery,
options: searchOptions,
});
const startTime = Date.now();
const telemetryData = {
operation,
query: enhancedQuery,
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: ${enhancedQuery}`, operation, { query: enhancedQuery, 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: ${enhancedQuery}`, operation, { query: enhancedQuery, options: searchOptions, cacheEnabled: enableCache });
console.log(JSON.stringify(logEntry));
}
const directResults = await (0, directSearch_1.directWebSearch)(enhancedQuery, {
locale: searchOptions.locale || 'us-en',
safeSearch: (0, directSearch_1.getSafeSearchString)((_e = options.safeSearch) !== null && _e !== void 0 ? _e : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
maxResults: undefined,
});
result = {
results: directResults.results.map(r => ({
title: r.title,
url: r.url,
description: r.description,
hostname: new URL(r.url).hostname,
})),
noResults: directResults.results.length === 0,
};
const maxResults = (_f = options.maxResults) !== null && _f !== void 0 ? _f : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
if (result.results && result.results.length > maxResults) {
result.results = result.results.slice(0, maxResults);
}
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: ${enhancedQuery}`, operation, { query: enhancedQuery, 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: ${enhancedQuery}`, operation, { query: enhancedQuery, options: searchOptions });
console.log(JSON.stringify(logEntry));
}
results = [{
json: {
success: true,
query: enhancedQuery,
count: 0,
results: [],
...(debugMode ? { requestOptions: searchOptions, fromCache: result !== undefined } : {}),
},
pairedItem: {
item: itemIndex,
},
}];
if (enableTelemetry) {
await (0, telemetry_1.reportEvent)(this, 'search_completed', {
operation,
query: enhancedQuery,
durationMs: Date.now() - startTime,
resultCount: 0,
fromCache: !!result,
});
}
}
else {
if (options.returnRawResults || debugMode) {
results = [{
json: {
success: true,
query: enhancedQuery,
result,
...(debugMode ? { requestOptions: searchOptions, fromCache: result !== undefined } : {}),
},
pairedItem: {
item: itemIndex,
},
}];
}
else {
const maxResults = (_g = options.maxResults) !== null && _g !== void 0 ? _g : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
results = (0, processors_1.processWebSearchResults)(result.results, itemIndex, result).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: enhancedQuery,
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: enhancedQuery, options: searchOptions }, error instanceof Error ? error : new Error(String(error)));
console.error(JSON.stringify(logEntry));
}
results = [{
json: {
success: false,
error: errorMessage,
query: enhancedQuery,
...(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: enhancedQuery,
durationMs: Date.now() - startTime,
error: errorMessage,
errorType: ((_h = error === null || error === void 0 ? void 0 : error.constructor) === null || _h === void 0 ? void 0 : _h.name) || 'Error',
});
}
}
}
else if (operation === types_1.DuckDuckGoOperation.SearchImages) {
const imageQuery = this.getNodeParameter('imageQuery', itemIndex);
const imageSearchOptions = this.getNodeParameter('imageSearchOptions', itemIndex, {});
const searchOptions = {
safeSearch: getSafeSearchType((_j = imageSearchOptions.safeSearch) !== null && _j !== void 0 ? _j : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
locale: (_l = (_k = imageSearchOptions.region) !== null && _k !== void 0 ? _k : globalLocale) !== null && _l !== void 0 ? _l : constants_1.DEFAULT_PARAMETERS.REGION,
size: imageSearchOptions.size,
color: imageSearchOptions.color,
type: imageSearchOptions.type,
layout: imageSearchOptions.layout,
license: imageSearchOptions.license,
};
if (apiKey) {
searchOptions.headers = {
Authorization: `Bearer ${apiKey}`,
};
}
const cacheKey = JSON.stringify({
operation,
query: imageQuery,
options: searchOptions,
});
const startTime = Date.now();
const telemetryData = {
operation,
query: imageQuery,
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 image search: ${imageQuery}`, operation, { query: imageQuery, 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 image search for: ${imageQuery}`, operation, { query: imageQuery, options: searchOptions, cacheEnabled: enableCache });
console.log(JSON.stringify(logEntry));
}
const directImageResults = await (0, directSearch_1.directImageSearch)(imageQuery, {
locale: searchOptions.locale || 'us-en',
safeSearch: (0, directSearch_1.getSafeSearchString)((_m = imageSearchOptions.safeSearch) !== null && _m !== void 0 ? _m : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
maxResults: undefined,
});
result = {
results: directImageResults.results.map(r => ({
title: r.title,
image: r.url,
thumbnail: r.thumbnail,
url: r.source,
height: r.height,
width: r.width,
})),
noResults: directImageResults.results.length === 0,
};
const maxResults = (_o = imageSearchOptions.maxResults) !== null && _o !== void 0 ? _o : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
if (result.results && result.results.length > maxResults) {
result.results = result.results.slice(0, maxResults);
}
if (enableCache && result) {
(0, cache_1.setCache)(cacheKey, result, cacheTTL);
if (debugMode) {
const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.INFO, `Cached image search result for: ${imageQuery}`, operation, { query: imageQuery, 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 image search query: ${imageQuery}`, operation, { query: imageQuery, options: searchOptions });
console.log(JSON.stringify(logEntry));
}
results = [{
json: {
success: true,
query: imageQuery,
count: 0,
results: [],
...(debugMode ? { requestOptions: searchOptions, fromCache: result !== undefined } : {}),
},
pairedItem: {
item: itemIndex,
},
}];
if (enableTelemetry) {
await (0, telemetry_1.reportEvent)(this, 'search_completed', {
operation,
query: imageQuery,
durationMs: Date.now() - startTime,
resultCount: 0,
fromCache: !!result,
});
}
}
else {
if (imageSearchOptions.returnRawResults || debugMode) {
results = [{
json: {
success: true,
query: imageQuery,
result,
...(debugMode ? { requestOptions: searchOptions, fromCache: result !== undefined } : {}),
},
pairedItem: {
item: itemIndex,
},
}];
}
else {
const maxResults = (_p = imageSearchOptions.maxResults) !== null && _p !== void 0 ? _p : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
results = (0, processors_1.processImageSearchResults)(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: imageQuery,
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)), 'image search');
if (debugMode) {
const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.ERROR, `Image search error: ${errorMessage}`, operation, { query: imageQuery, options: searchOptions }, error instanceof Error ? error : new Error(String(error)));
console.error(JSON.stringify(logEntry));
}
results = [{
json: {
success: false,
error: errorMessage,
query: imageQuery,
...(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: imageQuery,
durationMs: Date.now() - startTime,
error: errorMessage,
errorType: ((_q = error === null || error === void 0 ? void 0 : error.constructor) === null || _q === void 0 ? void 0 : _q.name) || 'Error',
});
}
}
}
else if (operation === types_1.DuckDuckGoOperation.SearchNews) {
const newsQuery = this.getNodeParameter('newsQuery', itemIndex);
const newsSearchOptions = this.getNodeParameter('newsSearchOptions', itemIndex, {});
const searchOptions = {
safeSearch: getSafeSearchType((_r = newsSearchOptions.safeSearch) !== null && _r !== void 0 ? _r : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
locale: (_t = (_s = newsSearchOptions.region) !== null && _s !== void 0 ? _s : globalLocale) !== null && _t !== void 0 ? _t : constants_1.DEFAULT_PARAMETERS.REGION,
time: getSearchTimeType((_u = newsSearchOptions.timePeriod) !== null && _u !== void 0 ? _u : constants_1.DEFAULT_PARAMETERS.TIME_PERIOD),
};
if (apiKey) {
searchOptions.headers = {
Authorization: `Bearer ${apiKey}`,
};
}
const maxResults = (_v = newsSearchOptions.maxResults) !== null && _v !== void 0 ? _v : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
const cacheKey = JSON.stringify({
operation,
query: newsQuery,
options: searchOptions,
});
const startTime = Date.now();
const telemetryData = {
operation,
query: newsQuery,
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 news search: ${newsQuery}`, operation, { query: newsQuery, 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 news search for: ${newsQuery}`, operation, { query: newsQuery, options: searchOptions, cacheEnabled: enableCache });
console.log(JSON.stringify(logEntry));
}
result = await (0, duck_duck_scrape_1.searchNews)(newsQuery, searchOptions);
if (newsSearchOptions.maxResults !== undefined && result.results && result.results.length > 0) {
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 news results (page ${page}) for: ${newsQuery}`, operation, { query: newsQuery, options: searchOptions, page });
console.log(JSON.stringify(logEntry));
}
try {
if (result.vqd) {
const offset = (page - 1) * 10;
const nextPageOptions = {
...searchOptions,
offset: offset,
vqd: result.vqd,
};
const nextPageResult = await (0, duck_duck_scrape_1.searchNews)(newsQuery, 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 news results: ${pageError.message}`, operation, { query: newsQuery, 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 news search result for: ${newsQuery}`, operation, { query: newsQuery, 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 news search query: ${newsQuery}`, operation, { query: newsQuery, options: searchOptions });
console.log(JSON.stringify(logEntry));
}
results = [{
json: {
success: true,
query: newsQuery,
count: 0,
results: [],
...(debugMode ? { requestOptions: searchOptions, fromCache: result !== undefined } : {}),
},
pairedItem: {
item: itemIndex,
},
}];
if (enableTelemetry) {
await (0, telemetry_1.reportEvent)(this, 'search_completed', {
operation,
query: newsQuery,
durationMs: Date.now() - startTime,
resultCount: 0,
fromCache: !!result,
});
}
}
else {
if (newsSearchOptions.returnRawResults || debugMode) {
results = [{
json: {
success: true,
query: newsQuery,
result,
...(debugMode ? { requestOptions: searchOptions, fromCache: result !== undefined } : {}),
},
pairedItem: {
item: itemIndex,
},
}];
}
else {
const maxResults = (_w = newsSearchOptions.maxResults) !== null && _w !== void 0 ? _w : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
results = (0, processors_1.processNewsSearchResults)(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: newsQuery,
durationMs: Date.now() - startTime,
resultCount,
fromCache: result !== undefined && !result.fromCache,
});
}
}
}
catch (error) {
try {
const fallbackResult = await (0, fallbackSearch_1.fallbackNewsSearch)(newsQuery, {
locale: searchOptions.locale,
safeSearch: searchOptions.safeSearch,
time: searchOptions.time,
});
if (fallbackResult.success && fallbackResult.results.length > 0) {
const newsResults = fallbackResult.results.map(item => ({
date: Date.now(),
title: item.title,
body: item.body,
url: item.href,
image: '',
source: 'DuckDuckGo Fallback',
}));
results = (0, processors_1.processNewsSearchResults)(newsResults, itemIndex).slice(0, (_x = newsSearchOptions.maxResults) !== null && _x !== void 0 ? _x : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS);
if (enableTelemetry) {
await (0, telemetry_1.reportEvent)(this, 'search_completed', {
operation,
query: newsQuery,
durationMs: Date.now() - startTime,
resultCount: newsResults.length,
fromCache: false,
fallbackUsed: true,
});
}
}
}
catch (fallbackError) {
console.error('Fallback news search also failed:', fallbackError);
}
const errorMessage = (0, utils_1.parseApiError)(error instanceof Error ? error : new Error(String(error)), 'news search');
if (debugMode) {
const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.ERROR, `News search error: ${errorMessage}`, operation, { query: newsQuery, options: searchOptions }, error instanceof Error ? error : new Error(String(error)));
console.error(JSON.stringify(logEntry));
}
results = [{
json: {
success: false,
error: errorMessage,
query: newsQuery,
...(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: newsQuery,
durationMs: Date.now() - startTime,
error: errorMessage,
errorType: ((_y = error === null || error === void 0 ? void 0 : error.constructor) === null || _y === void 0 ? void 0 : _y.name) || 'Error',
});
}
}
}
else if (operation === types_1.DuckDuckGoOperation.SearchVideos) {
const videoQuery = this.getNodeParameter('videoQuery', itemIndex);
const videoSearchOptions = this.getNodeParameter('videoSearchOptions', itemIndex, {});
const searchOptions = {
safeSearch: getSafeSearchType((_z = videoSearchOptions.safeSearch) !== null && _z !== void 0 ? _z : constants_1.DEFAULT_PARAMETERS.SAFE_SEARCH),
locale: (_1 = (_0 = videoSearchOptions.region) !== null && _0 !== void 0 ? _0 : globalLocale) !== null && _1 !== void 0 ? _1 : constants_1.DEFAULT_PARAMETERS.REGION,
time: getSearchTimeType((_2 = videoSearchOptions.timePeriod) !== null && _2 !== void 0 ? _2 : constants_1.DEFAULT_PARAMETERS.TIME_PERIOD),
definition: getVideoDefinition(videoSearchOptions.quality),
duration: getVideoDuration(videoSearchOptions.length),
license: getVideoLicense(videoSearchOptions.resolution),
};
const maxResults = (_3 = videoSearchOptions.maxResults) !== null && _3 !== void 0 ? _3 : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
if (apiKey) {
searchOptions.headers = {
Authorization: `Bearer ${apiKey}`,
};
}
const cacheKey = JSON.stringify({
operation,
query: videoQuery,
options: searchOptions,
});
const startTime = Date.now();
const telemetryData = {
operation,
query: videoQuery,
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 video search: ${videoQuery}`, operation, { query: videoQuery, 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 video search for: ${videoQuery}`, operation, { query: videoQuery, options: searchOptions, cacheEnabled: enableCache });
console.log(JSON.stringify(logEntry));
}
result = await (0, duck_duck_scrape_1.searchVideos)(videoQuery, searchOptions);
if (videoSearchOptions.maxResults !== undefined && result.results && result.results.length > 0) {
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 video results (page ${page}) for: ${videoQuery}`, operation, { query: videoQuery, options: searchOptions, page });
console.log(JSON.stringify(logEntry));
}
try {
if (result.vqd) {
const offset = (page - 1) * 10;
const nextPageOptions = {
...searchOptions,
offset: offset,
vqd: result.vqd,
};
const nextPageResult = await (0, duck_duck_scrape_1.searchVideos)(videoQuery, 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 video results: ${pageError.message}`, operation, { query: videoQuery, 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 video search result for: ${videoQuery}`, operation, { query: videoQuery, 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 video search query: ${videoQuery}`, operation, { query: videoQuery, options: searchOptions });
console.log(JSON.stringify(logEntry));
}
results = [{
json: {
success: true,
query: videoQuery,
count: 0,
results: [],
...(debugMode ? { requestOptions: searchOptions, fromCache: result !== undefined } : {}),
},
pairedItem: {
item: itemIndex,
},
}];
if (enableTelemetry) {
await (0, telemetry_1.reportEvent)(this, 'search_completed', {
operation,
query: videoQuery,
durationMs: Date.now() - startTime,
resultCount: 0,
fromCache: !!result,
});
}
}
else {
if (videoSearchOptions.returnRawResults || debugMode) {
results = [{
json: {
success: true,
query: videoQuery,
result,
...(debugMode ? { requestOptions: searchOptions, fromCache: result !== undefined } : {}),
},
pairedItem: {
item: itemIndex,
},
}];
}
else {
const maxResults = (_4 = videoSearchOptions.maxResults) !== null && _4 !== void 0 ? _4 : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS;
results = (0, processors_1.processVideoSearchResults)(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: videoQuery,
durationMs: Date.now() - startTime,
resultCount,
fromCache: result !== undefined && !result.fromCache,
});
}
}
}
catch (error) {
try {
const fallbackResult = await (0, fallbackSearch_1.fallbackVideoSearch)(videoQuery, {
locale: searchOptions.locale,
safeSearch: searchOptions.safeSearch,
});
if (fallbackResult.success && fallbackResult.results.length > 0) {
const videoResults = fallbackResult.results.map(item => ({
content: item.href,
description: item.body,
duration: '',
embed_html: '',
embed_url: item.href,
image_token: '',
images: {
large: '',
medium: '',
motion: '',
small: '',
},
provider: 'DuckDuckGo Fallback',
published: '',
publisher: 'Web',
statistics: { viewCount: 0 },
title: item.title,
uploader: 'Web',
}));
results = (0, processors_1.processVideoSearchResults)(videoResults, itemIndex).slice(0, (_5 = videoSearchOptions.maxResults) !== null && _5 !== void 0 ? _5 : constants_1.DEFAULT_PARAMETERS.MAX_RESULTS);
if (enableTelemetry) {
await (0, telemetry_1.reportEvent)(this, 'search_completed', {
operation,
query: videoQuery,
durationMs: Date.now() - startTime,
resultCount: videoResults.length,
fromCache: false,
fallbackUsed: true,
});
}
}
}
catch (fallbackError) {
console.error('Fallback video search also failed:', fallbackError);
}
const errorMessage = (0, utils_1.parseApiError)(error instanceof Error ? error : new Error(String(error)), 'video search');
if (debugMode) {
const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.ERROR, `Video search error: ${errorMessage}`, operation, { query: videoQuery, options: searchOptions }, error instanceof Error ? error : new Error(String(error)));
console.error(JSON.stringify(logEntry));
}
results = [{
json: {
success: false,
error: errorMessage,
query: videoQuery,
...(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: videoQuery,
durationMs: Date.now() - startTime,
error: errorMessage,
errorType: ((_6 = error === null || error === void 0 ? void 0 : error.constructor) === null || _6 === void 0 ? void 0 : _6.name) || 'Error',
});
}
}
}
else {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The operation "${operation}" is not supported!`, { itemIndex });
}
returnData.push(...results);
}
catch (error) {
const errorHandling = this.getNodeParameter('errorHandling', itemIndex, 'continueOnFail');
const debugMode = this.getNodeParameter('debugMode', itemIndex, false);
const operation = this.getNodeParameter('operation', itemIndex, 'unknown');
if (errorHandling === 'continueOnFail' || this.continueOnFail()) {
const errorMessage = (0, utils_1.parseApiError)(error instanceof Error ? error : new Error(String(error)), operation);
if (debugMode) {
const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.ERROR, `Operation failed: ${errorMessage}`, operation, {}, error instanceof Error ? error : new Error(String(error)));
console.error(JSON.stringify(logEntry));
}
returnData.push({
json: {
success: false,
error: errorMessage,
operation,
...(debugMode ? {
errorDetails: error instanceof Error ? error.stack : String(error)
} : {}),
...this.getInputData(itemIndex)[0].json,
},
pairedItem: {
item: itemIndex,
},
});
continue;
}
if (debugMode) {
const logEntry = (0, utils_1.createLogEntry)(utils_1.LogLevel.ERROR, `Breaking execution due to error: ${error instanceof Error ? error.message : String(error)}`, operation, {}, error instanceof Error ? error : new Error(String(error)));
console.error(JSON.stringify(logEntry));
}
if (enableTelemetry) {
await (0, telemetry_1.reportEvent)(this, 'node_error', {
error: error instanceof Error ? error.message : String(error),
errorType: ((_7 = error === null || error === void 0 ? void 0 : error.constructor) === null || _7 === void 0 ? void 0 : _7.name) || 'Error',
});
}
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex });
}
}
return [returnData];
}
}
exports.DuckDuckGo = DuckDuckGo;