cdk-nextjs 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +2404 -2723
- package/.jsii.tabl.json +1 -1
- package/API.md +1681 -1930
- package/CLAUDE.md +2 -1
- package/README.md +429 -130
- package/assets/lambdas/post-deploy/post-deploy.lambda/index.js +148 -169
- package/assets/lambdas/redeploy-stage/redeploy-stage.lambda/index.js +875 -0
- package/docs/adapter-runtime.md +185 -0
- package/docs/breaking-changes.md +667 -4
- package/docs/caching-guide.md +156 -112
- package/docs/cdk-nextjs-cfn-exec-policy.json +2 -1
- package/docs/development-guide.md +1 -1
- package/docs/harness-coverage.md +598 -0
- package/docs/init-cache-deployment.md +32 -1
- package/docs/next-build-output-guide.md +30 -20
- package/examples/README.md +5 -1
- package/lib/adapter/__fixtures__/app-playground-base-path.json +4926 -0
- package/lib/adapter/__fixtures__/app-playground.json +4917 -0
- package/lib/adapter/__fixtures__/pages-i18n.json +1728 -0
- package/lib/adapter/adapter.mjs +1548 -185
- package/lib/adapter/adapter.test.js +106 -0
- package/lib/adapter/aws-cache-store.d.ts +489 -0
- package/lib/adapter/aws-cache-store.js +1004 -0
- package/lib/adapter/aws-cache-store.test.d.ts +1 -0
- package/lib/adapter/aws-cache-store.test.js +361 -0
- package/lib/adapter/build-outputs.d.ts +80 -0
- package/lib/adapter/build-outputs.js +852 -0
- package/lib/adapter/build-outputs.test.d.ts +1 -0
- package/lib/adapter/build-outputs.test.js +861 -0
- package/lib/adapter/cache-handler.d.ts +9 -2
- package/lib/adapter/cache-handler.js +72 -96
- package/lib/adapter/cache-handler.mjs +18956 -17774
- package/lib/adapter/cache-handler.test.js +263 -31
- package/lib/adapter/cache-kinds.d.ts +32 -0
- package/lib/adapter/cache-kinds.js +35 -0
- package/lib/adapter/cache-kinds.test.d.ts +1 -0
- package/lib/adapter/cache-kinds.test.js +69 -0
- package/lib/adapter/cache-utils.d.ts +90 -0
- package/lib/adapter/cache-utils.js +199 -4
- package/lib/adapter/cache-utils.test.d.ts +1 -0
- package/lib/adapter/cache-utils.test.js +179 -0
- package/lib/adapter/function-groups.d.ts +215 -0
- package/lib/adapter/function-groups.js +1099 -0
- package/lib/adapter/function-groups.test.d.ts +1 -0
- package/lib/adapter/function-groups.test.js +743 -0
- package/lib/adapter/init-cache.d.ts +6 -0
- package/lib/adapter/init-cache.js +245 -0
- package/lib/adapter/init-cache.test.d.ts +1 -0
- package/lib/adapter/init-cache.test.js +137 -0
- package/lib/adapter/local-file-cache-handler.d.ts +23 -0
- package/lib/adapter/local-file-cache-handler.js +52 -1
- package/lib/adapter/memory-cache-handler.d.ts +26 -17
- package/lib/adapter/memory-cache-handler.js +32 -50
- package/lib/adapter/memory-cache-handler.test.js +105 -85
- package/lib/adapter/s3-cache-handler.d.ts +179 -40
- package/lib/adapter/s3-cache-handler.js +807 -277
- package/lib/adapter/s3-cache-handler.test.js +1186 -31
- package/lib/adapter/use-cache-common.d.ts +90 -0
- package/lib/adapter/use-cache-common.js +250 -0
- package/lib/adapter/use-cache-common.test.d.ts +1 -0
- package/lib/adapter/use-cache-common.test.js +646 -0
- package/lib/adapter/use-cache-default-handler.d.ts +24 -0
- package/lib/adapter/use-cache-default-handler.js +90 -0
- package/lib/adapter/use-cache-default-handler.mjs +38645 -0
- package/lib/adapter/use-cache-handlers.test.d.ts +1 -0
- package/lib/adapter/use-cache-handlers.test.js +349 -0
- package/lib/adapter/use-cache-remote-handler.d.ts +26 -0
- package/lib/adapter/use-cache-remote-handler.js +186 -0
- package/lib/{image-optimization/handler.mjs → adapter/use-cache-remote-handler.mjs} +33572 -38798
- package/lib/generated-structs/OptionalNextjsApiProps.d.ts +59 -0
- package/lib/generated-structs/OptionalNextjsApiProps.js +3 -0
- package/lib/generated-structs/OptionalNextjsCacheProps.d.ts +15 -6
- package/lib/generated-structs/OptionalNextjsCacheProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsContainersProps.d.ts +25 -11
- package/lib/generated-structs/OptionalNextjsContainersProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsDistributionProps.d.ts +31 -9
- package/lib/generated-structs/OptionalNextjsDistributionProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsPostDeployProps.d.ts +8 -0
- package/lib/generated-structs/OptionalNextjsPostDeployProps.js +1 -1
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.d.ts +6 -4
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.js +1 -1
- package/lib/index.d.ts +6 -7
- package/lib/index.js +2 -4
- package/lib/lambdas/post-deploy/prune-cache-bucket.d.ts +7 -3
- package/lib/lambdas/post-deploy/prune-cache-bucket.js +57 -71
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.js +114 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.js +23 -31
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.js +71 -0
- package/lib/lambdas/post-deploy/prune-s3.d.ts +14 -5
- package/lib/lambdas/post-deploy/prune-s3.js +41 -12
- package/lib/lambdas/post-deploy/prune-s3.test.js +97 -18
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.d.ts +13 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.js +55 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.d.ts +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.js +68 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.d.ts +20 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.js +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.d.ts +1 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.js +93 -0
- package/lib/nextjs-api.d.ts +102 -18
- package/lib/nextjs-api.js +338 -38
- package/lib/nextjs-api.test.js +537 -1
- package/lib/nextjs-build/global-containers.Dockerfile +15 -10
- package/lib/nextjs-build/nextjs-build.d.ts +339 -63
- package/lib/nextjs-build/nextjs-build.js +684 -327
- package/lib/nextjs-build/nextjs-build.test.d.ts +1 -0
- package/lib/nextjs-build/nextjs-build.test.js +412 -0
- package/lib/nextjs-build/patch-fetch.js +1 -1
- package/lib/nextjs-build/patch-fetch.test.js +166 -11
- package/lib/nextjs-build/regional-containers.Dockerfile +17 -10
- package/lib/nextjs-cache.d.ts +82 -4
- package/lib/nextjs-cache.js +186 -10
- package/lib/nextjs-cache.test.js +176 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.d.ts +21 -8
- package/lib/nextjs-compute/nextjs-compute-base-props.js +41 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-compute-base-props.test.js +44 -0
- package/lib/nextjs-compute/nextjs-containers.d.ts +19 -0
- package/lib/nextjs-compute/nextjs-containers.js +57 -20
- package/lib/nextjs-compute/nextjs-functions.d.ts +122 -7
- package/lib/nextjs-compute/nextjs-functions.js +113 -67
- package/lib/nextjs-compute/nextjs-functions.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-functions.test.js +99 -0
- package/lib/nextjs-distribution.d.ts +194 -23
- package/lib/nextjs-distribution.js +535 -62
- package/lib/nextjs-distribution.test.d.ts +1 -0
- package/lib/nextjs-distribution.test.js +697 -0
- package/lib/nextjs-post-deploy.d.ts +13 -5
- package/lib/nextjs-post-deploy.js +13 -10
- package/lib/nextjs-post-deploy.test.d.ts +1 -0
- package/lib/nextjs-post-deploy.test.js +34 -0
- package/lib/nextjs-static-assets.d.ts +17 -4
- package/lib/nextjs-static-assets.js +22 -5
- package/lib/root-constructs/nextjs-base-construct.d.ts +102 -58
- package/lib/root-constructs/nextjs-base-construct.js +139 -47
- package/lib/root-constructs/nextjs-global-containers.d.ts +18 -20
- package/lib/root-constructs/nextjs-global-containers.js +20 -71
- package/lib/root-constructs/nextjs-global-functions.d.ts +26 -28
- package/lib/root-constructs/nextjs-global-functions.js +52 -70
- package/lib/root-constructs/nextjs-regional-containers.d.ts +18 -5
- package/lib/root-constructs/nextjs-regional-containers.js +8 -15
- package/lib/root-constructs/nextjs-regional-functions.d.ts +22 -18
- package/lib/root-constructs/nextjs-regional-functions.js +8 -26
- package/lib/runtime/api-gateway-path.d.ts +34 -0
- package/lib/runtime/api-gateway-path.js +71 -0
- package/lib/runtime/api-gateway-path.test.d.ts +1 -0
- package/lib/runtime/api-gateway-path.test.js +65 -0
- package/lib/runtime/case-redirect.d.ts +22 -0
- package/lib/runtime/case-redirect.js +63 -0
- package/lib/runtime/case-redirect.test.d.ts +1 -0
- package/lib/runtime/case-redirect.test.js +32 -0
- package/lib/runtime/core.d.ts +234 -0
- package/lib/runtime/core.js +1213 -0
- package/lib/runtime/core.test.d.ts +1 -0
- package/lib/runtime/core.test.js +1678 -0
- package/lib/runtime/deployment-root.d.ts +13 -0
- package/lib/runtime/deployment-root.js +29 -0
- package/lib/runtime/dispatch.d.ts +331 -0
- package/lib/runtime/dispatch.js +751 -0
- package/lib/runtime/dispatch.test.d.ts +1 -0
- package/lib/runtime/dispatch.test.js +853 -0
- package/lib/runtime/entrypoints.d.ts +57 -0
- package/lib/runtime/entrypoints.js +115 -0
- package/lib/runtime/http/abort-signal.d.ts +6 -0
- package/lib/runtime/http/abort-signal.js +19 -0
- package/lib/runtime/http/abort-signal.test.d.ts +1 -0
- package/lib/runtime/http/abort-signal.test.js +49 -0
- package/lib/runtime/http/node-server.d.ts +38 -0
- package/lib/runtime/http/node-server.js +99 -0
- package/lib/runtime/http/node-server.test.d.ts +1 -0
- package/lib/runtime/http/node-server.test.js +96 -0
- package/lib/runtime/http/node-sink.d.ts +16 -0
- package/lib/runtime/http/node-sink.js +48 -0
- package/lib/runtime/http/node-sink.test.d.ts +1 -0
- package/lib/runtime/http/node-sink.test.js +66 -0
- package/lib/runtime/http/request.d.ts +62 -0
- package/lib/runtime/http/request.js +134 -0
- package/lib/runtime/http/request.test.d.ts +1 -0
- package/lib/runtime/http/request.test.js +103 -0
- package/lib/runtime/http/response.d.ts +77 -0
- package/lib/runtime/http/response.js +229 -0
- package/lib/runtime/http/response.test.d.ts +1 -0
- package/lib/runtime/http/response.test.js +178 -0
- package/lib/runtime/http/sink.d.ts +73 -0
- package/lib/runtime/http/sink.js +188 -0
- package/lib/runtime/http/sink.test.d.ts +1 -0
- package/lib/runtime/http/sink.test.js +257 -0
- package/lib/runtime/image-utils.d.ts +78 -0
- package/lib/runtime/image-utils.js +129 -0
- package/lib/runtime/image-utils.test.d.ts +1 -0
- package/lib/runtime/image-utils.test.js +237 -0
- package/lib/runtime/image.d.ts +76 -0
- package/lib/runtime/image.js +296 -0
- package/lib/runtime/image.test.d.ts +1 -0
- package/lib/runtime/image.test.js +405 -0
- package/lib/runtime/lambda-event.d.ts +4 -0
- package/lib/runtime/lambda-event.js +113 -0
- package/lib/runtime/lambda-event.test.d.ts +1 -0
- package/lib/runtime/lambda-event.test.js +119 -0
- package/lib/runtime/lambda.mjs +41595 -0
- package/lib/runtime/load-module.d.ts +8 -0
- package/lib/runtime/load-module.js +35 -0
- package/lib/runtime/manifest.d.ts +215 -0
- package/lib/runtime/manifest.js +118 -0
- package/lib/runtime/middleware.d.ts +43 -0
- package/lib/runtime/middleware.js +113 -0
- package/lib/runtime/middleware.test.d.ts +1 -0
- package/lib/runtime/middleware.test.js +259 -0
- package/lib/runtime/next-modules.d.ts +55 -0
- package/lib/runtime/next-modules.js +124 -0
- package/lib/runtime/next-modules.test.d.ts +1 -0
- package/lib/runtime/next-modules.test.js +34 -0
- package/lib/runtime/public-files.d.ts +32 -0
- package/lib/runtime/public-files.js +117 -0
- package/lib/runtime/public-files.test.d.ts +1 -0
- package/lib/runtime/public-files.test.js +97 -0
- package/lib/runtime/server.mjs +41588 -0
- package/lib/runtime/static-files.d.ts +50 -0
- package/lib/runtime/static-files.js +266 -0
- package/lib/runtime/static-files.test.d.ts +1 -0
- package/lib/runtime/static-files.test.js +118 -0
- package/lib/runtime/util.d.ts +28 -0
- package/lib/runtime/util.js +79 -0
- package/lib/utils/base-path.d.ts +106 -13
- package/lib/utils/base-path.js +254 -41
- package/lib/utils/base-path.test.js +219 -51
- package/lib/utils/get-architecture.d.ts +7 -8
- package/lib/utils/get-architecture.js +12 -13
- package/lib/utils/zip-directory.d.ts +14 -0
- package/lib/utils/zip-directory.js +121 -0
- package/lib/utils/zip-directory.test.d.ts +1 -0
- package/lib/utils/zip-directory.test.js +99 -0
- package/package.json +22 -3
- package/scripts/capture-adapter-fixture.mjs +325 -0
- package/scripts/e2e-deploy.sh +398 -0
- package/scripts/e2e-harness/README.md +707 -0
- package/scripts/e2e-harness/app.js +370 -0
- package/scripts/e2e-harness/common.sh +249 -0
- package/scripts/e2e-harness/screen.mjs +302 -0
- package/scripts/e2e-harness/stage-proxy.mjs +145 -0
- package/scripts/e2e-harness/type-filters.mjs +47 -0
- package/scripts/e2e-logs.sh +75 -0
- package/scripts/e2e-offline.sh +91 -0
- package/scripts/e2e-sweep.sh +370 -0
- package/scripts/e2e-warm.sh +114 -0
- package/scripts/zero-config-build.mjs +418 -0
- package/docs/1k-concurrent-users-executions.png +0 -0
- package/docs/plans/adapter-runtime-release.md +0 -362
- package/docs/plans/phase-0-image-function-opt-in.md +0 -295
- package/lib/generated-structs/OptionalDockerImageFunctionProps.d.ts +0 -425
- package/lib/generated-structs/OptionalDockerImageFunctionProps.js +0 -3
- package/lib/image-optimization/handler-utils.d.ts +0 -34
- package/lib/image-optimization/handler-utils.js +0 -83
- package/lib/image-optimization/handler-utils.test.js +0 -178
- package/lib/nextjs-build/functions.Dockerfile +0 -31
- package/lib/nextjs-compute/nextjs-image-function.d.ts +0 -53
- package/lib/nextjs-compute/nextjs-image-function.js +0 -59
- package/lib/utils/experimental-flags.d.ts +0 -24
- package/lib/utils/experimental-flags.js +0 -30
- /package/lib/{image-optimization/handler-utils.test.d.ts → adapter/adapter.test.d.ts} +0 -0
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
# Next.js compatibility harness — coverage record
|
|
2
|
+
|
|
3
|
+
What has been run from [vercel/next.js's own e2e suite][harness] against a real
|
|
4
|
+
cdk-nextjs deployment, and — for anything that fails — why, with a verdict on
|
|
5
|
+
whether the failure is acceptable or a bug to fix.
|
|
6
|
+
|
|
7
|
+
`test/deploy-tests-manifest.json` is the machine-readable half and the source of
|
|
8
|
+
truth for what runs: `rules.include` (whole files) and `suites` (a file minus
|
|
9
|
+
named cases). Nothing goes in either that has not been watched to pass. Its
|
|
10
|
+
`excluded-notes` carries a one-line verdict for everything deliberately left out,
|
|
11
|
+
pointing back here. See `scripts/e2e-harness/README.md` for how the harness works
|
|
12
|
+
and how to screen a new candidate.
|
|
13
|
+
|
|
14
|
+
## Verdict key
|
|
15
|
+
|
|
16
|
+
| Verdict | Meaning |
|
|
17
|
+
| ----------------- | ---------------------------------------------------------------------------------------------- |
|
|
18
|
+
| **pass** | Runs and passes against a real deployment. In `rules.include`. |
|
|
19
|
+
| **fixed** | Failed, was root-caused to a cdk-nextjs defect, and the defect is fixed. |
|
|
20
|
+
| **bug** | Fails for a cdk-nextjs reason. Not acceptable. Fix it, then add the file. |
|
|
21
|
+
| **upstream** | Fails because of a bug in next.js or its fixture, not reachable from here. |
|
|
22
|
+
| **unsupported** | Fails because of a deliberate, documented product limitation or design choice. Acceptable. |
|
|
23
|
+
| **CDN-inherent** | Fails for a reason true of any CDN-fronted deployment. Acceptable. |
|
|
24
|
+
| **architectural** | Fails for a reason true of any distributed (multi-instance) cache handler. Acceptable. |
|
|
25
|
+
| **no signal** | next.js skips or gates the file itself, or the fixture cannot be built here. Nothing measured. |
|
|
26
|
+
|
|
27
|
+
"Acceptable" means understood and not worth fixing. Every **bug** should come back
|
|
28
|
+
as a regression test once fixed.
|
|
29
|
+
|
|
30
|
+
## Status
|
|
31
|
+
|
|
32
|
+
As of 2026-09-23, against next.js 16.3.5. The screening funnel lives in the
|
|
33
|
+
manifest's `screening` block, regenerated by
|
|
34
|
+
`node scripts/e2e-harness/screen.mjs --next ../next.js --write` (`--check` to
|
|
35
|
+
verify, which every e2e-harness run does in shard 1).
|
|
36
|
+
|
|
37
|
+
| Of next.js's 1134 e2e files | Count |
|
|
38
|
+
| ----------------------------- | ----- |
|
|
39
|
+
| edge-free (the hard ceiling) | 931 |
|
|
40
|
+
| candidates after every screen | **0** |
|
|
41
|
+
| deployed and screened | 472 |
|
|
42
|
+
| in `rules.include` | 427 |
|
|
43
|
+
| plus, per-case, in `suites` | 10 |
|
|
44
|
+
|
|
45
|
+
The candidate pool is empty: every file is included, included per-case, or
|
|
46
|
+
disqualified by a screen, and every disqualification that is a cdk-nextjs decision
|
|
47
|
+
has a verdict below. The job now is keeping that true as next.js adds files and as
|
|
48
|
+
each scheduled run re-runs what is already included.
|
|
49
|
+
|
|
50
|
+
| Verdict | Files |
|
|
51
|
+
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
|
+
| pass | 427 whole files, plus the passing cases of the 10 `suites` files |
|
|
53
|
+
| fixed | the defects in the table below |
|
|
54
|
+
| bug | none open |
|
|
55
|
+
| upstream | 2 — `rewrites-destination-query-array`, `incremental-cache-path-traversal` |
|
|
56
|
+
| unsupported | 4 whole files (`prerender-encoding`, `middleware-fetches-with-any-http-method`, `revalidate-dynamic`, `proxy-readable-toweb`), plus the skipped cases of `trailingslash`, `revalidate-path-with-rewrites` and the 6 `invalid-static-asset-404-*` files, and 203 files the edge screen disqualifies without deploying |
|
|
57
|
+
| CDN-inherent | the 4 skipped cases of `dynamic-route-interpolation` |
|
|
58
|
+
| architectural | the 2 skipped `resume-data-cache` cases |
|
|
59
|
+
| no signal | 49 — 2 gated by next.js, 29 `skipDeployment` or stubbed in deploy mode, 18 `next-config-ts-native-ts` files that cannot be built here |
|
|
60
|
+
|
|
61
|
+
### Which types the harness proves
|
|
62
|
+
|
|
63
|
+
The harness runs on all four types: the schedule on `NextjsGlobalFunctions`, the
|
|
64
|
+
other three by `workflow_dispatch` (`nextjs_type`) or by hand, with
|
|
65
|
+
`NextjsRegionalFunctions` behind `scripts/e2e-harness/stage-proxy.mjs`, which puts
|
|
66
|
+
back the stage prefix the harness would otherwise drop. The findings whose
|
|
67
|
+
plumbing differs per deployment type are also back-filled into
|
|
68
|
+
`examples/e2e-tests` (run against `examples/app-playground` on all four types,
|
|
69
|
+
every commit) — the "4-type spec" column of the table under "Fixed". What stays
|
|
70
|
+
harness-only is what cannot coexist with the App Router fixture in one
|
|
71
|
+
`next.config.ts` (`trailingSlash`, `assetPrefix`, `i18n`, the Pages Router) or has
|
|
72
|
+
no per-type divergence (defect 25).
|
|
73
|
+
|
|
74
|
+
### The 2026-09-29 runs, all four types
|
|
75
|
+
|
|
76
|
+
The whole manifest, 15 shards per type, after the fixes of 2026-09-28/29:
|
|
77
|
+
|
|
78
|
+
| Type | Result |
|
|
79
|
+
| -------------------------- | ----------------------------------------------------------------------------------- |
|
|
80
|
+
| `NextjsRegionalContainers` | 437 / 437, no retries |
|
|
81
|
+
| `NextjsRegionalFunctions` | 434 / 435 (2 files in `excludeByType`); the one failure the `next-form` flake below |
|
|
82
|
+
| `NextjsGlobalContainers` | 411 / 414; all 3 failures and 23 unrun files on one shard, one hung `cdk deploy` |
|
|
83
|
+
|
|
84
|
+
What those fixes were, in the order the runs found them: the Global types'
|
|
85
|
+
`NEXT_E2E_TEST_TIMEOUT` raised to 900000 for CloudFront updates; the Regional
|
|
86
|
+
Functions runtime keeping `//` (`hydration`); the harness URL's host lowercased and
|
|
87
|
+
the container health check moved to `/_next/static/` (it had been caught by
|
|
88
|
+
fixtures' dynamic routes); and Global Containers building absolute URLs with the
|
|
89
|
+
viewer's protocol (`action-forward-loop`: CloudFront reaches the ALB over HTTP
|
|
90
|
+
without a certificate, so `x-forwarded-proto` said `http`).
|
|
91
|
+
|
|
92
|
+
### Running on `NextjsRegionalFunctions`
|
|
93
|
+
|
|
94
|
+
Run once, on 2026-09-24, behind the local stage proxy (`scripts/e2e-harness/README.md`,
|
|
95
|
+
"Running on `NextjsRegionalFunctions`"): 22 files picked for what differs on that
|
|
96
|
+
type, 17 green, no cdk-nextjs defect. The failures are what to expect on a re-run:
|
|
97
|
+
|
|
98
|
+
| File | Result | Why |
|
|
99
|
+
| --------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
100
|
+
| `app-dir/actions-streaming` | fails | The action `fetch`es `location.origin` from the server — `127.0.0.1` from inside the Lambda. Local proxy only. |
|
|
101
|
+
| `app-dir/app-basepath` | 10 / 13 | Action `redirect()` streamed by a server-side fetch of the app's own origin (`ECONNREFUSED 127.0.0.1`). Same cause. |
|
|
102
|
+
| `app-dir/redirect-rewrite-dynamic-basepath` | 1 / 2 | Same, surfacing as a 500 from the runtime's error path. |
|
|
103
|
+
| `invalid-static-asset-404-{app,pages}-asset-prefix` | fail | Nothing serves `assetPrefix` on the regional types; cdk-nextjs warns at synth (`isAssetPrefixUnserved`). |
|
|
104
|
+
|
|
105
|
+
Then in CI on 2026-09-28, the whole manifest (437 files) in 10 shards: 428 green,
|
|
106
|
+
8 of them only on retry, mostly `basePath` fixtures (the ~90s stage settle in the
|
|
107
|
+
README). Beyond the table above, the same two causes failed four more files, and
|
|
108
|
+
one failure was a cdk-nextjs defect. Everything but `hydration` is now in the
|
|
109
|
+
manifest's `rules.excludeByType["regional-functions"]`:
|
|
110
|
+
|
|
111
|
+
| File | Result | Why |
|
|
112
|
+
| --------------------------------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
113
|
+
| `app-dir/asset-prefix-absolute`, `app-dir/asset-prefix-with-basepath` | fail | `assetPrefix` is unserved on the regional types, as above; each logs the synth warning. |
|
|
114
|
+
| `next-form/default/app-dir` | fails | The action `redirect()`s, streamed by a server-side fetch of the app's own origin. Same cause as `app-basepath`. |
|
|
115
|
+
| `app-dir/action-forward-loop` | fails | Action forwarding fetches the request's own origin (`createForwardedActionResponse`, from `initURL`) — `127.0.0.1`. Same cause. |
|
|
116
|
+
| `hydration` | 1 / 3 | **Defect, fixed:** API Gateway collapses `//` to `/` in `event.path`, so Next.js never redirected it and the page failed to hydrate at `//`. `apiGatewayRequestPath` now restores the slashes. |
|
|
117
|
+
|
|
118
|
+
## Passing — in `rules.include`
|
|
119
|
+
|
|
120
|
+
The list is the manifest's `rules.include`; it is not duplicated here. Notes worth
|
|
121
|
+
having when one of them goes red:
|
|
122
|
+
|
|
123
|
+
- **Retry passes.** `app-dir/router-autoscroll`, `app-dir/bfcache-regression` and
|
|
124
|
+
`app-dir/third-parties` each failed attempt 0 once on their own scroll or embed
|
|
125
|
+
assertions, with no cdk-nextjs error, and passed on retry 1. The harness runs
|
|
126
|
+
`--retries 1`. Suspect these first if a run goes red for no other reason.
|
|
127
|
+
- **Slowest file:** `instrumentation-client-hook`, ~677s.
|
|
128
|
+
- **Weakest coverage:** the three `chrome-devtools-workspace` files assert only
|
|
129
|
+
`status: 404` in deploy mode (the dev-only endpoint must not be reachable in
|
|
130
|
+
production). Real, but thin.
|
|
131
|
+
- **Collapsed families:** `app-dir/next-config-ts/*` (21 files) and
|
|
132
|
+
`app-dir/next-config-ts-native-mts/*` (18) — each a distinct `next build` whose
|
|
133
|
+
resolved config the adapter reads back out of `required-server-files.json`; and
|
|
134
|
+
`app-dir/scss/*` (29 files), which on our side all test one thing: the emitted
|
|
135
|
+
stylesheet reaching the browser from S3. The only Sass coverage in the project.
|
|
136
|
+
- **`handle-non-hoisted-swc-helpers`** is the one fixture pinning
|
|
137
|
+
`packageManager: npm`. Deploy mode sets `skipInstall`, so its non-hoisted
|
|
138
|
+
`node_modules` layout is never exercised — green means it builds and serves. See
|
|
139
|
+
"npm-pinned fixtures" under Debugging.
|
|
140
|
+
- **`deprecation-warnings`** was once red on a harness bug: `scripts/e2e-logs.sh`
|
|
141
|
+
printed more than next.js treats as build logs. Its output _is_ `next.cliOutput`.
|
|
142
|
+
- **`test-utils-tests/basic`** tests `nextTestSetup` itself — the harness contract,
|
|
143
|
+
not the adapter.
|
|
144
|
+
|
|
145
|
+
## Passing in part — in `suites`
|
|
146
|
+
|
|
147
|
+
A `suites` file is included with the cases in its `failed` array skipped
|
|
148
|
+
(next.js's `test/get-test-filter.js` turns them into a negative
|
|
149
|
+
`--testNamePattern`, so names must be full jest names, `describe` title included).
|
|
150
|
+
Use this instead of dropping a whole file when the residual failures have a
|
|
151
|
+
verdict below.
|
|
152
|
+
|
|
153
|
+
| File | Cases | Skipped, and why |
|
|
154
|
+
| ------------------------------------------------------------------ | ---------- | ----------------------------------------------------------------------------------------------- |
|
|
155
|
+
| `app-dir/trailingslash` | 6 / 8 | 2 `should revalidate a page with generated static params` — unsupported (async invalidation) |
|
|
156
|
+
| `app-dir/resume-data-cache` | 3 / 5 | 2 `should have consistent data between static and dynamic renders` — architectural |
|
|
157
|
+
| `dynamic-route-interpolation` | 3 / 7 | 4 requesting unencoded `[`/`]` — CDN-inherent |
|
|
158
|
+
| `app-dir/revalidate-path-with-rewrites` | 1 / 2 | `static page` — unsupported (async invalidation) |
|
|
159
|
+
| `invalid-static-asset-404-app` (+ `-asset-prefix`, `-base-path`) | 2 / 3 each | `should return 404 with plain text when fetching invalid asset path` — S3's XML 404, unsupported |
|
|
160
|
+
| `invalid-static-asset-404-pages` (+ `-asset-prefix`, `-base-path`) | 2 / 3 each | Same case, same reason |
|
|
161
|
+
| `next-form/default/app-dir` | 13 / 14 | `flakey`: `should soft-navigate on submit and show the prefetched loading state` — timing |
|
|
162
|
+
|
|
163
|
+
The `next-form` case submits right after the page loads and waits for the loading
|
|
164
|
+
state the router prefetched; where the prefetch has not landed, `#loading` never
|
|
165
|
+
renders. It failed both attempts on Regional Functions and on both Containers
|
|
166
|
+
types at least once in the 2026-09-28/29 runs, and passed on each of them in
|
|
167
|
+
another, with no cdk-nextjs error.
|
|
168
|
+
|
|
169
|
+
## Fixed
|
|
170
|
+
|
|
171
|
+
`#` labels a row for the rest of this doc. **23**, **37** and **38** were found by
|
|
172
|
+
the four-type suite (`examples/e2e-tests`) rather than the harness: 23 is under
|
|
173
|
+
"Stale-while-revalidate after a tag revalidation", 37 and 38 follow the table.
|
|
174
|
+
There is no **28**: the number was skipped, not withdrawn. So 34 of the 37 fixed
|
|
175
|
+
defects came from the harness and 3 from the four-type suite.
|
|
176
|
+
|
|
177
|
+
"4-type spec" names the `examples/e2e-tests/src/*.test.ts` file guarding it on all
|
|
178
|
+
four deployment types; `—` means harness or unit tests only.
|
|
179
|
+
|
|
180
|
+
| # | Symptom (as a test sees it) | Root cause | Fix / guard | 4-type spec |
|
|
181
|
+
| --- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
182
|
+
| 1 | Every POST with a body (server actions) → `403 InvalidSignatureException` | `patchFetchInClientJs` ran inside `runNextBuild()`, so `skipBuild: true` shipped an unpatched client with no `x-amz-content-sha256` | Call moved to the `NextjsBuild` constructor, marker-guarded against double wrap. The harness is the `skipBuild` guard | `request-methods` (in-page POST ok, raw POST 403) |
|
|
183
|
+
| 2 | RSC responses labeled `text/html`; navigation duration `NaN`; action `redirect()` with no head | Seeded the HTML prerender's `initialHeaders` (`content-type`, `vary`, `x-nextjs-prerender`/`-postponed`) into `APP_PAGE` entries | `appPageCacheHeaders`, `src/adapter/cache-utils.ts` | `rsc-navigation` |
|
|
184
|
+
| 3 | Empty streamed response: Function URL drops the prelude (bare `200 octet-stream`), API Gateway `502` | `RESPONSE_STREAM` with zero bytes after the prelude delimiter | `ResponseSink.padEmptyBody` (one space byte), `src/runtime/http/sink.test.ts` | `response-bodies`, `request-methods`, `server-action-redirect` |
|
|
185
|
+
| 4 | Client navigation to `/` → 404, `x-nextjs-cache: HIT`, empty body | `groupPrerenders` rebuilt `${route}.rsc`; the root's payload is `/index.rsc` | `groupPrerenders`, `src/adapter/cache-utils.ts` | `rsc-navigation` |
|
|
186
|
+
| 5 | `[id]/[id2]`: second param is the first's value plus `2` | `@next/routing` substitutes `$nxtP*` placeholders in insertion order, so a prefixing name is mangled | `repairRouteParamQuery`, `src/runtime/dispatch.ts` | `routing-params` |
|
|
187
|
+
| 6 | `trailingSlash: true` — every canonical `/a/` URL 404s | Output pathnames never carry the slash; `@next/routing` matches exactly | `withTrailingSlashVariants`, `Dispatcher.normalizePathname` | — |
|
|
188
|
+
| 7 | `next build` fails: "encountered uncached or runtime data during prerendering" (`cacheComponents` + handler) | Build-time cache handler was write-only; the final prerender pass re-issued fetches | `LocalFileCacheHandler.get`; `cache-handler.test.ts` | — |
|
|
189
|
+
| 8 | `revalidateTag` never reaches a build-time prerender; tagged entries evicted right after storing | Handler read a `tags` array only runtime `set` writes, not `x-next-cache-tags`; mapping rows stamped `revalidatedAt = now` | `entryTags`, `checkIfRevalidated`, per-tag marker row — `src/adapter/s3-cache-handler.ts` | — |
|
|
190
|
+
| 9 | Origin re-renders after `revalidatePath`, browser still gets the old page (`x-cache: Hit from cloudfront`) | Invalidation paths came only from tag mapping rows, which build-time prerenders lack | `implicitTagPath`/`invalidationVariants`; the build's tag manifest (s3-cache-handler) | — |
|
|
191
|
+
| 10 | Fixture _n+1_ serves fixture _n_'s pages (another app's `/_not-found`) | With `NEXT_DEPLOYMENT_ID` set, next's `BUILD_ID` is the constant `build-TfctsWXpff2fKS`; our cache is partitioned by build id | `NextjsBuild.buildId` suffixes the manifest's `deploymentId` (`src/nextjs-build/nextjs-build.ts`) | — |
|
|
192
|
+
| 11 | Unmatched optional catchall renders a segment `"undefined"` | `@next/routing` reports an unfilled param as a present key with value `undefined` | `repairRouteParamQuery` second pass; `dispatch.test.ts` | `routing-params` |
|
|
193
|
+
| 12 | `robots.txt`, `sitemap.xml`, `manifest.webmanifest` served as `application/octet-stream` | Static metadata staged as `<route>.body`; `send` types by extension | `setBodyFileContentType`, `src/runtime/static-files.ts` | `metadata-routes` |
|
|
194
|
+
| 13 | Root-params app (no `app/layout.tsx`) 404s at every URL | next gates the `dynamicRoutes` rule on draft cookies; concrete prerenders were not registered | `addPrerenderPathnames`, `src/adapter/build-outputs.ts` | `draft-mode`, `cookies` (cookie half only) |
|
|
195
|
+
| 14 | PPR fallback shell rendered per request (`(runtime)` where `(buildtime)` expected) | Seeding loop skipped every prerender group whose pathname contains `[` | Prerender-group loop in `writeInitCache`, `src/adapter/init-cache.ts`; `init-cache.test.ts` | — |
|
|
196
|
+
| 15 | Custom `pages/404` replaced by the built-in 404 | `statusTarget` tried `/_error` before an invocable `/404` | `statusTarget`; `dispatch.test.ts` "prefers an invocable custom /404 over /\_error" | — |
|
|
197
|
+
| 16 | Path-style `assetPrefix` — every bundle 404s | No CloudFront behavior for `<prefix>/_next/static*`; Next applies `assetPrefix` on top of `basePath` | `relativeAssetPrefix`, `NextjsDistribution.addAssetPrefixBehavior` (CF function); regional types warn at synth | — |
|
|
198
|
+
| 17 | Pages Router home page `/` 404s (many unrelated-looking symptoms) | `normalizePagePath("/")` is `/index`; nothing registered `/` | `routablePathnames`, `src/adapter/build-outputs.ts` | — |
|
|
199
|
+
| 18 | Space in a `public/` filename fails synth: "Invalid CloudFront Distribution Cache Behavior Path Pattern" | Characters outside CloudFront's path-pattern alphabet threw | `toPathPattern` (`src/nextjs-distribution.ts`); `NextjsApi` warns and skips on Regional Functions. See 37 | `static-assets` (asserts 404 on Regional Functions) |
|
|
200
|
+
| 19 | A throw answers plain-text 500, never the app's error page | No error-page ladder; one `failWith` wrote a bare 500 | `resolveErrorTarget` (dispatch.ts) + `NextjsRuntime.sendError`, `no-store` | `status-codes` (App Router side) |
|
|
201
|
+
| 20 | Every i18n app answers `/` with a 308 to `/en-US` | `@next/routing` builds `/${locale}/`, which the trailing-slash 308 then matches | `Dispatcher.withRootLocale`, `normalizeRedirectLocation` | — |
|
|
202
|
+
| 21 | Static `getStaticProps` data route 404s, so client navigation into it full-reloads | Without middleware, next emits no rule for it; the concrete prerender was skipped | `addPrerenderPathnames` registers concrete `/_next/data/…json` when no ungated rule matches | — |
|
|
203
|
+
| 22 | Absolute `assetPrefix` with a path — every bundle 404s | The prefix's path was reduced to `""`, so no behavior was added | `assetPrefixPath`, `src/utils/base-path.ts` | — |
|
|
204
|
+
| 24 | `/_next/image` 400s for a `public/` file whose name needs percent-encoding | `fetchFromS3` used the still-encoded href as the S3 key | Tolerant percent-decode in `fetchFromS3`, `src/runtime/image-utils.ts` | `static-assets` (asserts `image/webp`) |
|
|
205
|
+
| 25 | A web worker never starts (assertion times out on placeholder text) | `patch-fetch.js` was prepended to `turbopack-worker-*.js` and read `window` | `patch-fetch.js` uses `globalThis`; worker-scope block in `patch-fetch.test.ts` | — |
|
|
206
|
+
| 26 | Large init cache only partly seeded (`MISS`, postponed shell); deploy reported success | `BucketDeployment` Lambda hit `ENOSPC` in 512 MiB `/tmp`; `--hotswap` ignores a custom resource's `FAILED` | `sizeDeploymentLambda`, `src/nextjs-cache.ts` | — |
|
|
207
|
+
| 27 | A 1 MB segment prefetch takes ~5s | Buffers serialized as JSON integer arrays and parsed with a reviver | Base64 in `serializeCacheValue`/`parseCacheValue` (old format still readable) | — |
|
|
208
|
+
| 29 | A `beforeFiles` rewrite that matches its own output is applied twice | `RouteModule.prepare` re-runs `handleRewrites` on the already-rewritten `req.url` | `src/runtime/core.ts` hands `requestMeta.query` the resolved query | `config-routing` |
|
|
209
|
+
| 30 | `//` → 500, `/api//json` → 404, instead of a 308 | `resolveRoutes` does not collapse repeated slashes; `new URL("//")` reads a host | Collapse + 308 in `src/runtime/core.ts`, ahead of any parsing | `url-normalization` |
|
|
210
|
+
| 31 | On Global Functions, `//` paths → `403 InvalidSignatureException` | OAC signs the raw path, the Function URL canonicalizes before verifying | Viewer-request function, `createDynamicCloudFrontFunctionAssociations` | `url-normalization` (`FunctionGeneratedResponse`) |
|
|
211
|
+
| 32 | Build-time `notFound: true` served as `200` with the 404 body | Seeded a `PAGES` entry holding `404.html`; the Pages HIT path ignores `value.status` | `writeInitCache` skips a `PAGES` prerender whose `initialStatus` ≠ 200; `init-cache.test.ts` | `status-codes` (App Router half) |
|
|
212
|
+
| 33 | `res.revalidate()` fails (`{revalidated:false}`), reason reports `stale` | No `routerServerContext.revalidate` supplied: "missing internal router-server-methods" | `requestMeta.revalidate` in `src/runtime/core.ts`; `core.test.ts` | — |
|
|
213
|
+
| 34 | Untagged `force-cache` fetch is never evicted by `revalidatePath`/`revalidateTag` | Fetch entries were checked against their stored tags, not the request's `ctx.tags` + `ctx.softTags` | `isFetchCacheGet` split in `S3CacheHandler.get` | — |
|
|
214
|
+
| 35 | Catch-all capture containing `%2F` → 500 ("Requested and resolved page mismatch") | The `nxtP` query contract decodes twice and splits on `/` | `outOfBandRouteParams` (dispatch.ts) passes `requestMeta.params`. Verified offline; the file is upstream | — |
|
|
215
|
+
| 36 | Regional Functions: every redirect drops the stage prefix, following it → 403 | API Gateway strips the stage before invoking the Lambda | `src/runtime/api-gateway-path.ts` uses `requestContext.path` when `basePath` starts with the stage | `url-normalization` (where it was found) |
|
|
216
|
+
|
|
217
|
+
Found by the four-type suite:
|
|
218
|
+
|
|
219
|
+
- **37** — defect 18's first fix wrote one `?` per _percent-encoded_ character.
|
|
220
|
+
CloudFront URL-decodes the path before matching and `?` matches one byte of the
|
|
221
|
+
decoded path, so `toPathPattern` writes one `?` per UTF-8 byte. A top-level
|
|
222
|
+
`public/` name with no character CloudFront can spell would become a pattern of
|
|
223
|
+
wildcards alone that captures unrelated app routes, so it gets **no behavior**
|
|
224
|
+
and a synth warning; its files reach the server, which reads them from S3.
|
|
225
|
+
Don't go back to per-encoded-character
|
|
226
|
+
patterns: they synthesize, deploy, and never match. The harness's
|
|
227
|
+
`next-image-legacy/unicode` could not catch it — the image optimizer reads S3
|
|
228
|
+
directly — `static-assets.test.ts` did.
|
|
229
|
+
- **38** — `NextjsRegionalContainers` served no `public/` file (the adapter API
|
|
230
|
+
lists `public/` in `staticFiles` only for `output: "export"`). The container
|
|
231
|
+
runtime now lists `public/` off disk at cold start the way `next start` does
|
|
232
|
+
(`src/runtime/public-files.ts`), ahead of app routes; the Lambda types have no
|
|
233
|
+
`public/` in the package and rely on the distribution routing it to S3.
|
|
234
|
+
|
|
235
|
+
## Upstream — a fixture that cannot pass as written
|
|
236
|
+
|
|
237
|
+
### `incremental-cache-path-traversal` asserts a 200 for a param shape that throws
|
|
238
|
+
|
|
239
|
+
1 of 1. Filed as [vercel/next.js#99154](https://github.com/vercel/next.js/issues/99154).
|
|
240
|
+
The file requests `/_next/data/<buildId>/pages-cache/..%2F..%2Fserver-reference-manifest.json`
|
|
241
|
+
and, in its `isNextDeploy && isAdapterTest` branch, asserts status 200 _together
|
|
242
|
+
with_ `pageProps.rest` equal to `['..', '..', 'server-reference-manifest']`. That
|
|
243
|
+
array is exactly the shape `normalizePagePath` rejects (`Requested and resolved
|
|
244
|
+
page mismatch`), and `cacheKey` is passed to `handleResponse` regardless of minimal
|
|
245
|
+
mode, so the two assertions cannot both hold on 16.3.5. The runtime 500 behind it
|
|
246
|
+
is fixed (defect 35: we now answer 200, byte-identical to `next start`). Its
|
|
247
|
+
`describe.skip` is conditional on `__NEXT_CACHE_COMPONENTS`, which the harness does
|
|
248
|
+
not set, so the screen does not remove it. **Recheck:** when #99154 lands, run it
|
|
249
|
+
offline first (`scripts/e2e-offline.sh incremental-cache-path-traversal`).
|
|
250
|
+
|
|
251
|
+
## Upstream — a bug in the adapter routing library
|
|
252
|
+
|
|
253
|
+
### A rewrite destination's repeated query key is collapsed to its last value
|
|
254
|
+
|
|
255
|
+
`rewrites-destination-query-array` (1 of 1). Filed as
|
|
256
|
+
[vercel/next.js#99155](https://github.com/vercel/next.js/issues/99155). A rewrite
|
|
257
|
+
to `/?items=1&items=2` reaches the page as `items: "2"`, so
|
|
258
|
+
`props.query.items.join(',')` throws and we answer 500 where `next start` renders
|
|
259
|
+
`1,2`. The loss is two `URLSearchParams#set` calls (should be `append`) in
|
|
260
|
+
`@next/routing@16.3.5`:
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
packages/next-routing/src/destination.ts:63 newUrl.searchParams.set(key, value)
|
|
264
|
+
packages/next-routing/src/resolve-routes.ts:312 mergedUrl.searchParams.set(key, value)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`next start` uses its in-tree `router-server` and is unaffected. By the time
|
|
268
|
+
`resolveRoutes` returns one value is gone; our `formatTarget` round-trips repeated
|
|
269
|
+
keys fine. No patch point — the library is a published dependency of `next`.
|
|
270
|
+
**Recheck:** `scripts/e2e-offline.sh rewrites-destination-query-array` (500 vs 200
|
|
271
|
+
off the same build today); requeue when the fix ships.
|
|
272
|
+
|
|
273
|
+
## Unsupported — a product limitation
|
|
274
|
+
|
|
275
|
+
### `dynamicParams = false`
|
|
276
|
+
|
|
277
|
+
`app-dir/prerender-encoding` (1 of 1). Not an encoding bug — dispatch resolves
|
|
278
|
+
`/sticks%20%26%20stones` to `nxtPid = "sticks & stones"` correctly. With
|
|
279
|
+
`export const dynamicParams = false`, Next.js gates the route's
|
|
280
|
+
`routing.dynamicRoutes` entries behind the preview cookies (`__prerender_bypass`,
|
|
281
|
+
`__next_preview_data`), expecting the platform to serve concrete prerendered paths
|
|
282
|
+
off the CDN and 404 everything else. cdk-nextjs registers only dynamic templates
|
|
283
|
+
(`addPrerenderTemplates`, `src/adapter/build-outputs.ts`), so every param 404s —
|
|
284
|
+
`/plain` too. Removing the flag from a copy of the fixture makes both resolve.
|
|
285
|
+
|
|
286
|
+
Supporting it means routing concrete prerendered paths to the owning entrypoint
|
|
287
|
+
while still supplying the template's `nxtP*` params, which done naively makes
|
|
288
|
+
`/isr/1` resolve to itself and lose `nxtPid` (see `addPrerenderTemplates`'s
|
|
289
|
+
comment). A design change; do it if a user asks. Defects 13 and 21 register
|
|
290
|
+
concrete pathnames in narrower cases (gated-only, and data routes with no ungated
|
|
291
|
+
rule). Side note: an unencoded `&` in a path parses as a query separator
|
|
292
|
+
(`/a&b` → `{nxtPid: "a", b: ""}`), matching Vercel.
|
|
293
|
+
|
|
294
|
+
## Unsupported — the edge runtime
|
|
295
|
+
|
|
296
|
+
cdk-nextjs refuses at build time to deploy any output whose runtime is not
|
|
297
|
+
`nodejs` (`assertNodeRuntimes`, `src/adapter/build-outputs.ts`): the edge runtime
|
|
298
|
+
is deprecated, and cdk-nextjs supports the Node-runtime `proxy.ts` instead of
|
|
299
|
+
legacy edge `middleware.ts`. The check throws during `next build`, so a fixture
|
|
300
|
+
with _any_ edge route or legacy middleware fails wholesale and `suites` cannot
|
|
301
|
+
rescue it. Both spellings count: `export const runtime = 'edge'` and
|
|
302
|
+
`export const config = { runtime: 'experimental-edge' }` — screening only the first
|
|
303
|
+
undercounts. 203 of 1134 files are disqualified this way.
|
|
304
|
+
|
|
305
|
+
| File | Why |
|
|
306
|
+
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
307
|
+
| `app-dir/app-static` | ~10 `*-edge` routes. Worth mining for individual cases to port: the densest ISR/caching coverage upstream. |
|
|
308
|
+
| `app-dir/actions/app-action` | Ships a legacy edge `middleware.js`. |
|
|
309
|
+
| `middleware-rewrites/test/index` | Ships a legacy edge `middleware.js`. |
|
|
310
|
+
| `middleware-fetches-with-any-http-method` | Writes its edge middleware inline via `nextTestSetup({ files })`, so `screen.mjs`'s on-disk grep missed it; failed in 7s on `assertNodeRuntimes`. |
|
|
311
|
+
|
|
312
|
+
The `middleware` screen's count is a floor: an inline-`files` fixture only shows up
|
|
313
|
+
as a ~7s build failure naming `assertNodeRuntimes`.
|
|
314
|
+
|
|
315
|
+
## Unsupported — a design choice
|
|
316
|
+
|
|
317
|
+
### A missing `/_next/static/*` is a 404 from S3, not from Next.js
|
|
318
|
+
|
|
319
|
+
The 6 `invalid-static-asset-404-*` files, 1 of 3 cases each (via `suites`). The
|
|
320
|
+
case wants a `404` with body exactly `Not Found` for a nonexistent static asset.
|
|
321
|
+
S3 behind Origin Access Control answers the `404` itself (the OAC principal has
|
|
322
|
+
`s3:ListBucket`, so S3 may say a key is absent) with its XML `NoSuchKey` body.
|
|
323
|
+
Matching the body would take a Lambda@Edge origin-response rewrite for one
|
|
324
|
+
assertion. Not done. Real-app cost: none beyond the body. The two passing cases
|
|
325
|
+
(valid asset → 200, invalid non-asset path → the app's 404 page) show Next.js's
|
|
326
|
+
own handling is intact.
|
|
327
|
+
|
|
328
|
+
### A `POST` body from a non-browser client needs a payload hash
|
|
329
|
+
|
|
330
|
+
`proxy-readable-toweb` (2 of 2, excluded outright). Both cases `POST` JSON from
|
|
331
|
+
the jest process and get `403 InvalidSignatureException`. CloudFront's OAC signs
|
|
332
|
+
the origin request to a Lambda Function URL but supplies only the empty-body hash,
|
|
333
|
+
so the viewer must send `x-amz-content-sha256` (defect 1's root cause from the
|
|
334
|
+
other side). Measured: with the header the request reaches the origin; without it, 403. cdk-nextjs covers the browser (`src/nextjs-build/patch-fetch.js` in the client
|
|
335
|
+
bundle); `next.fetch` is plain Node `fetch` in an unmodified next.js checkout. The
|
|
336
|
+
README's Limitations states it: on `NextjsGlobalFunctions` a non-browser caller
|
|
337
|
+
must hash a `POST`/`PUT` body. The Node-runtime proxy itself is covered by four
|
|
338
|
+
`proxy-*` files in `rules.include`.
|
|
339
|
+
|
|
340
|
+
### Revalidation re-requested about a second later
|
|
341
|
+
|
|
342
|
+
`app-dir/revalidate-dynamic` (2 of 2, excluded outright),
|
|
343
|
+
`app-dir/revalidate-path-with-rewrites` (`static page`) and `app-dir/trailingslash`
|
|
344
|
+
(2 of 8), the latter two via `suites`. Each loads a prerendered page, revalidates
|
|
345
|
+
via a route handler (which returns `revalidated: true`), refreshes, and expects new
|
|
346
|
+
content. The refresh is served from the edge — prerendered pages come back
|
|
347
|
+
`cache-control: s-maxage=31536000` and the second request reports
|
|
348
|
+
`x-cache: Hit from cloudfront`. cdk-nextjs does invalidate (defect 9, including
|
|
349
|
+
build-time prerenders), but `CreateInvalidation` is fire-and-forget with no bounded
|
|
350
|
+
completion time. The discriminator is how long the case waits: next.js's `retry()`
|
|
351
|
+
(`test/lib/next-test-utils.ts`) defaults to 3000ms; `segment-cache/prefetch-inlining`
|
|
352
|
+
passes 15000 and is green, these take the default or re-request immediately.
|
|
353
|
+
Vercel purges its CDN synchronously, which is why upstream does not gate these.
|
|
354
|
+
Making them pass would mean blocking every revalidation on an invalidation.
|
|
355
|
+
|
|
356
|
+
**Tell for a real regression instead:** the _same_ random value across every
|
|
357
|
+
attempt, on a case with no prerender to invalidate. That was defect 34 — the
|
|
358
|
+
`dynamic page` case of `revalidate-path-with-rewrites` had passed once by luck.
|
|
359
|
+
|
|
360
|
+
### A test's `env` reaches `next build`, not the deployed app
|
|
361
|
+
|
|
362
|
+
Per-test `env` from `nextTestSetup({ env })` is set for `next build`, so values
|
|
363
|
+
inlined at build time work, but not on the deployed function or task: a
|
|
364
|
+
request-time read of a non-inlined `process.env` value returns `undefined`.
|
|
365
|
+
next-deploy.ts hands the deploy script the test's `env` merged into the runner's
|
|
366
|
+
whole environment, so the script can't tell them apart, and forwarding it all
|
|
367
|
+
would also forward the runner's AWS credentials. No file in the manifest hits this.
|
|
368
|
+
|
|
369
|
+
## CDN-inherent — excluded, acceptable
|
|
370
|
+
|
|
371
|
+
### CloudFront rejects unencoded `[` and `]` in a path
|
|
372
|
+
|
|
373
|
+
`dynamic-route-interpolation`, 4 of 7 (the other 3 via `suites`). The four request
|
|
374
|
+
literal brackets (`/blog/[slug]`, `/api/dynamic/[abc]`) and CloudFront answers
|
|
375
|
+
before the Lambda:
|
|
376
|
+
|
|
377
|
+
```
|
|
378
|
+
$ curl -sg --path-as-is -D - -o /dev/null 'https://…/blog/[slug]'
|
|
379
|
+
HTTP/2 400
|
|
380
|
+
content-length: 0
|
|
381
|
+
x-cache: Error from cloudfront
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Percent-encoded (`/blog/%5Bslug%5D`) the same routes render correctly. `[`/`]`
|
|
385
|
+
are `gen-delims` and not legal in a path, so CloudFront is conformant.
|
|
386
|
+
|
|
387
|
+
## Architectural — excluded, acceptable
|
|
388
|
+
|
|
389
|
+
### Stale-while-revalidate after a tag revalidation
|
|
390
|
+
|
|
391
|
+
`app-dir/resume-data-cache`, 2 of 5 (the `it.each` "should have consistent data
|
|
392
|
+
between static and dynamic renders" cases; the other 3 via `suites`). Each
|
|
393
|
+
revalidates a tag and asserts the next dynamic RSC request still returns the
|
|
394
|
+
**stale** value once. cdk-nextjs is _too fresh_. Next.js tracks tag staleness in
|
|
395
|
+
`tagsManifest`, an in-process `Map`
|
|
396
|
+
(`packages/next/src/server/lib/incremental-cache/tags-manifest.external.ts`), and a
|
|
397
|
+
cache handler has no channel for "stale, serve it anyway": `lastModified: -1`
|
|
398
|
+
forces a blocking render. Fixing it needs Next.js to expose tag staleness to the
|
|
399
|
+
handler interface.
|
|
400
|
+
|
|
401
|
+
Don't "fix" it by returning `null` instead: on a PPR route with a `fallback` a hard
|
|
402
|
+
miss is answered from the fallback shell — uncacheable, never upgraded — which was
|
|
403
|
+
defect 23. Our handler returns `{ lastModified: -1, value }` for every tag-expired
|
|
404
|
+
response entry, and still deletes/returns `null` for a fetch entry.
|
|
405
|
+
|
|
406
|
+
The "fetch cache" case would also fail on its own: it prefetches the same URL with
|
|
407
|
+
the same `_rsc` hash as the "use cache" case, and CloudFront returns the earlier
|
|
408
|
+
payload — the problem next.js's `cdn-cache-busting` gates out itself.
|
|
409
|
+
|
|
410
|
+
## No signal — next.js skips these itself
|
|
411
|
+
|
|
412
|
+
| File | Why |
|
|
413
|
+
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
414
|
+
| `app-dir/segment-cache/refresh` | `describe.skip` upstream ("Disabled because too flaky"). "Passes" in ~5s without deploying. |
|
|
415
|
+
| `app-dir/segment-cache/cdn-cache-busting` | next.js skips it under `isNextDeploy` — deploy mode gating out what cannot work behind a CDN. |
|
|
416
|
+
| `app-dir/app-fetch-deduping` | Branches on `isNextStart`/`isNextDev` only; deploy mode gets `it('should skip other scenarios')`. 4s. |
|
|
417
|
+
| `cpu-profiling/cpu-profiling-dev` | `if (!isNextDev) { it('skip…'); return }` _after_ `nextTestSetup` — a full 187s deploy that asserts nothing. |
|
|
418
|
+
| `invalid-server-options` | Calls `next()` in-process and asserts on option-validation errors. Nothing built or deployed. 3.5s. |
|
|
419
|
+
| `next-dynamic-lazy-compilation` | Returns early under `shouldUseTurbopack()`, and the harness sets `IS_TURBOPACK_TEST=1`. Webpack-only. 5.1s. |
|
|
420
|
+
| `skipDeployment: true` files (manifest key `skipDeployment, 236 files`) | See "The `skipDeployment` traps" below. |
|
|
421
|
+
|
|
422
|
+
## No signal — the fixture cannot be built here
|
|
423
|
+
|
|
424
|
+
### `next-config-ts-native-ts/**` needs a `next build` flag the harness does not pass
|
|
425
|
+
|
|
426
|
+
All 18 files fail in `next build` with `ERR_REQUIRE_ASYNC_MODULE` or
|
|
427
|
+
`ReferenceError: await is not defined`. Every fixture uses top-level `await` in
|
|
428
|
+
`next.config.ts`, which only Node's native TS loader handles; that loader is gated
|
|
429
|
+
on `__NEXT_NODE_NATIVE_TS_LOADER_ENABLED`, set only by
|
|
430
|
+
`next build --experimental-next-config-strip-types`. A plain `next build` of a copy
|
|
431
|
+
with no adapter fails identically; with the flag it succeeds.
|
|
432
|
+
|
|
433
|
+
Adding the flag to `scripts/e2e-deploy.sh` would apply to the whole run and move
|
|
434
|
+
the 21 green `next-config-ts/*` files off the swc-transpile path they exist to
|
|
435
|
+
cover; the harness cannot set per-fixture build flags. The one question these files
|
|
436
|
+
ask cdk-nextjs — does `modifyConfig` still apply through the native loader? — is
|
|
437
|
+
asserted on every PR by `scripts/zero-config-build.mjs`. `next-config-ts-native-mts/**`
|
|
438
|
+
needs no flag (`.mts` is ESM either way) and is green.
|
|
439
|
+
|
|
440
|
+
## Debugging a harness failure
|
|
441
|
+
|
|
442
|
+
### Reading a harness log
|
|
443
|
+
|
|
444
|
+
- **Wholesale failure at ~120s or ~240s** with
|
|
445
|
+
`Exceeded timeout of … ms for a hook` in `beforeAll`: the deploy outran jest's
|
|
446
|
+
hook timeout (`NEXT_E2E_TEST_TIMEOUT`); nothing was asserted. Read the retry, not
|
|
447
|
+
attempt 0, and read durations, not case names — a file can report 8 of 8
|
|
448
|
+
"routing" cases failing when none ran. A deploy that suddenly takes a full
|
|
449
|
+
CloudFormation update (~110s+) instead of a hotswap (~50s) is the usual cause —
|
|
450
|
+
e.g. churning bucket `Tags` once did this; see the README. On the Global
|
|
451
|
+
types, `rejected changes: DistributionConfig` in the log means a CloudFront
|
|
452
|
+
update, which takes 8-10 minutes; if the next file logs `an earlier cdk deploy
|
|
453
|
+
… is still running; waiting`, it is waiting on that update, not failing on its
|
|
454
|
+
own. A wait that ends in `outlived 600s; stopping it` means the earlier deploy
|
|
455
|
+
hung: that file failed on it, and this one starts from a clean slate.
|
|
456
|
+
- **A pass in under ~10s deployed nothing.** A real file spends 100s+ on
|
|
457
|
+
`next build` plus a hotswap. `run-tests.js --timings` prints per-file duration —
|
|
458
|
+
the only signal for a passing file, since output prints on failure only.
|
|
459
|
+
- **But a slow pass can still be empty:** check for an early `return` after
|
|
460
|
+
`nextTestSetup` (`cpu-profiling-dev`) alongside the timing, not instead of it.
|
|
461
|
+
- **Playwright traces** land in `test/traces/` (`run-tests.js` sets
|
|
462
|
+
`TRACE_PLAYWRIGHT=true`). The `0-trace.network` entry shows every request's real
|
|
463
|
+
status and headers — this is how defect 1's 403s were found while the Lambda's own
|
|
464
|
+
logs were clean, and how defect 23's fallback shell was spotted.
|
|
465
|
+
- **Server logs:** `scripts/e2e-logs.sh` output _is_ `next.cliOutput`; anything
|
|
466
|
+
extra it prints can fail log-asserting fixtures (`deprecation-warnings`).
|
|
467
|
+
|
|
468
|
+
### The `skipDeployment` traps
|
|
469
|
+
|
|
470
|
+
- `nextTestSetup({ skipDeployment: true })` replaces the file with
|
|
471
|
+
`it.only('should skip next deploy')` (`test/lib/e2e-utils/index.ts`): a ~4s pass,
|
|
472
|
+
nothing deployed. Two files once sat in `rules.include` on such a pass.
|
|
473
|
+
`screen.mjs` disqualifies it.
|
|
474
|
+
- **It is not always in the test file.** It can be in a sibling `shared.util.ts`, or
|
|
475
|
+
the test is a two-line `require('./client.test')` wrapper
|
|
476
|
+
(`app-dir/cache-components-errors/*`). `screen.mjs` follows relative
|
|
477
|
+
imports two levels deep for this screen only; mode-gate screens stay
|
|
478
|
+
test-file-only, since a helper's `isNextDev` branch may cover only some cases.
|
|
479
|
+
- **`skipDeployment: !isAdapterTest` is different.** Ten files gate on
|
|
480
|
+
`NEXT_ENABLE_ADAPTER === '1'`; unset, they take the skip path (how
|
|
481
|
+
`partial-fallback-shell-upgrade` "passed" in 5.4s once). The harness exports
|
|
482
|
+
`NEXT_ENABLE_ADAPTER=1`, and `screen.mjs` deliberately does not disqualify these.
|
|
483
|
+
- **Mode gates after `nextTestSetup`** cost a full deploy and prove nothing, and
|
|
484
|
+
the `mode-gated` screen cannot see them.
|
|
485
|
+
|
|
486
|
+
### `NEXT_PRIVATE_TEST_MODE` is what makes a PPR response splittable
|
|
487
|
+
|
|
488
|
+
PPR tests split the body on `<!-- PPR_BOUNDARY_SENTINEL -->`
|
|
489
|
+
(`splitResponseWithPPRSentinel`, `test/lib/e2e-utils/ppr.ts`). The sentinel is
|
|
490
|
+
emitted only under `process.env.__NEXT_TEST_MODE`, a build-time constant inlined by
|
|
491
|
+
`define-env.ts`; the harness's `next.config` snippet aliases
|
|
492
|
+
`NEXT_PRIVATE_TEST_MODE` → `__NEXT_TEST_MODE`, and `scripts/e2e-deploy.sh` exports
|
|
493
|
+
it. Without it everything lands in `static$` and every "dynamic part not in the
|
|
494
|
+
static part" assertion fails — a missing env var, not a defect. It once masked
|
|
495
|
+
defect 14: after the var was set, `sub-shell-generation` still failed 6 of 7 but on
|
|
496
|
+
a _different_ assertion. **If the shape of a failure changes after an environment
|
|
497
|
+
fix, there is a second problem.**
|
|
498
|
+
|
|
499
|
+
### Reproduce offline against `next start` on the same build
|
|
500
|
+
|
|
501
|
+
`scripts/e2e-offline.sh <fixture>` builds the fixture through the adapter and
|
|
502
|
+
serves it twice — our container shell and `next start` — so a disagreement is one
|
|
503
|
+
`diff` of two responses (see the README, "Debugging one failing file without
|
|
504
|
+
deploying"). Defects 11, 13, 14, 15, 20, 21, 22, 33 and 35 and both upstream
|
|
505
|
+
verdicts were settled this way. Limits:
|
|
506
|
+
|
|
507
|
+
- The runtime cache reads S3 only, so a fully-prerendered route answers
|
|
508
|
+
`invariant: cache entry required but not generated` offline. Read the seed
|
|
509
|
+
directory (`.next/cdk-nextjs-init-cache`) instead — it is how defect 14's missing
|
|
510
|
+
shells were found (the seeding loop had no jest coverage then).
|
|
511
|
+
- **Offline agreement only rules out the request path, not the deploy.** Seeding
|
|
512
|
+
happens in the `BucketDeployment`, so an offline app has no seeded cache. Defect
|
|
513
|
+
32 agreed offline (both 404) and was a seeding bug.
|
|
514
|
+
- For a question below the HTTP layer, call the library directly: defect 11 ran
|
|
515
|
+
`resolveRoutes` from a shell; defect 35 fed candidate param shapes to next's built
|
|
516
|
+
`dist` to find which one `_getPathname` accepts.
|
|
517
|
+
- An oracle `next start` also settles "is this ours?" for deployed-type runs: the
|
|
518
|
+
Regional Functions back-fill ran `next start` on the same build with the same
|
|
519
|
+
`basePath` (`NEXTJS_BASE_PATH=/prod`) and found two failures that were the test's
|
|
520
|
+
own bug, one wrong expectation, and one real defect (36).
|
|
521
|
+
|
|
522
|
+
### Who answered? Reading response headers
|
|
523
|
+
|
|
524
|
+
| You see | It means |
|
|
525
|
+
| --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
526
|
+
| `x-cache: Hit from cloudfront` | Edge cache served it; the origin never saw the request (stale content after a revalidate: invalidation timing). |
|
|
527
|
+
| `x-cache: Error from cloudfront`, `400`, `content-length: 0` | CloudFront rejected the request itself (e.g. literal `[`/`]`). |
|
|
528
|
+
| A `400` with no `x-amzn-*` headers | CloudFront answered — says nothing about _why_, and not that a function cannot run (defect 31's bare `//`). |
|
|
529
|
+
| `x-cache: FunctionGeneratedResponse from cloudfront` | A viewer-request CloudFront Function answered (defect 31's 308). Assert as a prefix. |
|
|
530
|
+
| `403`, `x-amzn-errortype: InvalidSignatureException` | Lambda Function URL rejected OAC's signature: an unhashed body (1, `proxy-readable-toweb`) or a path the URL canonicalizes (31). |
|
|
531
|
+
| `403` for a static asset | S3 via OAC for a missing key (no `ListBucket`), or API Gateway for a path outside the stage (36). |
|
|
532
|
+
| `accept-ranges: bytes`, `last-modified: … 1980`, weak `etag` | `send` served a file off disk — a `static-file` dispatch, typed by file extension (defect 12). |
|
|
533
|
+
| `x-nextjs-cache: HIT`/`MISS`/`STALE` | The Next.js cache path. `HIT` with a wrong body/status → look at what was seeded (4, 32); `MISS` where a prerender should exist → seeding (26). |
|
|
534
|
+
| `cache-control: private, no-store` + `x-nextjs-postponed: 1`, no `x-nextjs-cache` | Answered from a PPR fallback shell, not a cache entry (defect 23). |
|
|
535
|
+
|
|
536
|
+
Useful probes: `curl -sI '…/?_rsc=probe' -H 'RSC: 1' -H 'Next-Router-Prefetch: 1'
|
|
537
|
+
-H 'Next-Router-Segment-Prefetch: /__PAGE__'` for a segment prefetch; call it twice
|
|
538
|
+
to see edge caching. `curl -g --path-as-is` keeps literal brackets and `//` on the
|
|
539
|
+
wire (a browser or Playwright `APIRequestContext` cannot send a `\` at all — WHATWG
|
|
540
|
+
parsing turns it into `/`). In Playwright, read duplicate headers with
|
|
541
|
+
`headersArray()`; `headers()` folds two `vary`s into one string.
|
|
542
|
+
|
|
543
|
+
### Symptom patterns worth recognizing
|
|
544
|
+
|
|
545
|
+
- **Unrelated-looking failures sharing one URL.** Defect 17's eleven files failed on
|
|
546
|
+
empty text, `JSON.parse('')`, a `naturalWidth` of `null` — every failing case
|
|
547
|
+
requested `/`, every passing one did not. Group failures by URL before by assertion.
|
|
548
|
+
- **A 404 page still hydrates.** A built-in 404 is a Next.js document; the browser
|
|
549
|
+
logs a single "Failed to load resource: 404" and the test times out on a selector
|
|
550
|
+
(defects 13, 17). Probe the page URL directly before debugging hydration.
|
|
551
|
+
- **jest misattributes un-awaited rejections.** A fixture's `bundles.forEach(async …)`
|
|
552
|
+
reports its failure against whichever test is running (defect 16).
|
|
553
|
+
- **A route returning 200 can still have failed.** A fixture that catches and
|
|
554
|
+
returns `{revalidated: false}` passes its status assertion; read the body and the
|
|
555
|
+
server log (defect 33).
|
|
556
|
+
- **Worker errors do not reach the page console.** They fire on the worker's
|
|
557
|
+
`error` event, which fixtures rarely listen for (defect 25).
|
|
558
|
+
- **Wrong `content-type` on RSC breaks things far away:** a `NaN` navigation
|
|
559
|
+
duration, or an action `redirect()` with no head (next fetches the target and
|
|
560
|
+
cancels it unless it is `text/x-component`) (defect 2).
|
|
561
|
+
- **`JSON.stringify` hides `undefined`-valued keys**, so logging a query object can
|
|
562
|
+
lie; inspect with a debugger or `Object.keys` (defect 11).
|
|
563
|
+
- **Races need more than one measurement.** Re-run a suspected defect several
|
|
564
|
+
times before filing it: a server-action redirect that looked like a third
|
|
565
|
+
instance of defect 36 was a pre-hydration click (native form POST), fixed by
|
|
566
|
+
waiting for the client router.
|
|
567
|
+
- **Instrument before guessing.** Defect 3 survived three plausible theories; one
|
|
568
|
+
run with `[probe]` logs in the deployed function settled it.
|
|
569
|
+
- **For slowness, split the time.** Compare Lambda duration with end-to-end, repeat
|
|
570
|
+
with `Accept-Encoding: identity` to rule out gzip/CDN, and look at the stored
|
|
571
|
+
S3 object's size and shape (defect 27: an 11.6 MiB entry for a ~1 MiB page).
|
|
572
|
+
|
|
573
|
+
### Deploy-side traps
|
|
574
|
+
|
|
575
|
+
- **`cdk deploy --hotswap` does not check custom resources.** It invokes them with
|
|
576
|
+
placeholder response URLs and prints success even when they send `FAILED`. If a
|
|
577
|
+
cache looks partly seeded, count objects in the cache bucket against the seed
|
|
578
|
+
directory and read the `BucketDeployment` Lambda's log (defect 26).
|
|
579
|
+
- **Hotswap never runs CloudFormation**, so the post-deploy custom resource does
|
|
580
|
+
not fire between fixtures — anything it does (e.g. pruning the previous build) is not
|
|
581
|
+
under harness coverage.
|
|
582
|
+
- **`NEXT_DEPLOYMENT_ID` makes every build id the same constant**; if one fixture
|
|
583
|
+
serves another's content, check for one cache prefix shared across builds
|
|
584
|
+
(defect 10).
|
|
585
|
+
- **npm-pinned fixtures** (`packageManager: npm@…`): npm with `--prefer-offline`
|
|
586
|
+
treats a stale cached packument as final (`ETARGET … No matching version found for
|
|
587
|
+
next@16.3.5`), so `e2e-deploy.sh` drops the flag for npm; and pnpm refuses to run
|
|
588
|
+
the hardcoded `pnpm post-build` in a project pinned to npm, so `e2e-deploy.sh`
|
|
589
|
+
exports `npm_config_package_manager_strict=false`.
|
|
590
|
+
|
|
591
|
+
### Keeping verdicts durable
|
|
592
|
+
|
|
593
|
+
`screen.mjs`'s **verdict** screen keeps a file out of `candidates` only if its
|
|
594
|
+
`excluded-notes` key is the file path, or a directory ending in `/**`. A verdict written
|
|
595
|
+
under a prose key ("revalidation behind the CDN, two files") is not matched, and
|
|
596
|
+
the file gets re-offered and re-deployed. Key new verdicts by path.
|
|
597
|
+
|
|
598
|
+
[harness]: https://nextjs.org/docs/app/api-reference/adapters/testing-adapters
|