Files
cloudflare__vinext/tests/pages-page-handler.test.ts
T
James Anderson f5355964aa fix(pages-router): set x-nextjs-deployment-id on SSG _next/data responses (#1863)
* fix(pages-router): set x-nextjs-deployment-id on SSG _next/data responses

Pages Router `/_next/data/<buildId>/<page>.json` responses were missing the
`x-nextjs-deployment-id` response header that Next.js sets on these routes
when a deployment ID is configured. The client router reads this header to
detect when a new deployment has been rolled out and triggers a hard
navigation for deployment-skew protection.

Root cause: the `isDataReq` short-circuit path in `pages-page-handler.ts`
that builds the JSON envelope (`buildNextDataJsonResponse`) never injected
the header. Next.js's `pages-handler.ts` sets `NEXT_NAV_DEPLOYMENT_ID_HEADER`
on every `isNextDataRequest` response (success, redirect, and notFound cases).

Fix: read `process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID`
(the runtime-available deployment ID, inlined at build time by Vite's define
transform) and set `x-nextjs-deployment-id` on the `_next/data` response
headers when a deployment ID is configured.

Maps to test: `pages ssg data deployment skew - hard navigate when a new
deployment occurs > header with deployment id > header is set on data routes`.

Fixes #1829

* fix(pages-router): complete x-nextjs-deployment-id parity for _next/data (bonk #1829)

Address all three ask-bonk findings on PR #1863:

1. Incomplete parity — add x-nextjs-deployment-id to redirect and notFound _next/data
   exits in pages-page-data.ts (buildPagesRedirectResponse, buildPagesDataNotFoundResponse).
   Thread deploymentId through ResolvePagesPageDataOptions and pass it from
   pages-page-handler.ts, matching the success-path pattern already in that file.

2. Dev/prod parity — patch dev-server.ts to set x-nextjs-deployment-id on all three
   _next/data exits: the writeGsspRedirect isDataReq path, the getStaticPaths/
   getServerSideProps/getStaticProps notFound isDataReq paths, and the success
   JSON envelope short-circuit (isDataReq block). Mirrors the prod handler.

3. Add regression tests asserting x-nextjs-deployment-id is present on _next/data
   success / redirect / notFound responses in both pages-page-data.test.ts (unit,
   via deploymentId option) and pages-page-handler.test.ts (integration, via
   process.env.__VINEXT_DEPLOYMENT_ID), and absent when no env var is set.

* fix(pages-router): add x-nextjs-deployment-id to wrong-buildId/route-miss _next/data 404s (#1829)

Fixes the deployment-skew gap identified in review: `buildNextDataNotFoundResponse` now
includes the `x-nextjs-deployment-id` header so all three wrong-buildId/route-miss exits
(normalizePagesDataRequest, prod-server Node path, pages-page-handler route-miss) surface
the deployment ID to the client router. Adds unit tests for the new header on all relevant
exits.

* fix(pages-router): set deployment-id header on remaining two dev-mode _next/data 404 exits (#1829)

Patches the stale-buildId 404 in the vite-dev middleware (index.ts) and
the route-miss data 404 in createSSRHandler (dev-server.ts) to set
x-nextjs-deployment-id, matching prod parity via buildNextDataNotFoundResponse().

* test(pages-router): cover dev-mode _next/data deployment-id header exits

Drive the two dev-only exits flagged in review through the real fixture
dev server: the wrong-buildId data 404 in the plugin middleware
(index.ts) and createSSRHandler's route-miss data 404 (dev-server.ts),
plus the dev success path and the no-deployment-id case.

* fix(pages-router): skip deployment-id header on /_error and /500 data success responses

Next.js pages-handler.ts guards the success-path x-nextjs-deployment-id
injection with `!isErrorPage && !is500Page`. Mirror that exclusion in
both the prod success short-circuit (pages-page-handler.ts) and the dev
one (dev-server.ts), and lock it in with a prod unit test (/_error and
/500) plus a dev integration test (/500).
2026-06-11 00:39:16 +00:00

604 lines
23 KiB
TypeScript

/**
* Unit tests for the Pages Router render orchestrator.
*
* Tests the behavior of `createPagesPageHandler` through stub closures,
* verifying route matching, 404/500 fallback, _next/data envelope,
* i18n redirect, 405 method check, and internal-error guard.
*/
import { describe, it, expect, vi } from "vite-plus/test";
import { createPagesPageHandler } from "../packages/vinext/src/server/pages-page-handler.js";
import type { CreatePagesPageHandlerOptions } from "../packages/vinext/src/server/pages-page-handler.js";
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function makeRequest(pathname = "/", method = "GET"): Request {
return new Request(`http://localhost${pathname}`, { method });
}
function makePageModule(overrides: Record<string, unknown> = {}): Record<string, unknown> {
return { default: () => null, ...overrides };
}
type PageRoute = {
pattern: string;
patternParts: string[];
isDynamic: boolean;
params: string[];
module: Record<string, unknown>;
filePath: string;
};
function makeRoute(pattern: string, module: Record<string, unknown> = makePageModule()): PageRoute {
return {
pattern,
patternParts: pattern === "/" ? [] : pattern.split("/").filter(Boolean),
isDynamic: pattern.includes(":"),
params: [],
module,
filePath: `/project/pages${pattern === "/" ? "/index" : pattern}.tsx`,
};
}
// Default stubs — most tests override only the pieces they care about.
function makeOpts(
overrides: Partial<CreatePagesPageHandlerOptions> = {},
): CreatePagesPageHandlerOptions {
const pageRoutes: PageRoute[] = overrides.pageRoutes ?? [makeRoute("/")];
return {
pageRoutes,
errorPageRoute: null,
matchRoute: (url, routes) => {
const p = url.split("?")[0];
const route = routes.find((r) => r.pattern === p || r.pattern === p.replace(/\/$/, ""));
return route ? { route, params: {} } : null;
},
i18nConfig: null,
vinextConfig: {
basePath: "",
assetPrefix: "",
trailingSlash: false,
disableOptimizedLoading: true,
},
buildId: "test-build-id",
hasMiddleware: false,
appAssetPath: null,
hasRewrites: false,
setSSRContext: null,
getPagesNavigationIsReadyFromSerializedState: null,
setI18nContext: null,
wrapWithRouterContext: null,
resetSSRHead: undefined,
getSSRHeadHTML: undefined,
setDocumentInitialHead: undefined,
flushPreloads: undefined,
getFontLinks: () => [],
getFontStyles: () => [],
getFontPreloads: () => [],
renderToReadableStream: async (_element) => {
const encoder = new TextEncoder();
return new ReadableStream({
start(controller) {
controller.enqueue(encoder.encode("<html><body>page</body></html>"));
controller.close();
},
});
},
renderIsrPassToStringAsync: async (_element) => "<html><body>isr</body></html>",
safeJsonStringify: (v) => JSON.stringify(v),
sanitizeDestination: (d) => d,
createPageElement: (_PageComp, _AppComp, _props) => null,
enhancePageElement: (_PageComp, _AppComp, _props, _opts) => null,
AppComponent: null,
DocumentComponent: null,
...overrides,
};
}
// ---------------------------------------------------------------------------
// Route miss → 404 fallback
// ---------------------------------------------------------------------------
describe("createPagesPageHandler — route miss", () => {
it("returns default 404 when no custom 404 page and no _error page", async () => {
const handler = createPagesPageHandler(makeOpts({ pageRoutes: [] }));
const res = await handler(makeRequest("/missing"), "/missing", null, null, null);
expect(res.status).toBe(404);
const body = await res.text();
expect(body).toContain("This page could not be found");
});
it("renders custom /404 page on route miss", async () => {
const notFoundModule = makePageModule();
const routes = [makeRoute("/404", notFoundModule)];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
matchRoute: (url, r) => {
const p = url.split("?")[0];
const route = r.find((rt) => rt.pattern === p);
return route ? { route, params: {} } : null;
},
}),
);
const res = await handler(makeRequest("/nonexistent"), "/nonexistent", null, null, null);
// Custom 404 renders successfully (200 body, 404 status via override)
expect(res.status).toBe(404);
});
it("returns _next/data 404 JSON on data request route miss", async () => {
const handler = createPagesPageHandler(makeOpts({ pageRoutes: [] }));
const res = await handler(makeRequest("/missing"), "/missing", null, null, { isDataReq: true });
expect(res.status).toBe(404);
const ct = res.headers.get("content-type");
expect(ct).toContain("application/json");
});
});
// ---------------------------------------------------------------------------
// Page has no default export
// ---------------------------------------------------------------------------
describe("createPagesPageHandler — no default export", () => {
it("returns 500 when page module has no default export", async () => {
const routes = [makeRoute("/", {})]; // no `default`
const handler = createPagesPageHandler(makeOpts({ pageRoutes: routes }));
const res = await handler(makeRequest("/"), "/", null, null, null);
expect(res.status).toBe(500);
});
});
// ---------------------------------------------------------------------------
// _next/data JSON envelope
// ---------------------------------------------------------------------------
describe("createPagesPageHandler — _next/data", () => {
it("detects /_next/data URL and returns JSON envelope", async () => {
const routes = [makeRoute("/about")];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
}),
);
const dataUrl = "/_next/data/test-build-id/about.json";
const res = await handler(makeRequest(dataUrl), dataUrl, null, null, null);
expect(res.status).toBe(200);
const ct = res.headers.get("content-type");
expect(ct).toContain("application/json");
const body = (await res.json()) as Record<string, unknown>;
expect(body).toHaveProperty("pageProps");
});
it("returns 404 JSON for _next/data with wrong buildId", async () => {
const handler = createPagesPageHandler(makeOpts());
const badUrl = "/_next/data/wrong-build-id/about.json";
const res = await handler(makeRequest(badUrl), badUrl, null, null, null);
expect(res.status).toBe(404);
const ct = res.headers.get("content-type");
expect(ct).toContain("application/json");
});
});
// ---------------------------------------------------------------------------
// 405 method check
// ---------------------------------------------------------------------------
describe("createPagesPageHandler — 405 method check", () => {
it("returns 405 for POST to a static page (no getServerSideProps)", async () => {
const routes = [makeRoute("/about", makePageModule())];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
}),
);
const res = await handler(makeRequest("/about", "POST"), "/about", null, null, null);
expect(res.status).toBe(405);
const allow = res.headers.get("Allow");
expect(allow).toContain("GET");
});
it("skips 405 check when page exports getServerSideProps", async () => {
// The 405 guard only applies to static (no-gSSP) pages. When gSSP is
// present, resolvePagesPageMethodResponse returns null and the render
// pipeline proceeds. Verify by spying on the module method check result.
// We use a module that returns { props: {} } from gSSP so the render
// can complete without hitting renderToReadableStream errors.
const gsspModule = makePageModule({
getServerSideProps: async () => ({ props: {} }),
});
const routes = [makeRoute("/about", gsspModule)];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
}),
);
const res = await handler(makeRequest("/about", "POST"), "/about", null, null, null);
// Must not be 405 — the method check is bypassed for gSSP pages
expect(res.status).not.toBe(405);
// Must not be 405 Allow header either
expect(res.headers.get("Allow")).toBeNull();
});
it("does not 405 on /404 pattern (error pages are exempt)", async () => {
const routes = [makeRoute("/404", makePageModule())];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
}),
);
const res = await handler(makeRequest("/404", "POST"), "/404", null, null, null);
expect(res.status).not.toBe(405);
});
});
// ---------------------------------------------------------------------------
// i18n redirect — 307 short-circuit from resolvePagesI18nRequest
// ---------------------------------------------------------------------------
describe("createPagesPageHandler — i18n redirect", () => {
// getLocaleRedirect fires when pathname === "/" and the Accept-Language
// header prefers a non-default locale. The handler must return a 307
// before attempting any route match.
it("returns 307 when i18n locale detection produces a redirect", async () => {
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: [makeRoute("/")],
i18nConfig: {
locales: ["en", "fr"],
defaultLocale: "en",
},
}),
);
// Visit / with Accept-Language: fr — resolvePagesI18nRequest redirects to /fr
const req = new Request("http://localhost/", {
headers: { "accept-language": "fr" },
});
const res = await handler(req, "/", null, null, null);
expect(res.status).toBe(307);
expect(res.headers.get("location")).toContain("/fr");
});
it("does not redirect when locale prefix is already present", async () => {
const routes = [makeRoute("/fr/about")];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
i18nConfig: {
locales: ["en", "fr"],
defaultLocale: "en",
},
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
}),
);
const res = await handler(makeRequest("/fr/about"), "/fr/about", null, null, null);
// Not a 307 — the locale prefix is already present
expect(res.status).not.toBe(307);
});
});
// ---------------------------------------------------------------------------
// Internal error guard (prevents infinite recursion on error pages)
// ---------------------------------------------------------------------------
describe("createPagesPageHandler — internal error guard", () => {
it("returns 500 text when __isInternalErrorRender is set and render throws", async () => {
const errorRoute = makeRoute("/_error", makePageModule());
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: [errorRoute],
errorPageRoute: errorRoute,
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
// Cause an error during render
renderToReadableStream: async () => {
throw new Error("render failure");
},
}),
);
const res = await handler(makeRequest("/_error"), "/_error", null, null, {
__isInternalErrorRender: true,
__forcedRoute: errorRoute,
});
expect(res.status).toBe(500);
const body = await res.text();
expect(body).toBe("Internal Server Error");
});
it("falls back to 500 text on data request even without __isInternalErrorRender", async () => {
// Data requests skip renderToReadableStream (JSON envelope path), so we
// need to throw earlier — in createPageElement, which is called inside
// resolvePagesPageData to build the element for ISR/SSR rendering.
const routes = [
makeRoute("/about", {
...makePageModule(),
getStaticProps: async () => {
throw new Error("gssp failure");
},
}),
];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
// getFontPreloads is called before the isDataReq branch.
// Throwing here triggers the catch block regardless of isDataReq.
getFontPreloads: () => {
throw new Error("font failure");
},
}),
);
// isDataReq=true → no error-page recursion, direct 500
const res = await handler(makeRequest("/about"), "/about", null, null, { isDataReq: true });
expect(res.status).toBe(500);
});
});
// ---------------------------------------------------------------------------
// renderErrorPageOnMiss: false — no 404 recursion
// ---------------------------------------------------------------------------
describe("createPagesPageHandler — renderErrorPageOnMiss: false", () => {
it("returns default 404 without recursing when renderErrorPageOnMiss=false", async () => {
const handler = createPagesPageHandler(makeOpts({ pageRoutes: [] }));
const res = await handler(makeRequest("/missing"), "/missing", null, null, {
renderErrorPageOnMiss: false,
});
expect(res.status).toBe(404);
const body = await res.text();
expect(body).toContain("This page could not be found");
});
});
// ---------------------------------------------------------------------------
// setSSRContext called
// ---------------------------------------------------------------------------
describe("createPagesPageHandler — SSR context", () => {
it("calls setSSRContext with the matched route pattern", async () => {
const setSSRContext = vi.fn();
const routes = [makeRoute("/about")];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
setSSRContext,
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
}),
);
await handler(makeRequest("/about"), "/about", null, null, null);
expect(setSSRContext).toHaveBeenCalled();
const ctx = setSSRContext.mock.calls[0][0] as Record<string, unknown>;
expect(ctx.pathname).toBe("/about");
});
});
// ---------------------------------------------------------------------------
// x-nextjs-deployment-id header — _next/data success / redirect / notFound
// ---------------------------------------------------------------------------
describe("createPagesPageHandler — x-nextjs-deployment-id", () => {
const DEPLOYMENT_ID = "prod-deploy-xyz";
it("sets x-nextjs-deployment-id on _next/data success response when env var is set", async () => {
const savedId = process.env.__VINEXT_DEPLOYMENT_ID;
process.env.__VINEXT_DEPLOYMENT_ID = DEPLOYMENT_ID;
try {
const routes = [makeRoute("/about")];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
}),
);
const dataUrl = "/_next/data/test-build-id/about.json";
const res = await handler(makeRequest(dataUrl), dataUrl, null, null, null);
expect(res.status).toBe(200);
expect(res.headers.get("x-nextjs-deployment-id")).toBe(DEPLOYMENT_ID);
} finally {
if (savedId === undefined) {
delete process.env.__VINEXT_DEPLOYMENT_ID;
} else {
process.env.__VINEXT_DEPLOYMENT_ID = savedId;
}
}
});
it("sets x-nextjs-deployment-id on _next/data redirect response when env var is set", async () => {
const savedId = process.env.__VINEXT_DEPLOYMENT_ID;
process.env.__VINEXT_DEPLOYMENT_ID = DEPLOYMENT_ID;
try {
const routes = [
makeRoute("/about", {
...makePageModule(),
getServerSideProps: async () => ({
redirect: { destination: "/new-about", permanent: false },
}),
}),
];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
}),
);
const dataUrl = "/_next/data/test-build-id/about.json";
const res = await handler(makeRequest(dataUrl), dataUrl, null, null, null);
expect(res.status).toBe(200);
expect(res.headers.get("content-type")).toContain("application/json");
expect(res.headers.get("x-nextjs-deployment-id")).toBe(DEPLOYMENT_ID);
const body = (await res.json()) as { pageProps: Record<string, unknown> };
expect(body.pageProps.__N_REDIRECT).toBe("/new-about");
} finally {
if (savedId === undefined) {
delete process.env.__VINEXT_DEPLOYMENT_ID;
} else {
process.env.__VINEXT_DEPLOYMENT_ID = savedId;
}
}
});
it("sets x-nextjs-deployment-id on _next/data notFound response when env var is set", async () => {
const savedId = process.env.__VINEXT_DEPLOYMENT_ID;
process.env.__VINEXT_DEPLOYMENT_ID = DEPLOYMENT_ID;
try {
const routes = [
makeRoute("/about", {
...makePageModule(),
getServerSideProps: async () => ({ notFound: true }),
}),
];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
}),
);
const dataUrl = "/_next/data/test-build-id/about.json";
const res = await handler(makeRequest(dataUrl), dataUrl, null, null, null);
expect(res.status).toBe(404);
expect(res.headers.get("x-nextjs-deployment-id")).toBe(DEPLOYMENT_ID);
} finally {
if (savedId === undefined) {
delete process.env.__VINEXT_DEPLOYMENT_ID;
} else {
process.env.__VINEXT_DEPLOYMENT_ID = savedId;
}
}
});
it("omits x-nextjs-deployment-id on _next/data responses when no deployment env var is set", async () => {
const savedVinext = process.env.__VINEXT_DEPLOYMENT_ID;
const savedNext = process.env.NEXT_DEPLOYMENT_ID;
delete process.env.__VINEXT_DEPLOYMENT_ID;
delete process.env.NEXT_DEPLOYMENT_ID;
try {
const routes = [makeRoute("/about")];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
}),
);
const dataUrl = "/_next/data/test-build-id/about.json";
const res = await handler(makeRequest(dataUrl), dataUrl, null, null, null);
expect(res.status).toBe(200);
expect(res.headers.get("x-nextjs-deployment-id")).toBeNull();
} finally {
if (savedVinext !== undefined) process.env.__VINEXT_DEPLOYMENT_ID = savedVinext;
if (savedNext !== undefined) process.env.NEXT_DEPLOYMENT_ID = savedNext;
}
});
it("omits x-nextjs-deployment-id on _next/data success responses for /_error and /500", async () => {
const savedId = process.env.__VINEXT_DEPLOYMENT_ID;
process.env.__VINEXT_DEPLOYMENT_ID = DEPLOYMENT_ID;
try {
// Next.js pages-handler.ts guards the success-path header with
// `!isErrorPage && !is500Page`; mirror that exclusion here.
for (const pattern of ["/_error", "/500"]) {
const routes = [makeRoute(pattern)];
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: routes,
matchRoute: (url, r) => {
const route = r.find((rt) => rt.pattern === url.split("?")[0]);
return route ? { route, params: {} } : null;
},
}),
);
const dataUrl = `/_next/data/test-build-id${pattern}.json`;
const res = await handler(makeRequest(dataUrl), dataUrl, null, null, null);
expect(res.status).toBe(200);
expect(res.headers.get("x-nextjs-deployment-id")).toBeNull();
}
} finally {
if (savedId === undefined) {
delete process.env.__VINEXT_DEPLOYMENT_ID;
} else {
process.env.__VINEXT_DEPLOYMENT_ID = savedId;
}
}
});
it("sets x-nextjs-deployment-id on _next/data wrong-buildId 404 response", async () => {
const savedId = process.env.__VINEXT_DEPLOYMENT_ID;
process.env.__VINEXT_DEPLOYMENT_ID = DEPLOYMENT_ID;
try {
const handler = createPagesPageHandler(makeOpts());
const badUrl = "/_next/data/stale-build-id/about.json";
const res = await handler(makeRequest(badUrl), badUrl, null, null, null);
expect(res.status).toBe(404);
expect(res.headers.get("x-nextjs-deployment-id")).toBe(DEPLOYMENT_ID);
} finally {
if (savedId === undefined) {
delete process.env.__VINEXT_DEPLOYMENT_ID;
} else {
process.env.__VINEXT_DEPLOYMENT_ID = savedId;
}
}
});
it("sets x-nextjs-deployment-id on _next/data route-miss 404 response", async () => {
const savedId = process.env.__VINEXT_DEPLOYMENT_ID;
process.env.__VINEXT_DEPLOYMENT_ID = DEPLOYMENT_ID;
try {
// Handler with no routes for /unknown — will hit the route-miss data exit.
const handler = createPagesPageHandler(
makeOpts({
pageRoutes: [makeRoute("/about")],
matchRoute: () => null, // always misses
}),
);
const dataUrl = "/_next/data/test-build-id/unknown.json";
const res = await handler(makeRequest(dataUrl), dataUrl, null, null, null);
expect(res.status).toBe(404);
expect(res.headers.get("x-nextjs-deployment-id")).toBe(DEPLOYMENT_ID);
} finally {
if (savedId === undefined) {
delete process.env.__VINEXT_DEPLOYMENT_ID;
} else {
process.env.__VINEXT_DEPLOYMENT_ID = savedId;
}
}
});
});