UNPKG

n8n-nodes-duckduckgo-search

Version:

AI Agent-ready n8n community node for DuckDuckGo search. Search the web, images, news, and videos with no API key required and no outbound telemetry. Optionally fetch and extract the main text of result pages or any URL, and get DuckDuckGo Instant Answers

309 lines (308 loc) 13.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.APIError = exports.ValidationError = exports.NetworkError = exports.RateLimitError = exports.DuckDuckGoError = exports.ErrorSeverity = exports.DuckDuckGoErrorType = void 0; exports.handleDuckDuckGoError = handleDuckDuckGoError; exports.parseApiError = parseApiError; exports.createRetryableError = createRetryableError; const n8n_workflow_1 = require("n8n-workflow"); var DuckDuckGoErrorType; (function (DuckDuckGoErrorType) { DuckDuckGoErrorType["NETWORK_ERROR"] = "NETWORK_ERROR"; DuckDuckGoErrorType["TIMEOUT"] = "TIMEOUT"; DuckDuckGoErrorType["CONNECTION_REFUSED"] = "CONNECTION_REFUSED"; DuckDuckGoErrorType["DNS_ERROR"] = "DNS_ERROR"; DuckDuckGoErrorType["API_ERROR"] = "API_ERROR"; DuckDuckGoErrorType["RATE_LIMIT_EXCEEDED"] = "RATE_LIMIT_EXCEEDED"; DuckDuckGoErrorType["TOO_MANY_REQUESTS"] = "TOO_MANY_REQUESTS"; DuckDuckGoErrorType["SERVER_ERROR"] = "SERVER_ERROR"; DuckDuckGoErrorType["BAD_GATEWAY"] = "BAD_GATEWAY"; DuckDuckGoErrorType["SERVICE_UNAVAILABLE"] = "SERVICE_UNAVAILABLE"; DuckDuckGoErrorType["GATEWAY_TIMEOUT"] = "GATEWAY_TIMEOUT"; DuckDuckGoErrorType["SEARCH_ERROR"] = "SEARCH_ERROR"; DuckDuckGoErrorType["VQD_TOKEN_ERROR"] = "VQD_TOKEN_ERROR"; DuckDuckGoErrorType["PAGINATION_ERROR"] = "PAGINATION_ERROR"; DuckDuckGoErrorType["RESULTS_PARSING_ERROR"] = "RESULTS_PARSING_ERROR"; DuckDuckGoErrorType["INVALID_INPUT"] = "INVALID_INPUT"; DuckDuckGoErrorType["MISSING_REQUIRED_PARAMETER"] = "MISSING_REQUIRED_PARAMETER"; DuckDuckGoErrorType["UNKNOWN_ERROR"] = "UNKNOWN_ERROR"; })(DuckDuckGoErrorType || (exports.DuckDuckGoErrorType = DuckDuckGoErrorType = {})); var ErrorSeverity; (function (ErrorSeverity) { ErrorSeverity["LOW"] = "LOW"; ErrorSeverity["MEDIUM"] = "MEDIUM"; ErrorSeverity["HIGH"] = "HIGH"; ErrorSeverity["CRITICAL"] = "CRITICAL"; })(ErrorSeverity || (exports.ErrorSeverity = ErrorSeverity = {})); class DuckDuckGoError extends Error { constructor(message, errorType = DuckDuckGoErrorType.UNKNOWN_ERROR, options = {}) { super(message); this.name = 'DuckDuckGoError'; this.errorType = errorType; this.severity = options.severity || this.determineSeverity(errorType); this.isRetryable = options.isRetryable !== undefined ? options.isRetryable : this.determineRetryability(errorType); this.userMessage = options.userMessage || this.generateUserMessage(errorType, message); this.technicalDetails = options.technicalDetails; this.retryAfter = options.retryAfter; this.statusCode = options.statusCode; } determineSeverity(errorType) { switch (errorType) { case DuckDuckGoErrorType.SERVER_ERROR: case DuckDuckGoErrorType.SERVICE_UNAVAILABLE: return ErrorSeverity.HIGH; case DuckDuckGoErrorType.RATE_LIMIT_EXCEEDED: case DuckDuckGoErrorType.TOO_MANY_REQUESTS: case DuckDuckGoErrorType.TIMEOUT: return ErrorSeverity.MEDIUM; case DuckDuckGoErrorType.INVALID_INPUT: case DuckDuckGoErrorType.VQD_TOKEN_ERROR: return ErrorSeverity.LOW; default: return ErrorSeverity.MEDIUM; } } determineRetryability(errorType) { switch (errorType) { case DuckDuckGoErrorType.NETWORK_ERROR: case DuckDuckGoErrorType.TIMEOUT: case DuckDuckGoErrorType.CONNECTION_REFUSED: case DuckDuckGoErrorType.RATE_LIMIT_EXCEEDED: case DuckDuckGoErrorType.TOO_MANY_REQUESTS: case DuckDuckGoErrorType.SERVER_ERROR: case DuckDuckGoErrorType.BAD_GATEWAY: case DuckDuckGoErrorType.SERVICE_UNAVAILABLE: case DuckDuckGoErrorType.GATEWAY_TIMEOUT: case DuckDuckGoErrorType.VQD_TOKEN_ERROR: return true; default: return false; } } generateUserMessage(errorType, originalMessage) { switch (errorType) { case DuckDuckGoErrorType.RATE_LIMIT_EXCEEDED: case DuckDuckGoErrorType.TOO_MANY_REQUESTS: return this.retryAfter ? `Too many requests. Please wait ${Math.ceil(this.retryAfter / 1000)} seconds before trying again.` : 'Too many requests. Please wait before making more requests.'; case DuckDuckGoErrorType.NETWORK_ERROR: return 'Network connection failed. Please check your internet connection and try again.'; case DuckDuckGoErrorType.TIMEOUT: return 'Request timed out. DuckDuckGo servers may be slow. Please try again.'; case DuckDuckGoErrorType.SERVER_ERROR: case DuckDuckGoErrorType.SERVICE_UNAVAILABLE: return 'DuckDuckGo servers are temporarily unavailable. Please try again later.'; case DuckDuckGoErrorType.VQD_TOKEN_ERROR: return 'Search session expired. The search will be retried automatically.'; case DuckDuckGoErrorType.INVALID_INPUT: return `Invalid input: ${originalMessage}`; case DuckDuckGoErrorType.RESULTS_PARSING_ERROR: return 'Unable to parse search results. DuckDuckGo may have changed their format.'; default: return originalMessage; } } toNodeOperationError(node, itemIndex) { const nodeError = new n8n_workflow_1.NodeOperationError(node, this.userMessage, { itemIndex }); nodeError.errorType = this.errorType; nodeError.severity = this.severity; nodeError.isRetryable = this.isRetryable; nodeError.technicalDetails = this.technicalDetails; return nodeError; } } exports.DuckDuckGoError = DuckDuckGoError; class RateLimitError extends DuckDuckGoError { constructor(message, options = {}) { super(message, DuckDuckGoErrorType.RATE_LIMIT_EXCEEDED, { severity: ErrorSeverity.MEDIUM, isRetryable: true, retryAfter: options.retryAfter, technicalDetails: options, }); } } exports.RateLimitError = RateLimitError; class NetworkError extends DuckDuckGoError { constructor(message, originalError) { const errorType = NetworkError.categorizeNetworkError(originalError); super(message, errorType, { severity: ErrorSeverity.HIGH, isRetryable: true, technicalDetails: originalError ? { originalMessage: originalError.message, stack: originalError.stack } : undefined, }); } static categorizeNetworkError(error) { if (!error) return DuckDuckGoErrorType.NETWORK_ERROR; const message = error.message.toLowerCase(); if (message.includes('timeout') || message.includes('etimedout')) { return DuckDuckGoErrorType.TIMEOUT; } if (message.includes('econnrefused') || message.includes('connection refused')) { return DuckDuckGoErrorType.CONNECTION_REFUSED; } if (message.includes('enotfound') || message.includes('dns')) { return DuckDuckGoErrorType.DNS_ERROR; } return DuckDuckGoErrorType.NETWORK_ERROR; } } exports.NetworkError = NetworkError; class ValidationError extends DuckDuckGoError { constructor(message, options = {}) { const userMessage = options.field ? `Invalid value for field "${options.field}": ${message}` : message; super(message, DuckDuckGoErrorType.INVALID_INPUT, { severity: ErrorSeverity.LOW, isRetryable: false, userMessage, technicalDetails: options, }); this.field = options.field; this.value = options.value; } } exports.ValidationError = ValidationError; class APIError extends DuckDuckGoError { constructor(message, options = {}) { const errorType = APIError.categorizeAPIError(options.statusCode); const severity = APIError.determineSeverityFromStatus(options.statusCode); const isRetryable = APIError.determineRetryabilityFromStatus(options.statusCode); super(message, errorType, { severity, isRetryable, statusCode: options.statusCode, technicalDetails: options, }); } static categorizeAPIError(statusCode) { if (!statusCode) return DuckDuckGoErrorType.API_ERROR; switch (statusCode) { case 429: return DuckDuckGoErrorType.TOO_MANY_REQUESTS; case 500: return DuckDuckGoErrorType.SERVER_ERROR; case 502: return DuckDuckGoErrorType.BAD_GATEWAY; case 503: return DuckDuckGoErrorType.SERVICE_UNAVAILABLE; case 504: return DuckDuckGoErrorType.GATEWAY_TIMEOUT; default: return DuckDuckGoErrorType.API_ERROR; } } static determineSeverityFromStatus(statusCode) { if (!statusCode) return ErrorSeverity.MEDIUM; if (statusCode >= 500) return ErrorSeverity.HIGH; if (statusCode >= 400) return ErrorSeverity.MEDIUM; return ErrorSeverity.LOW; } static determineRetryabilityFromStatus(statusCode) { if (!statusCode) return false; if (statusCode >= 500) return true; if (statusCode === 429) return true; return false; } } exports.APIError = APIError; function handleDuckDuckGoError(error, operation, options = {}) { var _a; if (error instanceof DuckDuckGoError) { if (options.node) { return error.toNodeOperationError(options.node, options.itemIndex); } } if (error instanceof n8n_workflow_1.NodeOperationError) { return error; } if (error.code) { const networkError = new NetworkError(`Network error during ${operation}`, error); if (options.node) { return networkError.toNodeOperationError(options.node, options.itemIndex); } } if ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) { const apiError = new APIError(`API error during ${operation}`, { statusCode: error.response.status, responseBody: error.response.data, headers: error.response.headers, }); if (options.node) { return apiError.toNodeOperationError(options.node, options.itemIndex); } } if (error.message) { let errorType = DuckDuckGoErrorType.UNKNOWN_ERROR; if (error.message.includes('VQD')) { errorType = DuckDuckGoErrorType.VQD_TOKEN_ERROR; } else if (error.message.includes('rate limit') || error.message.includes('429')) { errorType = DuckDuckGoErrorType.RATE_LIMIT_EXCEEDED; } else if (error.message.includes('parse') || error.message.includes('parsing')) { errorType = DuckDuckGoErrorType.RESULTS_PARSING_ERROR; } else if (error.message.includes('timeout')) { errorType = DuckDuckGoErrorType.TIMEOUT; } const duckError = new DuckDuckGoError(error.message, errorType, { technicalDetails: { operation, stack: error.stack, ...(options.debugMode && { originalError: error }), }, }); if (options.node) { return duckError.toNodeOperationError(options.node, options.itemIndex); } } const fallbackError = new DuckDuckGoError(`Unexpected error during ${operation}: ${error.message || 'Unknown error'}`, DuckDuckGoErrorType.UNKNOWN_ERROR, { technicalDetails: { operation, originalError: options.debugMode ? error : error.message, }, }); if (options.node) { return fallbackError.toNodeOperationError(options.node, options.itemIndex); } return new n8n_workflow_1.NodeOperationError(options.node || { name: 'DuckDuckGo', type: 'unknown' }, fallbackError.userMessage, { itemIndex: options.itemIndex }); } function parseApiError(response) { const statusCode = (response === null || response === void 0 ? void 0 : response.status) || (response === null || response === void 0 ? void 0 : response.statusCode); const responseBody = (response === null || response === void 0 ? void 0 : response.data) || (response === null || response === void 0 ? void 0 : response.body); if (statusCode) { return new APIError('API request failed', { statusCode, responseBody, headers: response === null || response === void 0 ? void 0 : response.headers, }); } return new DuckDuckGoError('Unknown API error', DuckDuckGoErrorType.API_ERROR, { technicalDetails: { response }, }); } function createRetryableError(error, operation, attemptNumber, maxAttempts) { const baseError = error instanceof DuckDuckGoError ? error : new DuckDuckGoError(error.message || 'Unknown error'); if (baseError.isRetryable && attemptNumber < maxAttempts) { return new DuckDuckGoError(`${operation} failed (attempt ${attemptNumber}/${maxAttempts}): ${baseError.message}`, baseError.errorType, { ...baseError, technicalDetails: { ...baseError.technicalDetails, attemptNumber, maxAttempts, willRetry: attemptNumber < maxAttempts, }, }); } return baseError; }