Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
2.1 kB ยท 75 lines
TypeScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576import { safeFetch } from "@openstatus/utils";import { Effect, Schedule } from "effect";
export class WebhookSendError extends Error { readonly httpStatus?: number;
constructor( message: string, opts?: { httpStatus?: number; cause?: unknown }, ) { super(message, { cause: opts?.cause }); this.name = "WebhookSendError"; this.httpStatus = opts?.httpStatus; }}
// Network errors and timeouts (no status) are transient; 5xx and 429 may clear.// Other 4xx are client errors that won't succeed on retry.const isRetryable = (err: WebhookSendError): boolean => err.httpStatus === undefined || err.httpStatus >= 500 || err.httpStatus === 429;
const describeFailure = (status: number) => status >= 300 && status < 400 ? `Webhook redirected (${status}); only 307/308 to the same host are followed` : `Webhook returned ${status}`;
const retryPolicy = { schedule: Schedule.exponential("200 millis").pipe(Schedule.jittered), times: 3, while: isRetryable,};
/** * POST to a webhook with exponential-backoff retries on transient failures. * Rejects with `WebhookSendError` once retries are exhausted. */export function postWebhookWithRetry(opts: { url: string; headers: Record<string, string>; body: string; timeoutMs: number;}): Promise<void> { const send = Effect.tryPromise({ try: (signal) => safeFetch(opts.url, { method: "POST", headers: opts.headers, body: opts.body, signal, }), catch: (cause) => new WebhookSendError("Webhook request failed", { cause }), }).pipe( Effect.timeoutOrElse({ duration: `${opts.timeoutMs} millis`, orElse: () => Effect.fail( new WebhookSendError(`Webhook timed out after ${opts.timeoutMs}ms`), ), }), Effect.flatMap((response) => response.ok ? Effect.void : Effect.fail( new WebhookSendError(describeFailure(response.status), { httpStatus: response.status, }), ), ), Effect.retry(retryPolicy), );
return Effect.runPromise(send);}