From 059a865170335cf1ce19fe062f32676cc773b8aa Mon Sep 17 00:00:00 2001 From: Lewis Carhart Date: Thu, 20 Aug 2026 16:29:06 -0400 Subject: [PATCH] fix(build-func): vendor peer deps needed by @nestjs/swagger at runtime @nestjs/swagger, @nestjs/common, and @nestjs/core declare their runtime deps as peerDependencies, so vendoring only followed `dependencies` and missed reflect-metadata, class-transformer, class-validator, and rxjs. The bundled function crashed with MODULE_NOT_FOUND on every request in production. vendorDeps now walks non-optional peerDependencies too. Verified by booting the built function locally against dev Postgres and confirming GET /openapi.json returns 200 with a real document. --- apps/api/scripts/build-func.mjs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/apps/api/scripts/build-func.mjs b/apps/api/scripts/build-func.mjs index 7c10a9c1a..b06f0c017 100644 --- a/apps/api/scripts/build-func.mjs +++ b/apps/api/scripts/build-func.mjs @@ -30,7 +30,15 @@ const EXTERNALS = [ "cardinal", ]; -const VENDOR_ROOTS = ["express", "@nestjs/swagger", "reflect-metadata"]; +const VENDOR_ROOTS = [ + "express", + "@nestjs/swagger", + "reflect-metadata", + "@nestjs/common", + "@nestjs/core", + "class-transformer", + "class-validator", +]; rmSync(outDir, { recursive: true, force: true }); mkdirSync(funcDir, { recursive: true }); @@ -102,7 +110,18 @@ function vendorDeps(realDir, name, placedDir) { } catch { return; } - for (const depName of Object.keys(pj.dependencies || {})) { + const optionalPeers = new Set( + Object.entries(pj.peerDependenciesMeta || {}) + .filter(([, meta]) => meta.optional) + .map(([depName]) => depName), + ); + const depNames = new Set([ + ...Object.keys(pj.dependencies || {}), + ...Object.keys(pj.peerDependencies || {}).filter( + (depName) => !optionalPeers.has(depName), + ), + ]); + for (const depName of depNames) { const depReal = resolveDep(realDir, name, depName); if (!depReal) { console.warn(` ! not found: ${depName} (needed by ${name})`);