export type InputRecord = Record export const DEFAULT_ACCESS_KEY_SECRET = 'awsAccessKeyId' export const DEFAULT_SECRET_KEY_SECRET = 'awsSecretAccessKey' export const DEFAULT_SESSION_TOKEN_SECRET = 'awsSessionToken' export const DEFAULT_REGION = 'us-east-1' const ACCOUNT_LABEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/ const ACCESS_KEY_SECRET_PATTERN = /^awsAccessKeyId(?:-[A-Za-z0-9][A-Za-z0-9_-]{0,47})?$/ const SECRET_KEY_SECRET_PATTERN = /^awsSecretAccessKey(?:-[A-Za-z0-9][A-Za-z0-9_-]{0,47})?$/ const SESSION_TOKEN_SECRET_PATTERN = /^awsSessionToken(?:-[A-Za-z0-9][A-Za-z0-9_-]{0,47})?$/ const REGION_PATTERN = /^[a-z]{2}(?:-[a-z]+)+-\d+$/ const SERVICE_PATTERN = /^[a-z0-9][a-z0-9-]{0,47}$/ const BUCKET_PATTERN = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/ export const COMMON_AWS_HOSTS = [ 'sts.amazonaws.com', 'sts.us-east-1.amazonaws.com', 's3.amazonaws.com', 's3.us-east-1.amazonaws.com', 'logs.us-east-1.amazonaws.com', 'monitoring.us-east-1.amazonaws.com', ] as const export function inputRecord(value: unknown): InputRecord { if (value === null || typeof value !== 'object' || Array.isArray(value)) { throw new Error('Input must be an object.') } return value as InputRecord } export function optionalString( input: InputRecord, key: string, ): string | undefined { const value = input[key] if (value === undefined || value === null || value === '') return undefined if (typeof value !== 'string') throw new Error(key + ' must be a string.') return value.trim() } export function requiredString(input: InputRecord, key: string): string { const value = optionalString(input, key) if (!value) throw new Error(key + ' is required.') return value } export function optionalBoolean( input: InputRecord, key: string, ): boolean | undefined { const value = input[key] if (value === undefined || value === null) return undefined if (typeof value !== 'boolean') throw new Error(key + ' must be a boolean.') return value } export function boundedPageSize( value: unknown, fallback = 20, max = 1000, ): number { if (value === undefined || value === null || value === '') return fallback if (!Number.isInteger(value) || Number(value) < 1 || Number(value) > max) { throw new Error( 'pageSize must be an integer from 1 through ' + max + '.', ) } return Number(value) } function accountLabel(value: string | undefined): string | null { const trimmed = (value ?? '').trim() if (!trimmed || trimmed === 'default') return null if (!ACCOUNT_LABEL_PATTERN.test(trimmed)) { throw new Error( 'account must be a short label such as "work" or "live" (letters, numbers, _ or -).', ) } return trimmed } export type AwsAuthOptions = { /** Extra AWS account label. `work` reads `awsAccessKeyId-work` / `awsSecretAccessKey-work`. */ account?: string /** Override the access key secret name. Must be `awsAccessKeyId` or `awsAccessKeyId-