import { awsSignedFetch } from './client.ts' import { mutationPreview } from './helpers.ts' import { redactUnknown, looksLikeCredentialText } from './redact.ts' import { assertService, inputRecord, optionalString, requiredString, type AwsAuthOptions, } from './validation.ts' const WRITE_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']) /** * Escape hatch for a generic SigV4-signed AWS REST call. * * Writes default to dry-run and require `confirm: true`. Responses never * include object bodies that look like credentials. * * @example * import { awsRequest } from 'kody:@kody/aws/request' * const result = await awsRequest({ * service: 'sts', * method: 'POST', * path: '/', * body: 'Action=GetCallerIdentity&Version=2011-06-15', * }) */ export async function awsRequest( params: AwsAuthOptions & { service: string method?: string path?: string host?: string headers?: Record query?: Record body?: string dryRun?: boolean confirm?: boolean }, ) { const input = inputRecord(params) const service = assertService(requiredString(input, 'service')) const method = (optionalString(input, 'method') ?? 'GET').toUpperCase() const path = optionalString(input, 'path') ?? '/' const host = optionalString(input, 'host') const body = optionalString(input, 'body') const mutating = WRITE_METHODS.has(method) if (mutating) { const preview = mutationPreview(input, { method, service, region: optionalString(input, 'region') ?? 'us-east-1', host: host ?? service + '.amazonaws.com', path, body: body ? { bodyLength: body.length } : null, }) if (preview) return preview } const result = await awsSignedFetch({ ...params, service, method, path, host, headers: (input.headers as Record | undefined) ?? {}, query: (input.query as Record | undefined) ?? {}, body, }) const looksJson = result.text.trim().startsWith('{') const looksXml = result.text.trim().startsWith('<') let parsed: unknown = null if (looksJson) { try { parsed = redactUnknown(JSON.parse(result.text)) } catch { parsed = null } } const credentialLike = looksLikeCredentialText(result.text) return { ok: true, status: result.status, service, region: result.region, host: result.host, method, path, body: credentialLike || (!looksJson && !looksXml) ? null : parsed ?? redactUnknown(result.text.slice(0, 4000)), bodyOmitted: credentialLike || (!looksJson && !looksXml && result.text.length > 0), } } export default awsRequest