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