< Summary - Envilder CLI

Information
Class: src/envilder/apps/cli/errors/CliErrorPresenter.ts
Assembly: Default
File(s): src/envilder/apps/cli/errors/CliErrorPresenter.ts
Tag: 427_29134414720
Line coverage
100%
Covered lines: 23
Uncovered lines: 0
Coverable lines: 23
Total lines: 95
Line coverage: 100%
Branch coverage
100%
Covered branches: 12
Total branches: 12
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

File(s)

src/envilder/apps/cli/errors/CliErrorPresenter.ts

#LineLine coverage
 1import pc from 'picocolors';
 2import {
 3  ExpiredCredentialsError,
 4  SecretsFetchError,
 5  SsoSessionExpiredError,
 6} from '../../../core/domain/errors/DomainErrors.js';
 7import { describeError } from '../../../core/infrastructure/describeError.js';
 8
 9export function presentError(error: unknown): string {
 1510  if (error instanceof SsoSessionExpiredError) {
 811    return renderSsoSessionExpired(error);
 12  }
 713  if (error instanceof ExpiredCredentialsError) {
 114    return renderExpiredCredentials();
 15  }
 616  if (error instanceof SecretsFetchError) {
 417    return renderSecretsFetchError(error);
 18  }
 219  return renderFallback(error);
 20}
 21
 22function gameOver(title: string): string {
 1523  return `\n${pc.bold(pc.red(`\u{1F4A5} GAME OVER \u2014 ${title}`))}`;
 24}
 25
 26function renderSsoSessionExpired(error: SsoSessionExpiredError): string {
 827  const body = error.profileName
 28    ? `  ${pc.dim('Your AWS SSO session for profile ')}${pc.magenta(`"${error.profileName}"`)}${pc.dim(' ran out of live
 29    : `  ${pc.dim('Your AWS SSO session ran out of lives.')}`;
 830  const command = error.profileName
 31    ? `aws sso login --profile ${error.profileName}`
 32    : 'aws sso login';
 33
 834  return [
 35    gameOver('SSO session expired'),
 36    '',
 37    body,
 38    '',
 39    `  ${pc.bold(pc.yellow('\u2B95 CONTINUE?'))}`,
 40    `  ${pc.dim('   Run:  ')}${pc.cyan(command)}`,
 41    `  ${pc.dim('   then re-run your envilder command.')}`,
 42    '',
 43  ].join('\n');
 44}
 45
 46function renderExpiredCredentials(): string {
 147  return [
 48    gameOver('AWS credentials expired'),
 49    '',
 50    `  ${pc.dim('Your security token ran out of time.')}`,
 51    '',
 52    `  ${pc.bold(pc.yellow('\u2B95 CONTINUE?'))}`,
 53    `  ${pc.dim('   Refresh your credentials and retry ')}${pc.dim('(for SSO: ')}${pc.cyan('aws sso login')}${pc.dim(').
 54    '',
 55  ].join('\n');
 56}
 57
 58function renderFallback(error: unknown): string {
 259  const message = describeError(error);
 260  return [
 61    gameOver('you fell down the wrong pipe!'),
 62    '',
 63    `  ${pc.dim(message)}`,
 64    '',
 65  ].join('\n');
 66}
 67
 368const ACCESS_DENIED_PATTERN = /access.?denied|not authorized|forbidden/i;
 69
 70function isAccessDeniedReason(reason: string): boolean {
 671  return ACCESS_DENIED_PATTERN.test(reason);
 72}
 73
 74function renderSecretsFetchError(error: SecretsFetchError): string {
 475  const rule = pc.red('\u2501'.repeat(60));
 476  const failureLines = error.failures.map(
 77    (failure) =>
 778      `  ${pc.red('\u2717 ')}${pc.bold(failure.envVar.padEnd(20))}${pc.dim('\u2192 ')}${pc.dim(failure.path)}`,
 79  );
 780  const reasons = [...new Set(error.failures.map((failure) => failure.reason))];
 481  const reasonLines = reasons.map((reason) =>
 682    isAccessDeniedReason(reason)
 83      ? `     ${pc.bold(pc.red(reason))}`
 84      : `     ${pc.dim(reason)}`,
 85  );
 486  return [
 87    gameOver('some secrets could not be fetched'),
 88    rule,
 89    ...failureLines,
 90    '',
 91    `  ${pc.bold(pc.yellow('\u2B95 WHY?'))}`,
 92    ...reasonLines,
 93    '',
 94  ].join('\n');
 95}