diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts index 6838140..d64e29a 100644 --- a/packages/config/src/index.ts +++ b/packages/config/src/index.ts @@ -118,8 +118,17 @@ export const envSchema = z.object({ .string() .transform((v) => v === "true") .default("false"), - PCAT_SOURCE_DB_URL: z.string().url().optional(), - EMEX_SOURCE_DB_URL: z.string().optional(), // mysql://... — not a strict URL per WHATWG + // docker-compose's `${VAR:-}` substitution always sets the env, even if to + // an empty string. zod's `.optional()` only accepts undefined, so a chained + // `.url()` would reject "" and crash boot — preprocess "" → undefined first. + PCAT_SOURCE_DB_URL: z.preprocess( + (v) => (typeof v === "string" && v.trim() === "" ? undefined : v), + z.string().url().optional(), + ), + EMEX_SOURCE_DB_URL: z.preprocess( + (v) => (typeof v === "string" && v.trim() === "" ? undefined : v), + z.string().optional(), + ), // mysql://... — not a strict URL per WHATWG // Per-source kill switches under the master CATALOG_SOURCE_DB_ENABLED. // EMEX_SOURCE_DB_ENABLED keeps the connection pool alive but, per the // 2026-06-01 safety audit, fetchCategoryParts ALWAYS returns null unless the