| import type { |
| AdapterEnvironmentCheck, |
| AdapterEnvironmentTestContext, |
| AdapterEnvironmentTestResult, |
| } from "../types.js"; |
| import { asString, parseObject } from "../utils.js"; |
|
|
| function summarizeStatus(checks: AdapterEnvironmentCheck[]): AdapterEnvironmentTestResult["status"] { |
| if (checks.some((check) => check.level === "error")) return "fail"; |
| if (checks.some((check) => check.level === "warn")) return "warn"; |
| return "pass"; |
| } |
|
|
| function normalizeMethod(input: string): string { |
| const trimmed = input.trim(); |
| return trimmed.length > 0 ? trimmed.toUpperCase() : "POST"; |
| } |
|
|
| export async function testEnvironment( |
| ctx: AdapterEnvironmentTestContext, |
| ): Promise<AdapterEnvironmentTestResult> { |
| const checks: AdapterEnvironmentCheck[] = []; |
| const config = parseObject(ctx.config); |
| const urlValue = asString(config.url, ""); |
| const method = normalizeMethod(asString(config.method, "POST")); |
|
|
| if (!urlValue) { |
| checks.push({ |
| code: "http_url_missing", |
| level: "error", |
| message: "HTTP adapter requires a URL.", |
| hint: "Set adapterConfig.url to an absolute http(s) endpoint.", |
| }); |
| return { |
| adapterType: ctx.adapterType, |
| status: summarizeStatus(checks), |
| checks, |
| testedAt: new Date().toISOString(), |
| }; |
| } |
|
|
| let url: URL | null = null; |
| try { |
| url = new URL(urlValue); |
| } catch { |
| checks.push({ |
| code: "http_url_invalid", |
| level: "error", |
| message: `Invalid URL: ${urlValue}`, |
| }); |
| } |
|
|
| if (url && url.protocol !== "http:" && url.protocol !== "https:") { |
| checks.push({ |
| code: "http_url_protocol_invalid", |
| level: "error", |
| message: `Unsupported URL protocol: ${url.protocol}`, |
| hint: "Use an http:// or https:// endpoint.", |
| }); |
| } |
|
|
| if (url) { |
| checks.push({ |
| code: "http_url_valid", |
| level: "info", |
| message: `Configured endpoint: ${url.toString()}`, |
| }); |
| } |
|
|
| checks.push({ |
| code: "http_method_configured", |
| level: "info", |
| message: `Configured method: ${method}`, |
| }); |
|
|
| if (url && (url.protocol === "http:" || url.protocol === "https:")) { |
| const controller = new AbortController(); |
| const timeout = setTimeout(() => controller.abort(), 3000); |
| try { |
| const response = await fetch(url, { |
| method: "HEAD", |
| signal: controller.signal, |
| }); |
| if (!response.ok && response.status !== 405 && response.status !== 501) { |
| checks.push({ |
| code: "http_endpoint_probe_unexpected_status", |
| level: "warn", |
| message: `Endpoint probe returned HTTP ${response.status}.`, |
| hint: "Verify the endpoint is reachable from the Paperclip server host.", |
| }); |
| } else { |
| checks.push({ |
| code: "http_endpoint_probe_ok", |
| level: "info", |
| message: "Endpoint responded to a HEAD probe.", |
| }); |
| } |
| } catch (err) { |
| checks.push({ |
| code: "http_endpoint_probe_failed", |
| level: "warn", |
| message: err instanceof Error ? err.message : "Endpoint probe failed", |
| hint: "This may be expected in restricted networks; verify connectivity when invoking runs.", |
| }); |
| } finally { |
| clearTimeout(timeout); |
| } |
| } |
|
|
| return { |
| adapterType: ctx.adapterType, |
| status: summarizeStatus(checks), |
| checks, |
| testedAt: new Date().toISOString(), |
| }; |
| } |
|
|