/* oxlint-disable executor/no-error-constructor, executor/no-try-catch-or-throw -- boundary: deployment CLI connection acquisition */ import { setTimeout } from "node:timers/promises"; const MAX_ATTEMPTS = 7; const RETRY_DELAY_MS = 10_000; /** * Validate the deploy transport without logging credentials. PlanetScale schema * migrations use the direct endpoint because code migrations hold session locks. */ export const directDatabaseUrl = (value: string): string => { let url: URL; try { url = new URL(value); } catch { throw new Error("DATABASE_URL must be a valid PostgreSQL URL"); } if (url.protocol !== "postgres:" && url.protocol !== "postgresql:") { throw new Error("DATABASE_URL must use the postgres or postgresql protocol"); } if (url.hostname.endsWith(".psdb.cloud") && url.port !== "" && url.port !== "5432") { throw new Error("PlanetScale deploy scripts require the direct endpoint on port 5432"); } return value; }; /** * Open the CLI's single connection before starting work. Retry only PostgreSQL * admission failures (53300), at most six times with ten seconds between tries. * The caller must set connect_timeout and close the client on every exit. * Migration and readiness mutations remain outside this retry boundary. */ export const waitForDatabaseConnection = async ( sql: { readonly unsafe: (query: string) => PromiseLike }, options: { readonly log: (message: string) => void; readonly sleep?: (milliseconds: number) => Promise; }, ): Promise => { const sleep = options.sleep ?? ((milliseconds: number) => setTimeout(milliseconds)); for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) { try { await sql.unsafe("SELECT 1"); return; } catch (cause) { const isCapacityError = typeof cause === "object" && cause !== null && "code" in cause && cause.code === "53300"; if (!isCapacityError || attempt === MAX_ATTEMPTS) throw cause; options.log( `Database connection capacity is full (53300). Retrying connection ${attempt}/${MAX_ATTEMPTS - 1} in 10s; no work has started.`, ); await sleep(RETRY_DELAY_MS); } } };