// Keep acceptance diagnostics useful without copying request or assertion data. const errorNames = new Set(['Error', 'TypeError', 'AbortError', 'TimeoutError']); const networkCodes = new Set([ 'ABORT_ERR', 'ECONNREFUSED', 'ECONNRESET', 'EPIPE', 'ETIMEDOUT', 'ENOTFOUND', 'EAI_AGAIN', 'UND_ERR_CONNECT_TIMEOUT', 'UND_ERR_HEADERS_TIMEOUT', 'UND_ERR_BODY_TIMEOUT', 'UND_ERR_SOCKET', ]); export function requestIdentity(method, path) { const pathname = typeof path === 'string' ? path.split('?')[0] : ''; return { method: method === 'POST' ? 'POST' : 'GET', path: /^\/api\/[a-zA-Z0-9_/-]+$/.test(pathname) ? pathname : '[invalid path]', }; } export function requestFailure(request, error) { const name = errorNames.has(error?.name) ? error.name : 'Error'; const candidate = error?.cause?.code ?? error?.code; const causeCode = networkCodes.has(candidate) ? candidate : undefined; return { ...request, kind: 'request-error', name, ...(causeCode ? { causeCode } : {}) }; } export function failureLocation(error) { const lines = typeof error?.stack === 'string' ? error.stack.split('\n') : []; const matches = lines.flatMap(line => { const match = line.match(/(?:^|\/)tools\/(admin-[a-z0-9-]+-verification\.mjs|verify-admin\.mjs):(\d+):(\d+)(?:\)|$)/); return match ? [`tools/${match[1]}:${match[2]}:${match[3]}`] : []; }); return matches.find(location => !location.startsWith('tools/verify-admin.mjs:')) ?? matches[0]; } export function describeFailure(request, location) { if (!request) return undefined; const prefix = `${request.method} ${request.path}`; const result = request.kind === 'request-error' ? `failed before completing a response (${request.name}${request.causeCode ? `, ${request.causeCode}` : ''})` : `last completed with HTTP ${request.status}`; return `${prefix} ${result}${location ? `; ${location}` : ''}`; }