UNPKG

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.

309 lines (308 loc) 14.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createRetryableError = exports.parseApiError = exports.handleDuckDuckGoError = exports.APIError = exports.ValidationError = exports.NetworkError = exports.RateLimitError = exports.DuckDuckGoError = exports.ErrorSeverity = exports.DuckDuckGoErrorType = void 0; 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 || (exports.DuckDuckGoErrorType = {})); var ErrorSeverity; (function (ErrorSeverity) { ErrorSeverity["LOW"] = "LOW"; ErrorSeverity["MEDIUM"] = "MEDIUM"; ErrorSeverity["HIGH"] = "HIGH"; ErrorSeverity["CRITICAL"] = "CRITICAL"; })(ErrorSeverity = exports.ErrorSeverity || (exports.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 }); } exports.handleDuckDuckGoError = handleDuckDuckGoError; 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 }, }); } exports.parseApiError = parseApiError; 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; } exports.createRetryableError = createRetryableError;