zephyr-agent
Version:
Zephyr plugin agent
90 lines (89 loc) • 3.94 kB
JavaScript
import "node:module";
import { ZEPHYR_API_ENDPOINT, ZE_API_ENDPOINT_HOST, ZE_IS_PREVIEW, safe_json_parse } from "zephyr-edge-contract";
import { ZeErrors, ZephyrError } from "../errors/index.mjs";
import { ze_log } from "../logging/debug.mjs";
import { cleanTokens } from "../node-persist/token.mjs";
import { redactString, redactUrl, safeStringifyForLogging } from "../security/redaction.mjs";
import { fetchWithRetries } from "./fetch-with-retries.mjs";
function applyApiHost(url) {
const is_preview = ZE_IS_PREVIEW();
const ze_api_endpoint_host = ZE_API_ENDPOINT_HOST();
const zephyr_api_endpoint = ZEPHYR_API_ENDPOINT();
if (is_preview && url.host === ze_api_endpoint_host) url.searchParams.set('api_host', zephyr_api_endpoint);
return url;
}
function parseUrl(urlStr) {
if ('string' == typeof urlStr) return applyApiHost(new URL(urlStr));
{
if (urlStr instanceof URL) return applyApiHost(urlStr);
const url = new URL(urlStr.path, urlStr.base);
for (const [key, value] of Object.entries(urlStr.query))url.searchParams.append(key, String(value));
return applyApiHost(url);
}
}
function redactResponse(url, options, data, response, startTime = Date.now()) {
return [
`[${options.method || 'GET'}][${redactUrl(url)}]: ${Date.now() - startTime}ms`,
data?.length ? ` - ${((data.length ?? 0) / 1024).toFixed(2)}kb` : '',
void 0 !== response ? `Response: ${redactString(String(response))}` : '',
`Options: ${safeStringifyForLogging(options)}`
].join('\n');
}
async function makeHttpRequest(url, options = {}, data) {
const startTime = Date.now();
const { credentialToken, skipTokenCleanup = false, ...requestOptions } = options;
try {
const response = await fetchWithRetries(url, {
...requestOptions,
body: data
});
const resText = await response.text();
if (401 === response.status) {
const authenticationError = new ZephyrError(ZeErrors.ERR_AUTH_ERROR, {
message: 'The request authentication is invalid or expired.'
});
if (!skipTokenCleanup) try {
await cleanTokens(credentialToken);
} catch (error) {
ze_log.error('Failed to remove rejected authentication credentials:', error);
}
throw authenticationError;
}
if (403 === response.status) throw new ZephyrError(ZeErrors.ERR_AUTH_FORBIDDEN_ERROR, {
message: 'The authenticated account does not have access to this target.'
});
const message = redactResponse(url, requestOptions, data, resText, startTime);
if ('Not Implemented' === resText.trim()) throw new ZephyrError(ZeErrors.ERR_UNKNOWN, {
message: 'Not implemented yet. Please get in contact with our support.'
});
if (!url.pathname.includes('application/logs')) ze_log.http(message);
const resData = safe_json_parse(resText) ?? resText;
if (!response.status || response.status >= 300) throw new ZephyrError(ZeErrors.ERR_HTTP_ERROR, {
status: response.status,
url: redactUrl(url),
content: 'string' == typeof resData ? redactString(resData) : safeStringifyForLogging(resData),
method: requestOptions.method?.toUpperCase() ?? 'GET'
});
return [
true,
null,
resData
];
} catch (error) {
return [
false,
error
];
}
}
function makeRequest(urlStr, options = {}, data) {
const url = parseUrl(urlStr);
return makeHttpRequest(url, options, data);
}
async function unwrapResponse(response) {
const [ok, error, data] = await response;
if (!ok) throw error;
return data;
}
export { makeHttpRequest, makeRequest, parseUrl, unwrapResponse };
//# sourceMappingURL=http-request.mjs.map