/** * `bun run deploy` preflight: refuses to deploy wrangler.jsonc while resource ids are still the all-zero * placeholders or the required production vars are empty, so a first deploy fails here with a readable list * instead of half-way through `wrangler deploy` (DEPLOY-P1). See docs/self-hosting.md "Production setup". */ type WranglerConfig = { vars?: Record; d1_databases?: Array<{ binding: string; database_id?: string }>; kv_namespaces?: Array<{ binding: string; id?: string }>; secrets_store_secrets?: Array<{ binding: string; store_id?: string }>; }; const PLACEHOLDER = /^[0-]*$/; export function deployConfigProblems(config: WranglerConfig): string[] { const problems: string[] = []; const placeholder = (value: string | undefined) => !value || PLACEHOLDER.test(value); for (const db of config.d1_databases ?? []) { if (placeholder(db.database_id)) problems.push(`D1 ${db.binding}: database_id is a placeholder (wrangler d1 create)`); } for (const kv of config.kv_namespaces ?? []) { if (placeholder(kv.id)) problems.push(`KV ${kv.binding}: id is a placeholder (wrangler kv namespace create)`); } for (const secret of config.secrets_store_secrets ?? []) { if (placeholder(secret.store_id)) { problems.push(`Secrets Store ${secret.binding}: store_id is a placeholder (wrangler secrets-store store create)`); } } const vars = config.vars ?? {}; if (!vars.PUBLIC_URL?.startsWith("https://")) problems.push("vars.PUBLIC_URL must be your https:// forge URL"); if (!vars.USERCONTENT_ORIGIN?.startsWith("https://")) { problems.push("vars.USERCONTENT_ORIGIN must be an https:// origin on a separate registrable domain"); } if (vars.GITFOR_LOCAL !== undefined) problems.push("vars.GITFOR_LOCAL must not be set in wrangler.jsonc (dev only)"); return problems; } /** Worth fixing before launch but not worth blocking a deploy over. */ export function deployConfigWarnings(config: WranglerConfig): string[] { const vars = config.vars ?? {}; const warnings: string[] = []; if (!vars.SUPPORT_EMAIL?.trim()) { warnings.push( vars.LEGAL_CONTACT_EMAIL?.trim() ? "vars.SUPPORT_EMAIL is empty; disabled-account notices fall back to LEGAL_CONTACT_EMAIL" : "vars.SUPPORT_EMAIL and LEGAL_CONTACT_EMAIL are empty; disabled-account notices have no support address", ); } return warnings; } function isConfig(value: unknown): value is WranglerConfig { return typeof value === "object" && value !== null && !Array.isArray(value); } if (import.meta.main) { const parsed: unknown = Bun.JSONC.parse(await Bun.file(new URL("../wrangler.jsonc", import.meta.url)).text()); const problems = isConfig(parsed) ? deployConfigProblems(parsed) : ["wrangler.jsonc is not an object"]; for (const warning of isConfig(parsed) ? deployConfigWarnings(parsed) : []) console.warn(`warning: ${warning}`); if (problems.length > 0) { console.error(`wrangler.jsonc is not ready to deploy:\n${problems.map((problem) => ` - ${problem}`).join("\n")}`); console.error('See docs/self-hosting.md "Production setup".'); process.exit(1); } }