import type { RequestFile, AuthConfig, BodyConfig, HeaderItem, ParamItem, VarItem, AssertionItem, FormField, GraphQLBody } from '../types.js' const INDENT = ' ' function quoteKey(key: string): string { if (/[:{}"' ]/.test(key)) return `"${key.replace(/"/g, '\\"')}"` return key } function dictBlock(name: string, entries: string[]): string { if (entries.length === 0) return `${name} {\n}\n` return `${name} {\n${entries.map(e => `${INDENT}${e}`).join('\n')}\n}\n` } function textBlock(name: string, content: string): string { const trimmed = content.trimEnd() return `${name} {\n${trimmed.split('\n').map(l => `${INDENT}${l}`).join('\n')}\n}\n` } function formatHeaders(headers: HeaderItem[]): string { const entries = headers.map(h => { const prefix = h.disabled ? '~' : '' return `${prefix}${quoteKey(h.name)}: ${h.value}` }) return dictBlock('headers', entries) } function formatParams(params: ParamItem[], type: 'query' | 'path'): string { const filtered = params.filter(p => p.type === type) if (filtered.length === 0) return '' const entries = filtered.map(p => { const prefix = p.disabled ? '~' : '' return `${prefix}${quoteKey(p.name)}: ${p.value}` }) return dictBlock(`params:${type}`, entries) } function formatAuthBlock(auth: AuthConfig): string { const type = auth.type if (type === 'none' || type === 'inherit') return '' if (type === 'basic' && auth.basic) { return dictBlock('auth:basic', [ `username: ${auth.basic.username}`, `password: ${auth.basic.password}`, ]) } if (type === 'bearer' && auth.bearer) { return dictBlock('auth:bearer', [`token: ${auth.bearer.token}`]) } if (type === 'apikey' && auth.apikey) { return dictBlock('auth:apikey', [ `key: ${auth.apikey.key}`, `value: ${auth.apikey.value}`, `placement: ${auth.apikey.placement}`, ]) } if (type === 'digest' && auth.digest) { return dictBlock('auth:digest', [ `username: ${auth.digest.username}`, `password: ${auth.digest.password}`, ]) } if (type === 'ntlm' && auth.ntlm) { const entries = [ `username: ${auth.ntlm.username}`, `password: ${auth.ntlm.password}`, ] if (auth.ntlm.domain) entries.push(`domain: ${auth.ntlm.domain}`) return dictBlock('auth:ntlm', entries) } if (type === 'wsse' && auth.wsse) { return dictBlock('auth:wsse', [ `username: ${auth.wsse.username}`, `password: ${auth.wsse.password}`, ]) } if (type === 'awsv4' && auth.awsv4) { const a = auth.awsv4 const entries = [ `accessKeyId: ${a.accessKeyId}`, `secretAccessKey: ${a.secretAccessKey}`, `sessionToken: ${a.sessionToken ?? ''}`, `service: ${a.service ?? ''}`, `region: ${a.region ?? ''}`, `profileName: ${a.profileName ?? ''}`, ] return dictBlock('auth:awsv4', entries) } if (type === 'oauth2' && auth.oauth2) { const entries = Object.entries(auth.oauth2).map(([k, v]) => `${k}: ${String(v)}`) return dictBlock('auth:oauth2', entries) } return '' } function formatBody(body: BodyConfig): string { const { type, data } = body if (type === 'none' || !data) return '' if (type === 'json' || type === 'text' || type === 'xml' || type === 'sparql') { return textBlock(`body:${type}`, String(data)) } if (type === 'graphql') { const gql = data as GraphQLBody let out = textBlock('body:graphql', gql.query) if (gql.variables) out += '\n' + textBlock('body:graphql:vars', gql.variables) return out } if (type === 'form-urlencoded') { const fields = data as FormField[] const entries = fields.map(f => { const prefix = f.enabled === false ? '~' : '' return `${prefix}${quoteKey(f.name)}: ${f.value}` }) return dictBlock('body:form-urlencoded', entries) } if (type === 'multipart-form') { const fields = data as FormField[] const entries = fields.map(f => { const prefix = f.enabled === false ? '~' : '' const val = f.type === 'file' ? `@file(${f.value})` : f.value return `${prefix}${quoteKey(f.name)}: ${val}` }) return dictBlock('body:multipart-form', entries) } if (type === 'file') { const fields = data as FormField[] const entries = fields.map(f => { const prefix = f.enabled === false ? '~' : '' return `${prefix}file: @file(${f.value})` }) return dictBlock('body:file', entries) } return '' } function formatVars(blockName: string, vars: VarItem[]): string { if (vars.length === 0) return '' const entries = vars.map(v => { const disabled = v.enabled === false ? '~' : '' const local = v.local ? '@' : '' return `${disabled}${local}${v.name}: ${v.value}` }) return dictBlock(blockName, entries) } function formatAssert(assertions: AssertionItem[]): string { if (assertions.length === 0) return '' const entries = assertions.map(a => { const prefix = a.disabled ? '~' : '' const val = a.value !== undefined ? ` ${a.operator} ${a.value}` : ` ${a.operator}` return `${prefix}${a.expression}:${val}` }) return dictBlock('assert', entries) } function bruBodyMode(body?: BodyConfig): string { if (!body || body.type === 'none') return 'none' const map: Record = { json: 'json', text: 'text', xml: 'xml', sparql: 'sparql', graphql: 'graphql', 'form-urlencoded': 'formUrlEncoded', 'multipart-form': 'multipartForm', file: 'file', } return map[body.type] ?? 'none' } export function formatRequest(req: RequestFile): string { const parts: string[] = [] // meta block const metaEntries = [`name: ${req.info.name}`, `type: ${req.info.type}`] if (req.info.seq !== undefined) metaEntries.push(`seq: ${req.info.seq}`) parts.push(dictBlock('meta', metaEntries)) // HTTP method block const method = req.http.method.toLowerCase() const methodBlock = [ `url: ${req.http.url}`, `body: ${bruBodyMode(req.http.body)}`, `auth: ${req.http.auth?.type ?? 'none'}`, ] parts.push(dictBlock(method, methodBlock)) // params const queryParams = formatParams(req.http.params ?? [], 'query') if (queryParams) parts.push(queryParams) const pathParams = formatParams(req.http.params ?? [], 'path') if (pathParams) parts.push(pathParams) // headers if (req.http.headers && req.http.headers.length > 0) { parts.push(formatHeaders(req.http.headers)) } // body if (req.http.body && req.http.body.type !== 'none') { const bodyStr = formatBody(req.http.body) if (bodyStr) parts.push(bodyStr) } // auth block if (req.http.auth && req.http.auth.type !== 'none' && req.http.auth.type !== 'inherit') { const authStr = formatAuthBlock(req.http.auth) if (authStr) parts.push(authStr) } // vars:pre-request const preVars = req.runtime?.vars?.req ?? [] if (preVars.length > 0) parts.push(formatVars('vars:pre-request', preVars)) // vars:post-response const postVars = req.runtime?.vars?.res ?? [] if (postVars.length > 0) parts.push(formatVars('vars:post-response', postVars)) // assert if (req.runtime?.assertions?.length) { parts.push(formatAssert(req.runtime.assertions)) } // script:pre-request const preScript = req.runtime?.scripts?.find(s => s.type === 'before-request') if (preScript) parts.push(textBlock('script:pre-request', preScript.code)) // script:post-response const postScript = req.runtime?.scripts?.find(s => s.type === 'after-response') if (postScript) parts.push(textBlock('script:post-response', postScript.code)) // tests const testsScript = req.runtime?.scripts?.find(s => s.type === 'tests') if (testsScript) parts.push(textBlock('tests', testsScript.code)) // settings if (req.settings) { const s = req.settings const settingsEntries: string[] = [] if (s.encodeUrl !== undefined) settingsEntries.push(`encodeUrl: ${s.encodeUrl}`) if (s.followRedirects !== undefined) settingsEntries.push(`followRedirects: ${s.followRedirects}`) if (s.maxRedirects !== undefined) settingsEntries.push(`maxRedirects: ${s.maxRedirects}`) if (s.timeout !== undefined) settingsEntries.push(`timeout: ${s.timeout}`) if (settingsEntries.length > 0) parts.push(dictBlock('settings', settingsEntries)) } // docs if (req.docs) parts.push(textBlock('docs', req.docs)) return parts.join('\n') }