bktide
Version:
Command-line interface for Buildkite CI/CD workflows with rich shell completions (Fish, Bash, Zsh) and Alfred workflow integration for macOS power users
150 lines • 6.63 kB
JavaScript
import { BaseErrorFormatter } from './Formatter.js';
import { COLORS, SYMBOLS, formatTips, TipStyle } from '../../ui/theme.js';
import { wrapText, termWidth } from '../../ui/width.js';
/**
* Plain text formatter for errors with structured sections
*/
export class PlainTextFormatter extends BaseErrorFormatter {
name = 'plain';
/**
* Format one or more errors for display in plain text
*
* @param errors The error(s) to format
* @param options Formatting options
* @returns Formatted error message
*/
formatError(errors, options) {
const errorArray = Array.isArray(errors) ? errors : [errors];
const sections = [];
const width = termWidth();
const contentWidth = Math.min(width - 4, 76); // Leave some margin, cap at 76 chars
for (const error of errorArray) {
if (sections.length > 0)
sections.push(''); // Add spacing between multiple errors
// Title section with icon
const errorName = this.getErrorName(error);
const errorMessage = this.getErrorMessage(error);
sections.push(COLORS.error(`${SYMBOLS.error} ERROR ${errorName}`));
// Message section (wrapped for readability)
if (errorMessage && errorMessage !== errorName) {
const wrappedMessage = wrapText(errorMessage, contentWidth);
wrappedMessage.forEach(line => {
sections.push(` ${line}`);
});
}
// Cause section (if available)
const cause = this.getErrorCause(error);
if (cause) {
sections.push('');
sections.push(COLORS.muted(`CAUSE ${cause}`));
}
// API errors section
const apiErrors = this.getApiErrors(error);
if (apiErrors?.length) {
sections.push('');
sections.push(COLORS.warn(`${SYMBOLS.warn} DETAILS`));
apiErrors.forEach((apiError) => {
const message = apiError.message || 'Unknown error';
sections.push(` ${SYMBOLS.bullet} ${message}`);
if (apiError.path) {
sections.push(COLORS.muted(` Path: ${apiError.path.join('.')}`));
}
});
}
// Stack trace (debug only)
const stack = this.getStackTrace(error);
if (stack && options?.debug) {
sections.push('');
sections.push(COLORS.muted('STACK'));
// Indent stack trace lines
stack.split('\n').forEach(line => {
sections.push(COLORS.muted(` ${line}`));
});
}
// Request details (debug only)
const request = this.getRequestDetails(error);
if (request && options?.debug) {
sections.push('');
sections.push(COLORS.muted('REQUEST'));
if (request.url)
sections.push(COLORS.muted(` URL: ${request.url}`));
if (request.method)
sections.push(COLORS.muted(` Method: ${request.method}`));
}
// Hints section - context-aware suggestions
const hints = this.getContextualHints(error, errorMessage);
if (hints.length > 0) {
sections.push('');
sections.push(formatTips(hints, TipStyle.FIXES));
}
}
return sections.join('\n');
}
/**
* Get contextual hints based on the error
*/
getContextualHints(_error, message) {
const hints = [];
const lowerMessage = message.toLowerCase();
// Authentication errors
if (lowerMessage.includes('auth') || lowerMessage.includes('unauthorized') ||
lowerMessage.includes('401') || lowerMessage.includes('token')) {
hints.push('Check your token: bktide token --check');
hints.push('Store a new token: bktide token --store');
hints.push('Set token via: export BUILDKITE_API_TOKEN=<your-token>');
}
// Network errors
else if (lowerMessage.includes('econnrefused') || lowerMessage.includes('network') ||
lowerMessage.includes('etimedout') || lowerMessage.includes('dns')) {
hints.push('Check your internet connection');
hints.push('Verify Buildkite API is accessible');
hints.push('Try again with --no-cache to bypass cache');
}
// Permission errors
else if (lowerMessage.includes('permission') || lowerMessage.includes('forbidden') ||
lowerMessage.includes('403')) {
hints.push('Verify you have access to this resource');
hints.push('Check organization permissions');
hints.push('List accessible orgs: bktide orgs');
}
// Not found errors
else if (lowerMessage.includes('not found') || lowerMessage.includes('404')) {
hints.push('Verify the resource exists');
hints.push('Check spelling of organization/pipeline/build names');
hints.push('List available resources with appropriate list command');
}
// Rate limit errors
else if (lowerMessage.includes('rate limit') || lowerMessage.includes('429')) {
hints.push('Wait a moment before retrying');
hints.push('Use --no-cache to avoid repeated API calls');
hints.push('Consider using --count with a smaller value');
}
// Generic hints
else {
hints.push('Run with --debug for detailed error information');
hints.push('Check command syntax: bktide <command> --help');
hints.push('Report issues: https://github.com/your-repo/issues');
}
// Always include debug hint if not already in debug mode
if (!hints.some(h => h.includes('--debug'))) {
hints.push('Use --debug flag for stack trace');
}
return hints;
}
/**
* Try to extract a cause from the error
*/
getErrorCause(error) {
if (error && typeof error === 'object' && 'cause' in error) {
const cause = error.cause;
if (cause instanceof Error) {
return cause.message;
}
if (typeof cause === 'string') {
return cause;
}
}
return null;
}
}
//# sourceMappingURL=PlainTextFormatter.js.map