diff --git a/apps/backend/api/config-github-apply.ts b/apps/backend/api/config-github-apply.ts new file mode 100644 index 000000000..59deed829 --- /dev/null +++ b/apps/backend/api/config-github-apply.ts @@ -0,0 +1,8 @@ +import app from "../dist/vercel.mjs"; + +export const config = { + runtime: "nodejs", + maxDuration: 800, +}; + +export default app; diff --git a/apps/backend/api/config-github-commit.ts b/apps/backend/api/config-github-commit.ts new file mode 100644 index 000000000..74b64ab32 --- /dev/null +++ b/apps/backend/api/config-github-commit.ts @@ -0,0 +1,8 @@ +import app from "../dist/vercel.mjs"; + +export const config = { + runtime: "nodejs", + maxDuration: 120, +}; + +export default app; diff --git a/apps/backend/src/server/app.ts b/apps/backend/src/server/app.ts index 047278972..2bc0c5dba 100644 --- a/apps/backend/src/server/app.ts +++ b/apps/backend/src/server/app.ts @@ -178,6 +178,33 @@ import.meta.vitest?.test("API version migrations do not expose their internal re } }); +import.meta.vitest?.test("uncaught dispatch errors use the global sanitized error boundary", async ({ expect }) => { + const { vi } = import.meta.vitest!; + vi.stubEnv("NODE_ENV", "production"); + vi.stubEnv("HEXCLAVE_ARTIFICIAL_DEVELOPMENT_DELAY_MS", "1"); + vi.stubEnv("STACK_ARTIFICIAL_DEVELOPMENT_DELAY_MS", "1"); + + try { + const response = await app.handle(new Request("http://localhost/api/v1")); + + expect({ + status: response.status, + body: await response.text(), + contentType: response.headers.get("content-type"), + contentTypeOptions: response.headers.get("x-content-type-options"), + }).toMatchInlineSnapshot(` + { + "body": "Internal Server Error", + "contentType": "text/plain; charset=utf-8", + "contentTypeOptions": "nosniff", + "status": 500, + } + `); + } finally { + vi.unstubAllEnvs(); + } +}); + function homeHtml() { const devStatsLink = getNodeEnvironment() === "development" ? `
Dev Stats
` diff --git a/apps/backend/src/server/vercel-config.test.ts b/apps/backend/src/server/vercel-config.test.ts new file mode 100644 index 000000000..b83eb08b5 --- /dev/null +++ b/apps/backend/src/server/vercel-config.test.ts @@ -0,0 +1,31 @@ +import { readFile } from "node:fs/promises"; +import { describe, expect, it } from "vitest"; + +describe("Vercel function durations", () => { + it("preserves the route-specific limits from the Next.js deployment", async () => { + const [vercelJson, applyEntry, commitEntry] = await Promise.all([ + readFile(new URL("../../vercel.json", import.meta.url), "utf8"), + readFile(new URL("../../api/config-github-apply.ts", import.meta.url), "utf8"), + readFile(new URL("../../api/config-github-commit.ts", import.meta.url), "utf8"), + ]); + + expect(JSON.parse(vercelJson)).toMatchObject({ + rewrites: [ + { + source: "/api/:version/internal/config/github/apply", + destination: "/api/config-github-apply", + }, + { + source: "/api/:version/internal/config/github/commit", + destination: "/api/config-github-commit", + }, + { + source: "/(.*)", + destination: "/api", + }, + ], + }); + expect(applyEntry).toContain("maxDuration: 800"); + expect(commitEntry).toContain("maxDuration: 120"); + }); +}); diff --git a/apps/backend/vercel.json b/apps/backend/vercel.json index 921bc8f0d..fb7b45f13 100644 --- a/apps/backend/vercel.json +++ b/apps/backend/vercel.json @@ -2,6 +2,8 @@ "$schema": "https://openapi.vercel.sh/vercel.json", "framework": null, "rewrites": [ + { "source": "/api/:version/internal/config/github/apply", "destination": "/api/config-github-apply" }, + { "source": "/api/:version/internal/config/github/commit", "destination": "/api/config-github-commit" }, { "source": "/(.*)", "destination": "/api" } ], "crons": [