< Summary - Envilder CLI

Information
Class: src/envilder/apps/cli/errors/CliErrorPresenter.ts
Assembly: Default
File(s): src/envilder/apps/cli/errors/CliErrorPresenter.ts
Tag: 561_41292037308
Line coverage
100%
Covered lines: 25
Uncovered lines: 0
Coverable lines: 25
Total lines: 99
Line coverage: 100%
Branch coverage
100%
Covered branches: 14
Total branches: 14
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  ExecInterruptedError,
 4  ExpiredCredentialsError,
 5  SecretsFetchError,
 6  SsoSessionExpiredError,
 7} from '../../../core/domain/errors/DomainErrors.js';
 8import { describeError } from '../../../core/infrastructure/describeError.js';
 9
 10export function presentError(error: unknown): string {
 3211  if (error instanceof SsoSessionExpiredError) {
 1312    return renderSsoSessionExpired(error);
 13  }
 1914  if (error instanceof ExpiredCredentialsError) {
 115    return renderExpiredCredentials();
 16  }
 1817  if (error instanceof SecretsFetchError) {
 718    return renderSecretsFetchError(error);
 19  }
 1120  if (error instanceof ExecInterruptedError) {
 221    return pc.red(error.message);
 22  }
 923  return renderFallback(error);
 24}
 25
 26function gameOver(title: string): string {
 3027  return `\n${pc.bold(pc.red(`\u{1F4A5} GAME OVER \u2014 ${title}`))}`;
 28}
 29
 30function renderSsoSessionExpired(error: SsoSessionExpiredError): string {
 1331  const body = error.profileName
 32    ? `  ${pc.dim('Your AWS SSO session for profile ')}${pc.magenta(`"${error.profileName}"`)}${pc.dim(' ran out of live
 33    : `  ${pc.dim('Your AWS SSO session ran out of lives.')}`;
 1334  const command = error.profileName
 35    ? `aws sso login --profile ${error.profileName}`
 36    : 'aws sso login';
 37
 1338  return [
 39    gameOver('SSO session expired'),
 40    '',
 41    body,
 42    '',
 43    `  ${pc.bold(pc.yellow('\u2B95 CONTINUE?'))}`,
 44    `  ${pc.dim('   Run:  ')}${pc.cyan(command)}`,
 45    `  ${pc.dim('   then re-run your envilder command.')}`,
 46    '',
 47  ].join('\n');
 48}
 49
 50function renderExpiredCredentials(): string {
 151  return [
 52    gameOver('AWS credentials expired'),
 53    '',
 54    `  ${pc.dim('Your security token ran out of time.')}`,
 55    '',
 56    `  ${pc.bold(pc.yellow('\u2B95 CONTINUE?'))}`,
 57    `  ${pc.dim('   Refresh your credentials and retry ')}${pc.dim('(for SSO: ')}${pc.cyan('aws sso login')}${pc.dim(').
 58    '',
 59  ].join('\n');
 60}
 61
 62function renderFallback(error: unknown): string {
 963  const message = describeError(error);
 964  return [
 65    gameOver('you fell down the wrong pipe!'),
 66    '',
 67    `  ${pc.dim(message)}`,
 68    '',
 69  ].join('\n');
 70}
 71
 772const ACCESS_DENIED_PATTERN = /access.?denied|not authorized|forbidden/i;
 73
 74function isAccessDeniedReason(reason: string): boolean {
 975  return ACCESS_DENIED_PATTERN.test(reason);
 76}
 77
 78function renderSecretsFetchError(error: SecretsFetchError): string {
 779  const rule = pc.red('\u2501'.repeat(60));
 780  const failureLines = error.failures.map(
 81    (failure) =>
 1082      `  ${pc.red('\u2717 ')}${pc.bold(failure.envVar.padEnd(20))}${pc.dim('\u2192 ')}${pc.dim(failure.path)}`,
 83  );
 1084  const reasons = [...new Set(error.failures.map((failure) => failure.reason))];
 785  const reasonLines = reasons.map((reason) =>
 986    isAccessDeniedReason(reason)
 87      ? `     ${pc.bold(pc.red(reason))}`
 88      : `     ${pc.dim(reason)}`,
 89  );
 790  return [
 91    gameOver('some secrets could not be fetched'),
 92    rule,
 93    ...failureLines,
 94    '',
 95    `  ${pc.bold(pc.yellow('\u2B95 WHY?'))}`,
 96    ...reasonLines,
 97    '',
 98  ].join('\n');
 99}