Compare commits

...

30 Commits

Author SHA1 Message Date
Vercel Release Bot
809740b385 Version Packages (#11570)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @vercel/functions@1.0.2

### Patch Changes

- Convert package to CommonJS
([#11569](https://github.com/vercel/vercel/pull/11569))

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-09 18:12:02 -07:00
Nathan Rajlich
bf9acd023d [functions] Convert to CommonJS (#11569)
The package was not working because it is using ESM syntax, but the
`package.json` did not have `"type": "module"`.

Anyways, for broader compatibility, let's use CommonJS.
2024-05-09 18:10:39 -07:00
Vercel Release Bot
f4ba9cdcf8 Version Packages (#11564)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## vercel@34.1.10

### Patch Changes

- Updated dependencies
\[[`119f80e96`](119f80e961),
[`11584b0e9`](11584b0e9b),
[`3023122d4`](3023122d4e),
[`0e774b6be`](0e774b6be0)]:
    -   @vercel/next@4.2.9
    -   @vercel/static-build@2.5.4

## @vercel/next@4.2.9

### Patch Changes

- Support incremental partial prerendering
([#11560](https://github.com/vercel/vercel/pull/11560))

- ensure `.action` outputs are created for edge functions
([#11568](https://github.com/vercel/vercel/pull/11568))

-   ([#11566](https://github.com/vercel/vercel/pull/11566))

## @vercel/static-build@2.5.4

### Patch Changes

- Fix `Cannot read properties of null` error
([#11559](https://github.com/vercel/vercel/pull/11559))

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-09 10:03:52 -07:00
Zack Tanner
11584b0e9b [next] ensure action outputs are created for edge functions (#11568)
Since we're rewriting `next-action` requests to `.action` outputs, we need to ensure that we also duplicate the edge function handler for it. 

This runs all of the existing Node runtime tests in the Edge runtime as well. However, there's a discrepancy in how `x-vercel-cache` is returned, so for now we instead validate that its responding with `x-edge-runtime`.
2024-05-09 01:39:08 +00:00
JJ Kasper
3023122d4e [next] Update react version in next fixtures (#11566)
We are locking the peerDependency to react v19 beta on canary so we need to update these fixtures to tolerate that. 

x-ref: [slack thread](https://vercel.slack.com/archives/C048KLPEEH1/p1715191372631389)
x-ref: https://github.com/vercel/next.js/pull/65058
2024-05-08 23:33:24 +00:00
Nathan Rajlich
0e774b6be0 [static-build] Fix Cannot read properties of null error (#11559)
A bug was introduced in #11529 that causes the build to fail if `ruby`
is not installed. The fix here accounts for that scenario.

Closes #11547.
2024-05-08 11:44:49 -07:00
Wyatt Johnson
119f80e961 [next] Support Incremental Partial Prerendering (#11560)
This fixes the logic that detects if partial prerendering (PPR) has been
enabled for a project. The new incremental adoption pathway allows
setting the `experimental.ppr = 'incremental'` which the previous code
did not support.
2024-05-08 08:13:39 -06:00
Vercel Release Bot
9fe92d7de0 Version Packages (#11558)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @vercel/functions@1.0.1

### Patch Changes

- Don't throw error if context is missing
([`0817527f9`](0817527f9e))

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-07 17:13:10 +02:00
Kiko Beats
0817527f9e [@vercel/functions]: don't throw error if context is missing (#11557)
making `waituntil` a noop
2024-05-07 17:02:23 +02:00
Vercel Release Bot
6476f4f786 Version Packages (#11551)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @vercel/functions@1.0.0

### Major Changes

- Initial release
([#11553](https://github.com/vercel/vercel/pull/11553))

## @vercel/node@3.1.0

### Minor Changes

- Make waitUntil consistent for Node.js & Edge
([#11553](https://github.com/vercel/vercel/pull/11553))

## vercel@34.1.9

### Patch Changes

- Updated dependencies
\[[`5a532a5b9`](5a532a5b94),
[`50fc27ba5`](50fc27ba57),
[`c1d852295`](c1d8522950),
[`a5ea04154`](a5ea04154b)]:
    -   @vercel/next@4.2.8
    -   @vercel/node@3.1.0

## @vercel/next@4.2.8

### Patch Changes

- Fix missing initial RSC headers
([#11552](https://github.com/vercel/vercel/pull/11552))

- Remove .prefetch.rsc rewrites for non-PPR
([#11540](https://github.com/vercel/vercel/pull/11540))

- [next] rename middleware manifest env
([#11549](https://github.com/vercel/vercel/pull/11549))

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-07 15:40:37 +02:00
Kiko Beats
a5ea04154b Make waitUntil consistent for Node.js & Edge (#11553)
This PR makes `waitUntil` consistent when interacting with it via `vc
dev`. That includes:

**be possible to call `waitUntil` from the context second argument in
web handlers functions (Node.js & Edge)**:

```js
export function GET(request, { waitUntil }) {
  waitUntil(fetch('https://vercel.com'));
  return new Response('OK');
}
```

**be possible to call `waitUntil` imported from `@vercel/functions`**
(Node.js & Edge):

```js
import { waitUntil } from '@vercel/functions';

export function GET(request) {
  waitUntil(fetch('https://vercel.com'));
  return new Response('OK');
}
```

Additionally, `@vercel/functions` can be adopted for other framework to
take advantage of this pattern at Vercel.

---------

Co-authored-by: Javi Velasco <javier.velasco86@gmail.com>
2024-05-07 15:36:43 +02:00
JJ Kasper
5a532a5b94 [next] Fix missing initial RSC headers (#11552)
While investigating https://github.com/vercel/next.js/issues/59883 we noticed the RSC Prerender did not have all the expected headers like the HTML Prerender which caused unexpected revalidation behavior specifically on client-transition as some of these headers are used to detect if a revalidated tag is associated with specific outputs. 

x-ref: https://github.com/vercel/next.js/issues/59883
2024-05-07 07:43:00 +00:00
Jiachi Liu
c1d8522950 [next] rename middleware manifest env (#11549)
Rename `environment` property to `env` from middleware manifest added in
#11390.
Align with https://github.com/vercel/next.js/pull/64521

The `environments` property wasn't working properly before due to
unfinished next.js changes, it should work now after testing with next
build from https://github.com/vercel/next.js/pull/64521. We change it to
a better naming word `env`.
2024-05-06 21:32:07 +02:00
JJ Kasper
50fc27ba57 [next] Remove .prefetch.rsc rewrites for non-PPR (#11540)
Removes .prefetch.rsc rewrites rules for non-PPR, as Next.js doesn't emit any static prefetches otherwise
2024-05-06 18:17:22 +00:00
Joel Henningsen
3101d2497f Fix broken link to Code of Conduct in README (#11544)
### Purpose of the Pull Request

This PR addresses a broken link in the README.md file that is supposed
to direct readers to the Code of Conduct. Previously, the link pointed
to `CODE_OF_CONDUCT.md`, which does not exist in the repository's main
directory. The corrected link points to the actual location of the file
in the `.github` directory.

### Changes Made

- Updated the link to the Code of Conduct from `[Code of
Conduct](CODE_OF_CONDUCT.md)` to `[Code of
Conduct](./.github/CODE_OF_CONDUCT.md)` to reflect the correct path.

### Additional Information

I previously opened discussion #11498 regarding this issue with the goal
of following the [Contribuing
Guidelines](https://github.com/vercel/vercel?tab=readme-ov-file#contributing),
but did not receive any feedback over the past week. Given the nature of
the change (a simple documentation fix), I proceeded with this pull
request to ensure the documentation remains useful and accurate for all
users.

### Impact

This change will help ensure that new contributors and users can easily
access the Code of Conduct, fostering better community interactions and
adherence to project standards.

Thank you for considering this pull request. I am open to any further
suggestions or requirements needed to merge this change.
2024-05-04 17:28:21 -07:00
Vercel Release Bot
4b0716b556 Version Packages (#11524)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## vercel@34.1.8

### Patch Changes

- Updated dependencies
\[[`52e435aa5`](52e435aa5d),
[`124846a3e`](124846a3e6),
[`dc974b679`](dc974b6797),
[`58c6755e0`](58c6755e0c)]:
    -   @vercel/next@4.2.7
    -   @vercel/static-build@2.5.3

## @vercel/next@4.2.7

### Patch Changes

- Fix missing .rsc outputs for pages prerenders
([#11503](https://github.com/vercel/vercel/pull/11503))

- Fix base path app index RSC handling
([#11528](https://github.com/vercel/vercel/pull/11528))

- Fix interception routes PPR route handling
([#11527](https://github.com/vercel/vercel/pull/11527))

## @vercel/static-build@2.5.3

### Patch Changes

- Support Ruby and Python static site generators in AL2023 build image
([#11529](https://github.com/vercel/vercel/pull/11529))

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-03 10:01:03 -07:00
JJ Kasper
124846a3e6 [next] Fix base path app index RSC handling (#11528)
This ensures we properly name the outputs when a basePath is present for the `index` output as previously we weren't properly routing to the RSC output due to nested `index` naming/route conflict. This only was a noticeable issue with prerender/static index outputs with base path and app router since if the index route was dynamic it would still catch the RSC request and just handle it without the correct naming. 

Fixes: https://github.com/vercel/next.js/issues/64903
2024-05-02 21:07:58 +00:00
dependabot[bot]
30cd0ec898 [framework-fixtures]: Bump the core group in /packages/static-build/test/fixtures/ionic-angular-v8 with 2 updates (#11530)
Bumps the core group in /packages/static-build/test/fixtures/ionic-angular-v8 with 2 updates: [@ionic/angular](https://github.com/ionic-team/ionic-framework) and [@ionic/core](https://github.com/ionic-team/ionic-framework).

Updates `@ionic/angular` from 8.0.1 to 8.1.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/ionic-team/ionic-framework/releases"><code>@​ionic/angular</code>'s releases</a>.</em></p>
<blockquote>
<h2>v8.1.0</h2>
<h1><a href="https://github.com/ionic-team/ionic-framework/compare/v8.0.2...v8.1.0">8.1.0</a> (2024-05-01)</h1>
<h3>Features</h3>
<ul>
<li>add experimental transition focus manager (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29400">#29400</a>) (<a href="5b686efc10">5b686ef</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/23650">#23650</a></li>
<li><strong>content:</strong> add fixedSlotPlacement prop (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29233">#29233</a>) (<a href="90a7e70a1c">90a7e70</a>)</li>
<li><strong>datetime:</strong> pass roles to overlay when dismissing (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29221">#29221</a>) (<a href="6945adc3cc">6945adc</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/28298">#28298</a></li>
<li><strong>input:</strong> add clearInputIcon property (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29246">#29246</a>) (<a href="0f5d1c02d2">0f5d1c0</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/26974">#26974</a></li>
<li><strong>modal, popover:</strong> add ability to temporarily disable focus trapping (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29379">#29379</a>) (<a href="7c00351680">7c00351</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/24646">#24646</a></li>
<li><strong>picker:</strong> picker column is easier to select with assistive technology (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29371">#29371</a>) (<a href="e38e2e4d35">e38e2e4</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/25221">#25221</a></li>
</ul>
<h2>v8.0.2</h2>
<h2><a href="https://github.com/ionic-team/ionic-framework/compare/v8.0.1...v8.0.2">8.0.2</a> (2024-05-01)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>datetime:</strong> navigating months in RTL works correctly (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29421">#29421</a>) (<a href="4d09890d69">4d09890</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29198">#29198</a></li>
<li><strong>ios:</strong> large title transition accounts for back button with no text  (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29327">#29327</a>) (<a href="bd8d065e1a">bd8d065</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/28751">#28751</a></li>
<li><strong>md:</strong> item border has improved contrast in dark mode (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29398">#29398</a>) (<a href="fa85f030cf">fa85f03</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29386">#29386</a></li>
<li><strong>select:</strong> options are visible with fit-content width and fill outline (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29408">#29408</a>) (<a href="f15b62a9ca">f15b62a</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29321">#29321</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/ionic-team/ionic-framework/blob/main/CHANGELOG.md"><code>@​ionic/angular</code>'s changelog</a>.</em></p>
<blockquote>
<h1><a href="https://github.com/ionic-team/ionic-framework/compare/v8.0.2...v8.1.0">8.1.0</a> (2024-05-01)</h1>
<h3>Features</h3>
<ul>
<li>add experimental transition focus manager (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29400">#29400</a>) (<a href="5b686efc10">5b686ef</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/23650">#23650</a></li>
<li><strong>content:</strong> add fixedSlotPlacement prop (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29233">#29233</a>) (<a href="90a7e70a1c">90a7e70</a>)</li>
<li><strong>datetime:</strong> pass roles to overlay when dismissing (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29221">#29221</a>) (<a href="6945adc3cc">6945adc</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/28298">#28298</a></li>
<li><strong>input:</strong> add clearInputIcon property (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29246">#29246</a>) (<a href="0f5d1c02d2">0f5d1c0</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/26974">#26974</a></li>
<li><strong>modal, popover:</strong> add ability to temporarily disable focus trapping (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29379">#29379</a>) (<a href="7c00351680">7c00351</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/24646">#24646</a></li>
<li><strong>picker:</strong> picker column is easier to select with assistive technology (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29371">#29371</a>) (<a href="e38e2e4d35">e38e2e4</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/25221">#25221</a></li>
</ul>
<h2><a href="https://github.com/ionic-team/ionic-framework/compare/v8.0.1...v8.0.2">8.0.2</a> (2024-05-01)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>datetime:</strong> navigating months in RTL works correctly (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29421">#29421</a>) (<a href="4d09890d69">4d09890</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29198">#29198</a></li>
<li><strong>ios:</strong> large title transition accounts for back button with no text  (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29327">#29327</a>) (<a href="bd8d065e1a">bd8d065</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/28751">#28751</a></li>
<li><strong>md:</strong> item border has improved contrast in dark mode (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29398">#29398</a>) (<a href="fa85f030cf">fa85f03</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29386">#29386</a></li>
<li><strong>select:</strong> options are visible with fit-content width and fill outline (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29408">#29408</a>) (<a href="f15b62a9ca">f15b62a</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29321">#29321</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="78d3076703"><code>78d3076</code></a> v8.1.0</li>
<li><a href="4ef5036c7b"><code>4ef5036</code></a> chore: sync with main (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29433">#29433</a>)</li>
<li><a href="72711bba2e"><code>72711bb</code></a> chore: sync with main</li>
<li><a href="d81c88403e"><code>d81c884</code></a> merge release-8.0.2 (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29431">#29431</a>)</li>
<li><a href="5b686efc10"><code>5b686ef</code></a> feat: add experimental transition focus manager (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29400">#29400</a>)</li>
<li><a href="665d67778f"><code>665d677</code></a> chore(): update package lock files</li>
<li><a href="101b50e14c"><code>101b50e</code></a> v8.0.2</li>
<li><a href="a01c3d49bb"><code>a01c3d4</code></a> docs(testing, components): clarify testing and focusable usage (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29424">#29424</a>)</li>
<li><a href="bd8d065e1a"><code>bd8d065</code></a> fix(ios): large title transition accounts for back button with no text  (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29327">#29327</a>)</li>
<li><a href="4d09890d69"><code>4d09890</code></a> fix(datetime): navigating months in RTL works correctly (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29421">#29421</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/ionic-team/ionic-framework/compare/v8.0.1...v8.1.0">compare view</a></li>
</ul>
</details>
<br />

Updates `@ionic/core` from 8.0.1 to 8.1.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/ionic-team/ionic-framework/releases"><code>@​ionic/core</code>'s releases</a>.</em></p>
<blockquote>
<h2>v8.1.0</h2>
<h1><a href="https://github.com/ionic-team/ionic-framework/compare/v8.0.2...v8.1.0">8.1.0</a> (2024-05-01)</h1>
<h3>Features</h3>
<ul>
<li>add experimental transition focus manager (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29400">#29400</a>) (<a href="5b686efc10">5b686ef</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/23650">#23650</a></li>
<li><strong>content:</strong> add fixedSlotPlacement prop (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29233">#29233</a>) (<a href="90a7e70a1c">90a7e70</a>)</li>
<li><strong>datetime:</strong> pass roles to overlay when dismissing (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29221">#29221</a>) (<a href="6945adc3cc">6945adc</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/28298">#28298</a></li>
<li><strong>input:</strong> add clearInputIcon property (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29246">#29246</a>) (<a href="0f5d1c02d2">0f5d1c0</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/26974">#26974</a></li>
<li><strong>modal, popover:</strong> add ability to temporarily disable focus trapping (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29379">#29379</a>) (<a href="7c00351680">7c00351</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/24646">#24646</a></li>
<li><strong>picker:</strong> picker column is easier to select with assistive technology (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29371">#29371</a>) (<a href="e38e2e4d35">e38e2e4</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/25221">#25221</a></li>
</ul>
<h2>v8.0.2</h2>
<h2><a href="https://github.com/ionic-team/ionic-framework/compare/v8.0.1...v8.0.2">8.0.2</a> (2024-05-01)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>datetime:</strong> navigating months in RTL works correctly (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29421">#29421</a>) (<a href="4d09890d69">4d09890</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29198">#29198</a></li>
<li><strong>ios:</strong> large title transition accounts for back button with no text  (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29327">#29327</a>) (<a href="bd8d065e1a">bd8d065</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/28751">#28751</a></li>
<li><strong>md:</strong> item border has improved contrast in dark mode (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29398">#29398</a>) (<a href="fa85f030cf">fa85f03</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29386">#29386</a></li>
<li><strong>select:</strong> options are visible with fit-content width and fill outline (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29408">#29408</a>) (<a href="f15b62a9ca">f15b62a</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29321">#29321</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/ionic-team/ionic-framework/blob/main/CHANGELOG.md"><code>@​ionic/core</code>'s changelog</a>.</em></p>
<blockquote>
<h1><a href="https://github.com/ionic-team/ionic-framework/compare/v8.0.2...v8.1.0">8.1.0</a> (2024-05-01)</h1>
<h3>Features</h3>
<ul>
<li>add experimental transition focus manager (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29400">#29400</a>) (<a href="5b686efc10">5b686ef</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/23650">#23650</a></li>
<li><strong>content:</strong> add fixedSlotPlacement prop (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29233">#29233</a>) (<a href="90a7e70a1c">90a7e70</a>)</li>
<li><strong>datetime:</strong> pass roles to overlay when dismissing (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29221">#29221</a>) (<a href="6945adc3cc">6945adc</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/28298">#28298</a></li>
<li><strong>input:</strong> add clearInputIcon property (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29246">#29246</a>) (<a href="0f5d1c02d2">0f5d1c0</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/26974">#26974</a></li>
<li><strong>modal, popover:</strong> add ability to temporarily disable focus trapping (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29379">#29379</a>) (<a href="7c00351680">7c00351</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/24646">#24646</a></li>
<li><strong>picker:</strong> picker column is easier to select with assistive technology (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29371">#29371</a>) (<a href="e38e2e4d35">e38e2e4</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/25221">#25221</a></li>
</ul>
<h2><a href="https://github.com/ionic-team/ionic-framework/compare/v8.0.1...v8.0.2">8.0.2</a> (2024-05-01)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>datetime:</strong> navigating months in RTL works correctly (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29421">#29421</a>) (<a href="4d09890d69">4d09890</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29198">#29198</a></li>
<li><strong>ios:</strong> large title transition accounts for back button with no text  (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29327">#29327</a>) (<a href="bd8d065e1a">bd8d065</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/28751">#28751</a></li>
<li><strong>md:</strong> item border has improved contrast in dark mode (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29398">#29398</a>) (<a href="fa85f030cf">fa85f03</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29386">#29386</a></li>
<li><strong>select:</strong> options are visible with fit-content width and fill outline (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29408">#29408</a>) (<a href="f15b62a9ca">f15b62a</a>), closes <a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29321">#29321</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="78d3076703"><code>78d3076</code></a> v8.1.0</li>
<li><a href="4ef5036c7b"><code>4ef5036</code></a> chore: sync with main (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29433">#29433</a>)</li>
<li><a href="72711bba2e"><code>72711bb</code></a> chore: sync with main</li>
<li><a href="d81c88403e"><code>d81c884</code></a> merge release-8.0.2 (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29431">#29431</a>)</li>
<li><a href="5b686efc10"><code>5b686ef</code></a> feat: add experimental transition focus manager (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29400">#29400</a>)</li>
<li><a href="665d67778f"><code>665d677</code></a> chore(): update package lock files</li>
<li><a href="101b50e14c"><code>101b50e</code></a> v8.0.2</li>
<li><a href="a01c3d49bb"><code>a01c3d4</code></a> docs(testing, components): clarify testing and focusable usage (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29424">#29424</a>)</li>
<li><a href="bd8d065e1a"><code>bd8d065</code></a> fix(ios): large title transition accounts for back button with no text  (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29327">#29327</a>)</li>
<li><a href="4d09890d69"><code>4d09890</code></a> fix(datetime): navigating months in RTL works correctly (<a href="https://redirect.github.com/ionic-team/ionic-framework/issues/29421">#29421</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/ionic-team/ionic-framework/compare/v8.0.1...v8.1.0">compare view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions


</details>
2024-05-02 15:23:12 +00:00
Nathan Rajlich
dc974b6797 [static-build] Support Ruby static site generators in AL2023 build image (#11529)
* Set `GEM_HOME` env var based on the Ruby version in `$PATH` (i.e.
`3.2.0` for the AL2 build image, `3.3.0` for the AL2023 build image)
* Copy previous `jekyll-v4` test fixture to `jekyll-v4-al2` (uses AL2
build image)
* Update `jekyll-v4` test fixture to use Jekyll 4.3.3 (required for Ruby
3.3.0), and remove `package.json` so that it uses AL2023 build image
* Copy previous `middleman-v4` test fixture to `middleman-v4-al2` (uses
AL2 build image)
* Removed `package.json` from `middleman-v4` fixture, so that it uses
AL2023 build image
2024-05-02 00:57:59 -07:00
JJ Kasper
58c6755e0c [next] Fix interception routes PPR route handling (#11527)
This ensures we properly create `.prefetch.rsc` output aliases for non-prerender outputs when PPR is enabled so that we can properly route which handles the interception route case as that is always dynamic so was missing it's `.prefetch.rsc` output causing the prefetch request to 404 unexpectedly. 

x-ref: [slack thread](https://vercel.slack.com/archives/C070ZMMKVU7/p1714511126337289)
2024-05-01 22:40:04 +00:00
dependabot[bot]
f18575fcd0 [framework-fixtures]: Bump preact from 10.20.1 to 10.21.0 in /packages/static-build/test/fixtures/preact-v10 in the core group (#11517)
Bumps the core group in /packages/static-build/test/fixtures/preact-v10 with 1 update: [preact](https://github.com/preactjs/preact).

Updates `preact` from 10.20.1 to 10.21.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/preactjs/preact/releases">preact's releases</a>.</em></p>
<blockquote>
<h2>10.21.0</h2>
<h2>Features</h2>
<ul>
<li>Debug throw on too many rerenders (<a href="https://redirect.github.com/preactjs/preact/issues/4349">#4349</a>, thanks <a href="https://github.com/rschristian"><code>@​rschristian</code></a>)</li>
<li>Add compat/client types (<a href="https://redirect.github.com/preactjs/preact/issues/4345">#4345</a>, thanks <a href="https://github.com/rschristian"><code>@​rschristian</code></a>)</li>
</ul>
<h2>Fixes</h2>
<ul>
<li>Expose hooks through compat's <code>ReactCurrentDispatcher</code> (<a href="https://redirect.github.com/preactjs/preact/issues/4342">#4342</a>, thanks <a href="https://github.com/rschristian"><code>@​rschristian</code></a>)</li>
<li>Respect default value (<a href="https://redirect.github.com/preactjs/preact/issues/4341">#4341</a>, thanks <a href="https://github.com/JoviDeCroock"><code>@​JoviDeCroock</code></a>)</li>
<li>Incorrect &quot;missing transform-jsx-source&quot; warning (<a href="https://redirect.github.com/preactjs/preact/issues/4350">#4350</a>, thanks <a href="https://github.com/rschristian"><code>@​rschristian</code></a>)</li>
</ul>
<h2>Types</h2>
<ul>
<li>Support ComponentChild(ren) in compat render/hydrate/createPortal (<a href="https://redirect.github.com/preactjs/preact/issues/4346">#4346</a>, thanks <a href="https://github.com/rschristian"><code>@​rschristian</code></a>)</li>
<li>Import and re-export PreactElement (<a href="https://redirect.github.com/preactjs/preact/issues/3228">#3228</a>, thanks <a href="https://github.com/henryqdineen"><code>@​henryqdineen</code></a>)</li>
</ul>
<h2>Maintenance</h2>
<ul>
<li>Add zustand and redux-toolkit to the demo. (<a href="https://redirect.github.com/preactjs/preact/issues/3523">#3523</a>, thanks <a href="https://github.com/MortezaMirjavadi"><code>@​MortezaMirjavadi</code></a>)</li>
<li>Optimise jsx runtime (<a href="https://redirect.github.com/preactjs/preact/issues/4337">#4337</a>, thanks <a href="https://github.com/JoviDeCroock"><code>@​JoviDeCroock</code></a>)</li>
</ul>
<h2>10.20.2</h2>
<h2>Fixes</h2>
<ul>
<li>Check whether <code>oldDom</code> is present in the DOM (<a href="https://redirect.github.com/preactjs/preact/issues/4318">#4318</a>, thanks <a href="https://github.com/JoviDeCroock"><code>@​JoviDeCroock</code></a>)</li>
<li>Simplify the logic introduced in <a href="https://redirect.github.com/preactjs/preact/issues/4322">#4322</a> &amp; use eventClock for capture events too (<a href="https://redirect.github.com/preactjs/preact/issues/4324">#4324</a>, thanks <a href="https://github.com/jviide"><code>@​jviide</code></a>)</li>
<li>Use a virtual clock instead of Date.now() for event dispatch times (<a href="https://redirect.github.com/preactjs/preact/issues/4322">#4322</a>, thanks <a href="https://github.com/jviide"><code>@​jviide</code></a>)</li>
</ul>
<h2>Types</h2>
<ul>
<li>Add template tag JSX type (<a href="https://redirect.github.com/preactjs/preact/issues/4334">#4334</a>, thanks <a href="https://github.com/marvinhagemeister"><code>@​marvinhagemeister</code></a>)</li>
</ul>
<h2>Maintenance</h2>
<ul>
<li>Integrate the new benchmarks repo and update  (<a href="https://redirect.github.com/preactjs/preact/issues/4310">#4310</a>, thanks <a href="https://github.com/andrewiggins"><code>@​andrewiggins</code></a>)</li>
<li>Some byte improvements (<a href="https://redirect.github.com/preactjs/preact/issues/4321">#4321</a>, thanks <a href="https://github.com/JoviDeCroock"><code>@​JoviDeCroock</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="a832512a4e"><code>a832512</code></a> 10.21.0 (<a href="https://redirect.github.com/preactjs/preact/issues/4360">#4360</a>)</li>
<li><a href="dbc4d6109c"><code>dbc4d61</code></a> Merge pull request <a href="https://redirect.github.com/preactjs/preact/issues/4350">#4350</a> from preactjs/fix/jsx-source-debug-warning</li>
<li><a href="50c3f3ebe8"><code>50c3f3e</code></a> Merge branch 'main' into fix/jsx-source-debug-warning</li>
<li><a href="613cacc0e3"><code>613cacc</code></a> feat: debug throw on too many rerenders (<a href="https://redirect.github.com/preactjs/preact/issues/4349">#4349</a>)</li>
<li><a href="2e2f5ab3a7"><code>2e2f5ab</code></a> fix: Appease ESLint</li>
<li><a href="d4ff12e97c"><code>d4ff12e</code></a> test: Add test for fixed jsx-source warning</li>
<li><a href="61ce7a4f26"><code>61ce7a4</code></a> refactor: Slight rename</li>
<li><a href="784af469d1"><code>784af46</code></a> fix: Incorrect &quot;missing <code>transform-jsx-source</code>&quot; warning</li>
<li><a href="aa95aa924d"><code>aa95aa9</code></a> Merge pull request <a href="https://redirect.github.com/preactjs/preact/issues/4346">#4346</a> from preactjs/types/fix-hydrate-render-createPortal</li>
<li><a href="1fc799dc6f"><code>1fc799d</code></a> refactor: Support ComponentChild(ren) in compat render/hydrate/createPortal</li>
<li>Additional commits viewable in <a href="https://github.com/preactjs/preact/compare/10.20.1...10.21.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=preact&package-manager=npm_and_yarn&previous-version=10.20.1&new-version=10.21.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions


</details>
2024-04-30 21:37:27 +00:00
Erika Rowland
d6412be6bc Migrate build argument parsing (#11492)
Migrate `vc build` to use updated argument parsing helpers.

Adds `try/catch` and `handleError` logic that was previously missing.
2024-04-30 21:23:41 +00:00
JJ Kasper
52e435aa5d Fix missing .rsc outputs for pages prerenders (#11503)
When generating the outputs for pages when app router is also present we weren't creating the placeholder `.rsc` outputs for all prerender outputs so when a `fallback: false` route was configured in the pages router that could fall through to an app route it causes issues with client-navigation behaving differently than a direct visit. 

This ensures we add the placeholder `.rsc` outputs for all prerenders so that `fallback: false` pages don't have this unexpected behavior.
2024-04-30 20:34:41 +00:00
Vercel Release Bot
303256343a Version Packages (#11523)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## vercel@34.1.7

### Patch Changes

- Updated dependencies
\[[`3420ba015`](3420ba0153)]:
    -   @vercel/next@4.2.6

## @vercel/next@4.2.6

### Patch Changes

- [next] Update test fixture version
([#11514](https://github.com/vercel/vercel/pull/11514))

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-30 13:09:58 -07:00
Vercel Release Bot
88f3a816d9 Version Packages (#11520)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## vercel@34.1.6

### Patch Changes

- Updated dependencies
\[[`b1adaf76e`](b1adaf76ec),
[`3fb97d1d2`](3fb97d1d27)]:
    -   @vercel/next@4.2.5
    -   @vercel/static-build@2.5.2

## @vercel/next@4.2.5

### Patch Changes

- Only rewrite next-action requests to `.action` handlers
([#11504](https://github.com/vercel/vercel/pull/11504))

## @vercel/static-build@2.5.2

### Patch Changes

- Revert `SUPPORTED_RUBY_VERSION` change to fix Ruby site generators on
AL2 build image ([#11522](https://github.com/vercel/vercel/pull/11522))

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-30 13:04:00 -07:00
JJ Kasper
3420ba0153 [next] Update test fixture version (#11514)
Noticed in https://github.com/vercel/vercel/pull/11503 that this test
fixture is incorrectly locked to a specific version when we don't want
it to be.
2024-04-30 13:03:36 -07:00
Nathan Rajlich
3fb97d1d27 [static-build] Add changeset for SUPPORTED_RUBY_VERSION revert (#11522)
A changeset should have been included for
https://github.com/vercel/vercel/pull/11518. Adding that here.
2024-04-30 13:01:59 -07:00
Nathan Rajlich
f11c7024c4 [static-build] Fix running of e2e tests (#11518)
We accidentally committed a change which prevented the static-build e2e
tests, so fixing that.

Also reverting a change from https://github.com/vercel/vercel/pull/11497
which was causing issues with Ruby-based static site generators when
using the AL2 build image.
2024-04-30 12:57:25 -07:00
Sean Massa
5eb4cc6f5d Revert "[framework-fixtures]: Bump the core group in /packages/static-build/test/fixtures/hydrogen-v2023 with 6 updates" (#11521)
Reverts vercel/vercel#11480

This was merged in when we weren't running the tests.
([Fix](https://github.com/vercel/vercel/pull/11518)).
2024-04-30 12:35:07 -07:00
Zack Tanner
b1adaf76ec [next] Only rewrite next-action requests to .action handlers (#11504)
This only rewrites requests that contain a `next-action` header (explicitly indicating it's a server action). A side effect is that POST requests to a server action on a static route, without a next-action header, won't be marked as streaming (but will still execute normally). This is fine as only the handled action needs to stream. 

This relands .action handling behind a feature flag.
2024-04-30 18:32:54 +00:00
237 changed files with 8649 additions and 2023 deletions

View File

@@ -37,7 +37,7 @@ You can use the `dev` script to run local changes as if you were invoking Vercel
When contributing to this repository, please first discuss the change you wish to make via [GitHub Discussions](https://github.com/vercel/vercel/discussions/new) with the owners of this repository before submitting a Pull Request.
Please read our [Code of Conduct](CODE_OF_CONDUCT.md) and follow it in all your interactions with the project.
Please read our [Code of Conduct](./.github/CODE_OF_CONDUCT.md) and follow it in all your interactions with the project.
### Local development

View File

@@ -1,5 +1,44 @@
# vercel
## 34.1.10
### Patch Changes
- Updated dependencies [[`119f80e96`](https://github.com/vercel/vercel/commit/119f80e9611a7a5a755aa689502dcdab323194aa), [`11584b0e9`](https://github.com/vercel/vercel/commit/11584b0e9b55f312f34d0d6467ab498e472ac9df), [`3023122d4`](https://github.com/vercel/vercel/commit/3023122d4e0dd292340d9e9e61ef232baf6e610d), [`0e774b6be`](https://github.com/vercel/vercel/commit/0e774b6be0c832213a64124e1f4fc6d150e87d9f)]:
- @vercel/next@4.2.9
- @vercel/static-build@2.5.4
## 34.1.9
### Patch Changes
- Updated dependencies [[`5a532a5b9`](https://github.com/vercel/vercel/commit/5a532a5b948994ba04783ac560357eed9f94a3f3), [`50fc27ba5`](https://github.com/vercel/vercel/commit/50fc27ba5773870956300bbbaffbe387d549bc12), [`c1d852295`](https://github.com/vercel/vercel/commit/c1d85229509dd319a1f11beb940a759113564d33), [`a5ea04154`](https://github.com/vercel/vercel/commit/a5ea04154ba26ee4e635d8953aa4f0d9d82d3a96)]:
- @vercel/next@4.2.8
- @vercel/node@3.1.0
## 34.1.8
### Patch Changes
- Updated dependencies [[`52e435aa5`](https://github.com/vercel/vercel/commit/52e435aa5d7b4014d19477969ad5cbfbe94aa76f), [`124846a3e`](https://github.com/vercel/vercel/commit/124846a3e65a3bf1ae82327fd4ba2b132674fb39), [`dc974b679`](https://github.com/vercel/vercel/commit/dc974b6797de0b6e90373c92e1f2bbdafcfc6687), [`58c6755e0`](https://github.com/vercel/vercel/commit/58c6755e0c12cae2ce55978b7bf8722133151196)]:
- @vercel/next@4.2.7
- @vercel/static-build@2.5.3
## 34.1.7
### Patch Changes
- Updated dependencies [[`3420ba015`](https://github.com/vercel/vercel/commit/3420ba0153dcabffef7114ba2361fb0f3c43a7b3)]:
- @vercel/next@4.2.6
## 34.1.6
### Patch Changes
- Updated dependencies [[`b1adaf76e`](https://github.com/vercel/vercel/commit/b1adaf76ec17d1bbfe30a2bf65405bd886fa9bcf), [`3fb97d1d2`](https://github.com/vercel/vercel/commit/3fb97d1d270e835ce34a687bd234ea53dfe446a2)]:
- @vercel/next@4.2.5
- @vercel/static-build@2.5.2
## 34.1.5
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "34.1.5",
"version": "34.1.10",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -36,13 +36,13 @@
"@vercel/fun": "1.1.0",
"@vercel/go": "3.1.1",
"@vercel/hydrogen": "1.0.2",
"@vercel/next": "4.2.4",
"@vercel/node": "3.0.28",
"@vercel/next": "4.2.9",
"@vercel/node": "3.1.0",
"@vercel/python": "4.2.0",
"@vercel/redwood": "2.0.8",
"@vercel/remix-builder": "2.1.5",
"@vercel/ruby": "2.1.0",
"@vercel/static-build": "2.5.1",
"@vercel/static-build": "2.5.4",
"chokidar": "3.3.1"
},
"devDependencies": {

View File

@@ -10,7 +10,7 @@ export const buildCommand: Command = {
name: 'prod',
description: 'Build a production deployment',
shorthand: null,
type: String,
type: Boolean,
deprecated: false,
},
{

View File

@@ -43,7 +43,7 @@ import type { VercelConfig } from '@vercel/client';
import pull from '../pull';
import { staticFiles as getFiles } from '../../util/get-files';
import Client from '../../util/client';
import getArgs from '../../util/get-args';
import { parseArguments } from '../../util/get-args';
import cmd from '../../util/output/cmd';
import * as cli from '../../util/pkg-name';
import cliPkg from '../../util/pkg';
@@ -66,12 +66,13 @@ import {
import { importBuilders } from '../../util/build/import-builders';
import { initCorepack, cleanupCorepack } from '../../util/build/corepack';
import { sortBuilders } from '../../util/build/sort-builders';
import { toEnumerableError } from '../../util/error';
import { handleError, toEnumerableError } from '../../util/error';
import { validateConfig } from '../../util/validate-config';
import { setMonorepoDefaultSettings } from '../../util/build/monorepo';
import { help } from '../help';
import { buildCommand } from './command';
import { scrubArgv } from '../../util/build/scrub-argv';
import { getFlagsSpecification } from '../../util/get-flags-specification';
type BuildResult = BuildResultV2 | BuildResultV3;
@@ -133,22 +134,26 @@ export default async function main(client: Client): Promise<number> {
process.env.__VERCEL_BUILD_RUNNING = '1';
}
// Parse CLI args
const argv = getArgs(client.argv.slice(2), {
'--output': String,
'--prod': Boolean,
'--yes': Boolean,
'-y': '--yes',
});
let parsedArgs = null;
if (argv['--help']) {
const flagsSpecification = getFlagsSpecification(buildCommand.options);
// Parse CLI args
try {
parsedArgs = parseArguments(client.argv.slice(2), flagsSpecification);
} catch (error) {
handleError(error);
return 1;
}
if (parsedArgs.flags['--help']) {
output.print(help(buildCommand, { columns: client.stderr.columns }));
return 2;
}
// Build `target` influences which environment variables will be used
const target = argv['--prod'] ? 'production' : 'preview';
const yes = Boolean(argv['--yes']);
const target = parsedArgs.flags['--prod'] ? 'production' : 'preview';
const yes = Boolean(parsedArgs.flags['--yes']);
try {
await validateNpmrc(cwd);
@@ -213,8 +218,8 @@ export default async function main(client: Client): Promise<number> {
// Delete output directory from potential previous build
const defaultOutputDir = join(cwd, projectRootDirectory, OUTPUT_DIR);
const outputDir = argv['--output']
? resolve(argv['--output'])
const outputDir = parsedArgs.flags['--output']
? resolve(parsedArgs.flags['--output'])
: defaultOutputDir;
await Promise.all([
fs.remove(outputDir),

View File

@@ -0,0 +1,19 @@
# @vercel/functions
## 1.0.2
### Patch Changes
- Convert package to CommonJS ([#11569](https://github.com/vercel/vercel/pull/11569))
## 1.0.1
### Patch Changes
- Don't throw error if context is missing ([`0817527f9`](https://github.com/vercel/vercel/commit/0817527f9e9d0d5fceb73f21e695089349a96d3e))
## 1.0.0
### Major Changes
- Initial release ([#11553](https://github.com/vercel/vercel/pull/11553))

17
packages/functions/index.d.ts vendored Normal file
View File

@@ -0,0 +1,17 @@
/**
* Extends the lifetime of the request handler for the lifetime of the given {@link Promise}
* @see https://developer.mozilla.org/en-US/docs/Web/API/ExtendableEvent/waitUntil
*
* @param promise The promise to wait for.
* @example
*
* ```
* import { waitUntil } from '@vercel/functions';
*
* export function GET(request) {
* waitUntil(fetch('https://vercel.com'));
* return new Response('OK');
* }
* ```
*/
export function waitUntil(promise: Promise<unknown>): void;

View File

@@ -0,0 +1,16 @@
/* global globalThis */
exports.waitUntil = promise => {
if (
promise === null ||
typeof promise !== 'object' ||
typeof promise.then !== 'function'
) {
throw new TypeError(
`waitUntil can only be called with a Promise, got ${typeof promise}`
);
}
const ctx = globalThis[Symbol.for('@vercel/request-context')]?.get?.() ?? {};
ctx.waitUntil?.(promise);
};

View File

@@ -0,0 +1,42 @@
/* global globalThis */
import { vi, expect, test } from 'vitest';
import { waitUntil } from '.';
test.each([
{},
() => {},
function () {},
NaN,
1,
false,
undefined,
null,
[],
'▲',
])('waitUntil throws when called with %s', input => {
expect(() => waitUntil(input)).toThrow(TypeError);
expect(() => waitUntil(input)).toThrow(
`waitUntil can only be called with a Promise, got ${typeof input}`
);
});
test.each([null, undefined, {}])(
'waitUntil does not throw an error when context is %s',
input => {
const promise = Promise.resolve();
globalThis[Symbol.for('@vercel/request-context')] = input;
expect(() => waitUntil(promise)).not.toThrow();
}
);
test('waitUntil calls ctx.waitUntil when available', async () => {
const promise = Promise.resolve();
const waitUntilMock = vi.fn().mockReturnValue(promise);
globalThis[Symbol.for('@vercel/request-context')] = {
get: () => ({ waitUntil: waitUntilMock }),
};
waitUntil(promise);
expect(waitUntilMock).toHaveBeenCalledWith(promise);
});

View File

@@ -0,0 +1,33 @@
{
"name": "@vercel/functions",
"description": "Runtime functions to be used with your Vercel Functions",
"homepage": "https://vercel.com",
"version": "1.0.2",
"types": "index.d.ts",
"main": "index.js",
"repository": {
"directory": "packages/functions",
"type": "git",
"url": "git+https://github.com/vercel/vercel.git"
},
"bugs": {
"url": "https://github.com/vercel/vercel/issues"
},
"devDependencies": {
"vitest": "1.3.1"
},
"engines": {
"node": ">= 16"
},
"files": [
"index.d.ts",
"index.js"
],
"scripts": {
"test": "vitest"
},
"license": "Apache-2.0",
"publishConfig": {
"access": "public"
}
}

View File

@@ -1,5 +1,47 @@
# @vercel/next
## 4.2.9
### Patch Changes
- Support incremental partial prerendering ([#11560](https://github.com/vercel/vercel/pull/11560))
- ensure `.action` outputs are created for edge functions ([#11568](https://github.com/vercel/vercel/pull/11568))
- ([#11566](https://github.com/vercel/vercel/pull/11566))
## 4.2.8
### Patch Changes
- Fix missing initial RSC headers ([#11552](https://github.com/vercel/vercel/pull/11552))
- Remove .prefetch.rsc rewrites for non-PPR ([#11540](https://github.com/vercel/vercel/pull/11540))
- [next] rename middleware manifest env ([#11549](https://github.com/vercel/vercel/pull/11549))
## 4.2.7
### Patch Changes
- Fix missing .rsc outputs for pages prerenders ([#11503](https://github.com/vercel/vercel/pull/11503))
- Fix base path app index RSC handling ([#11528](https://github.com/vercel/vercel/pull/11528))
- Fix interception routes PPR route handling ([#11527](https://github.com/vercel/vercel/pull/11527))
## 4.2.6
### Patch Changes
- [next] Update test fixture version ([#11514](https://github.com/vercel/vercel/pull/11514))
## 4.2.5
### Patch Changes
- Only rewrite next-action requests to `.action` handlers ([#11504](https://github.com/vercel/vercel/pull/11504))
## 4.2.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/next",
"version": "4.2.4",
"version": "4.2.9",
"license": "Apache-2.0",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",

View File

@@ -1928,6 +1928,7 @@ export const build: BuildV2 = async ({
bypassToken: prerenderManifest.bypassToken || '',
isServerMode,
experimentalPPRRoutes,
hasActionOutputSupport: false,
}).then(arr =>
localizeDynamicRoutes(
arr,
@@ -1958,6 +1959,7 @@ export const build: BuildV2 = async ({
bypassToken: prerenderManifest.bypassToken || '',
isServerMode,
experimentalPPRRoutes,
hasActionOutputSupport: false,
}).then(arr =>
arr.map(route => {
route.src = route.src.replace('^', `^${dynamicPrefix}`);

View File

@@ -51,6 +51,7 @@ import {
normalizePrefetches,
CreateLambdaFromPseudoLayersOptions,
getPostponeResumePathname,
LambdaGroup,
MAX_UNCOMPRESSED_LAMBDA_SIZE,
} from './utils';
import {
@@ -68,6 +69,7 @@ const CORRECT_NOT_FOUND_ROUTES_VERSION = 'v12.0.1';
const CORRECT_MIDDLEWARE_ORDER_VERSION = 'v12.1.7-canary.29';
const NEXT_DATA_MIDDLEWARE_RESOLVING_VERSION = 'v12.1.7-canary.33';
const EMPTY_ALLOW_QUERY_FOR_PRERENDERED_VERSION = 'v12.2.0';
const ACTION_OUTPUT_SUPPORT_VERSION = 'v14.2.2';
const CORRECTED_MANIFESTS_VERSION = 'v12.2.0';
// Ideally this should be in a Next.js manifest so we can change it in
@@ -199,6 +201,9 @@ export async function serverBuild({
nextVersion,
EMPTY_ALLOW_QUERY_FOR_PRERENDERED_VERSION
);
const hasActionOutputSupport =
semver.gte(nextVersion, ACTION_OUTPUT_SUPPORT_VERSION) &&
Boolean(process.env.NEXT_EXPERIMENTAL_STREAMING_ACTIONS);
const projectDir = requiredServerFilesManifest.relativeAppDir
? path.join(baseDir, requiredServerFilesManifest.relativeAppDir)
: requiredServerFilesManifest.appDir || entryPath;
@@ -213,7 +218,9 @@ export async function serverBuild({
}
const experimental = {
ppr: requiredServerFilesManifest.config.experimental?.ppr === true,
ppr:
requiredServerFilesManifest.config.experimental?.ppr === true ||
requiredServerFilesManifest.config.experimental?.ppr === 'incremental',
};
let appRscPrefetches: UnwrapPromise<ReturnType<typeof glob>> = {};
@@ -223,8 +230,6 @@ export async function serverBuild({
if (appPathRoutesManifest) {
appDir = path.join(pagesDir, '../app');
appBuildTraces = await glob('**/*.js.nft.json', appDir);
// TODO: maybe?
appRscPrefetches = experimental.ppr
? {}
: await glob(`**/*${RSC_PREFETCH_SUFFIX}`, appDir);
@@ -246,7 +251,7 @@ export async function serverBuild({
if (rewrite.src && rewrite.dest) {
rewrite.src = rewrite.src.replace(
/\/?\(\?:\/\)\?/,
'(?<rscsuff>(\\.prefetch)?\\.rsc)?(?:/)?'
`(?<rscsuff>${experimental.ppr ? '(\\.prefetch)?' : ''}\\.rsc)?(?:/)?`
);
let destQueryIndex = rewrite.dest.indexOf('?');
@@ -926,11 +931,23 @@ export async function serverBuild({
inversedAppPathManifest,
});
const appRouterStreamingActionLambdaGroups: LambdaGroup[] = [];
for (const group of appRouterLambdaGroups) {
if (!group.isPrerenders || group.isExperimentalPPR) {
group.isStreaming = true;
}
group.isAppRouter = true;
// We create a streaming variant of the Prerender lambda group
// to support actions that are part of a Prerender
if (hasActionOutputSupport) {
appRouterStreamingActionLambdaGroups.push({
...group,
isActionLambda: true,
isStreaming: true,
});
}
}
for (const group of appRouteHandlersLambdaGroups) {
@@ -982,6 +999,13 @@ export async function serverBuild({
pseudoLayerBytes: group.pseudoLayerBytes,
uncompressedLayerBytes: group.pseudoLayerUncompressedBytes,
})),
appRouterStreamingPrerenderLambdaGroups:
appRouterStreamingActionLambdaGroups.map(group => ({
pages: group.pages,
isPrerender: group.isPrerenders,
pseudoLayerBytes: group.pseudoLayerBytes,
uncompressedLayerBytes: group.pseudoLayerUncompressedBytes,
})),
appRouteHandlersLambdaGroups: appRouteHandlersLambdaGroups.map(
group => ({
pages: group.pages,
@@ -999,6 +1023,7 @@ export async function serverBuild({
const combinedGroups = [
...pageLambdaGroups,
...appRouterLambdaGroups,
...appRouterStreamingActionLambdaGroups,
...apiLambdaGroups,
...appRouteHandlersLambdaGroups,
];
@@ -1208,6 +1233,11 @@ export async function serverBuild({
let outputName = path.posix.join(entryDirectory, pageName);
if (group.isActionLambda) {
// give the streaming prerenders a .action suffix
outputName = `${outputName}.action`;
}
// If this is a PPR page, then we should prefix the output name.
if (isPPR) {
if (!revalidate) {
@@ -1378,6 +1408,7 @@ export async function serverBuild({
isServerMode: true,
dynamicMiddlewareRouteMap: middleware.dynamicRouteMap,
experimentalPPRRoutes,
hasActionOutputSupport,
}).then(arr =>
localizeDynamicRoutes(
arr,
@@ -1557,10 +1588,23 @@ export async function serverBuild({
if (lambdas[pathname]) {
lambdas[`${pathname}.rsc`] = lambdas[pathname];
if (experimental.ppr) {
lambdas[`${pathname}${RSC_PREFETCH_SUFFIX}`] = lambdas[pathname];
}
}
if (edgeFunctions[pathname]) {
edgeFunctions[`${pathname}.rsc`] = edgeFunctions[pathname];
if (experimental.ppr) {
edgeFunctions[`${pathname}${RSC_PREFETCH_SUFFIX}`] =
edgeFunctions[pathname];
}
if (hasActionOutputSupport) {
edgeFunctions[`${pathname}.action`] = edgeFunctions[pathname];
}
}
}
}
@@ -1863,7 +1907,7 @@ export async function serverBuild({
...(appDir
? [
...(rscPrefetchHeader
...(rscPrefetchHeader && experimental.ppr
? [
{
src: `^${path.posix.join('/', entryDirectory, '/')}`,
@@ -1905,9 +1949,44 @@ export async function serverBuild({
},
]
: []),
...(hasActionOutputSupport
? [
// Create rewrites for streaming prerenders (.action routes)
// This contains separate rewrites for each possible "has" (action header, or content-type)
// Also includes separate handling for index routes which should match to /index.action.
// This follows the same pattern as the rewrites for .rsc files.
{
src: `^${path.posix.join('/', entryDirectory, '/')}`,
dest: path.posix.join('/', entryDirectory, '/index.action'),
has: [
{
type: 'header',
key: 'next-action',
},
],
continue: true,
override: true,
},
{
src: `^${path.posix.join(
'/',
entryDirectory,
'/((?!.+\\.action).+?)(?:/)?$'
)}`,
dest: path.posix.join('/', entryDirectory, '/$1.action'),
has: [
{
type: 'header',
key: 'next-action',
},
],
continue: true,
override: true,
},
]
: []),
{
src: `^${path.posix.join('/', entryDirectory, '/')}`,
src: `^${path.posix.join('/', entryDirectory, '/?')}`,
has: [
{
type: 'header',
@@ -1970,47 +2049,6 @@ export async function serverBuild({
]
: []),
...(rscPrefetchHeader && !experimental.ppr
? [
{
src: path.posix.join(
'/',
entryDirectory,
`/__index${RSC_PREFETCH_SUFFIX}`
),
dest: path.posix.join('/', entryDirectory, '/index.rsc'),
has: [
{
type: 'header',
key: rscPrefetchHeader,
},
],
continue: true,
override: true,
},
{
src: `^${path.posix.join(
'/',
entryDirectory,
`/(.+?)${RSC_PREFETCH_SUFFIX}(?:/)?$`
)}`,
dest: path.posix.join(
'/',
entryDirectory,
`/$1${experimental.ppr ? RSC_PREFETCH_SUFFIX : '.rsc'}`
),
has: [
{
type: 'header',
key: rscPrefetchHeader,
},
],
continue: true,
override: true,
},
]
: []),
// These need to come before handle: miss or else they are grouped
// with that routing section
...afterFilesRewrites,

View File

@@ -321,6 +321,7 @@ export async function getDynamicRoutes({
isServerMode,
dynamicMiddlewareRouteMap,
experimentalPPRRoutes,
hasActionOutputSupport,
}: {
entryPath: string;
entryDirectory: string;
@@ -333,6 +334,7 @@ export async function getDynamicRoutes({
isServerMode?: boolean;
dynamicMiddlewareRouteMap?: ReadonlyMap<string, RouteWithSrc>;
experimentalPPRRoutes: ReadonlySet<string>;
hasActionOutputSupport: boolean;
}): Promise<RouteWithSrc[]> {
if (routesManifest) {
switch (routesManifest.version) {
@@ -423,14 +425,25 @@ export async function getDynamicRoutes({
});
}
routes.push({
...route,
src: route.src.replace(
new RegExp(escapeStringRegexp('(?:/)?$')),
'(?:\\.rsc)(?:/)?$'
),
dest: route.dest?.replace(/($|\?)/, '.rsc$1'),
});
if (hasActionOutputSupport) {
routes.push({
...route,
src: route.src.replace(
new RegExp(escapeStringRegexp('(?:/)?$')),
'(?<nxtsuffix>(?:\\.action|\\.rsc))(?:/)?$'
),
dest: route.dest?.replace(/($|\?)/, '$nxtsuffix$1'),
});
} else {
routes.push({
...route,
src: route.src.replace(
new RegExp(escapeStringRegexp('(?:/)?$')),
'(?:\\.rsc)(?:/)?$'
),
dest: route.dest?.replace(/($|\?)/, '.rsc$1'),
});
}
routes.push(route);
}
@@ -1487,6 +1500,7 @@ export type LambdaGroup = {
isStreaming?: boolean;
isPrerenders?: boolean;
isExperimentalPPR?: boolean;
isActionLambda?: boolean;
isPages?: boolean;
isApiLambda: boolean;
pseudoLayer: PseudoLayer;
@@ -2451,6 +2465,7 @@ export const onPrerenderRoute =
...(rscEnabled
? {
initialHeaders: {
...initialHeaders,
'content-type': rscContentTypeHeader,
vary: rscVaryHeader,
// If it contains a pre-render, then it was postponed.
@@ -2463,6 +2478,22 @@ export const onPrerenderRoute =
});
}
// we need to ensure all prerenders have a matching .rsc output
// otherwise routing could fall through unexpectedly for the
// fallback: false case as it doesn't have a dynamic route
// to catch the `.rsc` request for app -> pages routing
if (outputPrerenderPathData?.endsWith('.json') && appDir) {
const dummyOutput = new FileBlob({
data: '{}',
contentType: 'application/json',
});
const rscKey = `${outputPathPage}.rsc`;
const prefetchRscKey = `${outputPathPage}${RSC_PREFETCH_SUFFIX}`;
prerenders[rscKey] = dummyOutput;
prerenders[prefetchRscKey] = dummyOutput;
}
++prerenderGroup;
if (routesManifest?.i18n && isBlocking) {
@@ -2754,7 +2785,7 @@ interface EdgeFunctionInfoV2 extends BaseEdgeFunctionInfo {
interface EdgeFunctionInfoV3 extends BaseEdgeFunctionInfo {
matchers: EdgeFunctionMatcher[];
environments: Record<string, string>;
env: Record<string, string>;
}
interface EdgeFunctionMatcher {
@@ -2989,7 +3020,7 @@ export async function getMiddlewareBundle({
slug: 'nextjs',
version: nextVersion,
},
environment: edgeFunction.environments,
environment: edgeFunction.env,
});
})(),
routeMatchers: getRouteMatchers(edgeFunction, routesManifest),
@@ -3169,7 +3200,7 @@ export function upgradeMiddlewareManifestV1(
return {
...rest,
matchers: [{ regexp }],
environments: {},
env: {},
};
}
@@ -3197,7 +3228,7 @@ export function upgradeMiddlewareManifestV2(
const { ...rest } = v2Info;
return {
...rest,
environments: {},
env: {},
};
}

View File

@@ -0,0 +1,6 @@
'use server';
export async function increment(value) {
await new Promise(resolve => setTimeout(resolve, 500));
return value + 1;
}

View File

@@ -0,0 +1,19 @@
'use client';
import { useState } from 'react';
import { increment } from '../../../actions';
export default function Page() {
const [count, setCount] = useState(0);
async function updateCount() {
const newCount = await increment(count);
setCount(newCount);
}
return (
<form action={updateCount}>
<div id="count">{count}</div>
<button>Submit</button>
</form>
);
}

View File

@@ -0,0 +1,5 @@
export const dynamic = 'force-dynamic';
export default function Layout({ children }) {
return children;
}

View File

@@ -0,0 +1,19 @@
'use client';
import { useState } from 'react';
import { increment } from '../../../actions';
export default function Page() {
const [count, setCount] = useState(0);
async function updateCount() {
const newCount = await increment(count);
setCount(newCount);
}
return (
<form action={updateCount}>
<div id="count">{count}</div>
<button>Submit</button>
</form>
);
}

View File

@@ -0,0 +1,5 @@
export const dynamic = 'force-static';
export default function Layout({ children }) {
return children;
}

View File

@@ -0,0 +1,19 @@
'use client';
import { useState } from 'react';
import { increment } from '../../actions';
export default function Page() {
const [count, setCount] = useState(0);
async function updateCount() {
const newCount = await increment(count);
setCount(newCount);
}
return (
<form action={updateCount}>
<div id="count">{count}</div>
<button>Submit</button>
</form>
);
}

View File

@@ -0,0 +1,19 @@
'use client';
import { useState } from 'react';
import { increment } from '../../../../actions';
export default function Page() {
const [count, setCount] = useState(0);
async function updateCount() {
const newCount = await increment(count);
setCount(newCount);
}
return (
<form action={updateCount}>
<div id="count">{count}</div>
<button>Submit</button>
</form>
);
}

View File

@@ -0,0 +1,5 @@
export const dynamic = 'force-dynamic';
export default function Layout({ children }) {
return children;
}

View File

@@ -0,0 +1,19 @@
'use client';
import { useState } from 'react';
import { increment } from '../../../../actions';
export default function Page() {
const [count, setCount] = useState(0);
async function updateCount() {
const newCount = await increment(count);
setCount(newCount);
}
return (
<form action={updateCount}>
<div id="count">{count}</div>
<button>Submit</button>
</form>
);
}

View File

@@ -0,0 +1,5 @@
export const dynamic = 'force-static';
export default function Layout({ children }) {
return children;
}

View File

@@ -0,0 +1,19 @@
'use client';
import { useState } from 'react';
import { increment } from '../../../actions';
export default function Page() {
const [count, setCount] = useState(0);
async function updateCount() {
const newCount = await increment(count);
setCount(newCount);
}
return (
<form action={updateCount}>
<div id="count">{count}</div>
<button>Submit</button>
</form>
);
}

View File

@@ -0,0 +1,5 @@
export const runtime = 'edge';
export default function Layout({ children }) {
return <div>{children}</div>;
}

View File

@@ -0,0 +1,45 @@
'use client';
// @ts-ignore
import { useCallback, useState } from 'react';
function request(method) {
return fetch('/api/test', {
method,
headers: {
'content-type': 'multipart/form-data;.*',
},
});
}
export default function Home() {
const [result, setResult] = useState('Press submit');
const onClick = useCallback(async method => {
const res = await request(method);
const text = await res.text();
setResult(res.ok ? `${method} ${text}` : 'Error: ' + res.status);
}, []);
return (
<>
<div className="flex flex-col items-center justify-center h-screen">
<div className="flex flex-row space-x-2 items-center justify-center">
<button
className="border border-white rounded-sm p-4 mb-4"
onClick={() => onClick('GET')}
>
Submit GET
</button>
<button
className="border border-white rounded-sm p-4 mb-4"
onClick={() => onClick('POST')}
>
Submit POST
</button>
</div>
<div className="text-white">{result}</div>
</div>
</>
);
}

View File

@@ -0,0 +1,5 @@
export const dynamic = 'force-dynamic';
export default function Layout({ children }) {
return children;
}

View File

@@ -0,0 +1,15 @@
import { revalidatePath } from 'next/cache';
export default async function Page() {
async function serverAction() {
'use server';
await new Promise(resolve => setTimeout(resolve, 1000));
revalidatePath('/dynamic');
}
return (
<form action={serverAction}>
<button>Submit</button>
</form>
);
}

View File

@@ -0,0 +1,15 @@
import { revalidatePath } from 'next/cache';
export default async function Page() {
async function serverAction() {
'use server';
await new Promise(resolve => setTimeout(resolve, 1000));
revalidatePath('/dynamic');
}
return (
<form action={serverAction}>
<button>Submit</button>
</form>
);
}

View File

@@ -0,0 +1,19 @@
import { revalidatePath } from 'next/cache';
export default async function Page() {
async function serverAction() {
'use server';
await new Promise(resolve => setTimeout(resolve, 1000));
revalidatePath('/dynamic');
}
return (
<form action={serverAction}>
<button>Submit</button>
</form>
);
}
export function generateStaticParams() {
return [{ slug: 'pre-generated' }];
}

View File

@@ -0,0 +1,5 @@
export const dynamic = 'force-static';
export default function Layout({ children }) {
return children;
}

View File

@@ -0,0 +1,15 @@
import { revalidatePath } from 'next/cache'
export default async function Page() {
async function serverAction() {
'use server';
await new Promise((resolve) => setTimeout(resolve, 1000));
revalidatePath('/dynamic');
}
return (
<form action={serverAction}>
<button>Submit</button>
</form>
);
}

View File

@@ -0,0 +1,10 @@
export default function Root({ children }) {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>{children}</body>
</html>
);
}

View File

@@ -0,0 +1,45 @@
'use client';
// @ts-ignore
import { useCallback, useState } from 'react';
function request(method) {
return fetch('/api/test', {
method,
headers: {
'content-type': 'multipart/form-data;.*',
},
});
}
export default function Home() {
const [result, setResult] = useState('Press submit');
const onClick = useCallback(async method => {
const res = await request(method);
const text = await res.text();
setResult(res.ok ? `${method} ${text}` : 'Error: ' + res.status);
}, []);
return (
<>
<div className="flex flex-col items-center justify-center h-screen">
<div className="flex flex-row space-x-2 items-center justify-center">
<button
className="border border-white rounded-sm p-4 mb-4"
onClick={() => onClick('GET')}
>
Submit GET
</button>
<button
className="border border-white rounded-sm p-4 mb-4"
onClick={() => onClick('POST')}
>
Submit POST
</button>
</div>
<div className="text-white">{result}</div>
</div>
</>
);
}

View File

@@ -0,0 +1,5 @@
export const dynamic = 'force-dynamic';
export default function Layout({ children }) {
return children;
}

View File

@@ -0,0 +1,15 @@
import { revalidatePath } from 'next/cache';
export default async function Page() {
async function serverAction() {
'use server';
await new Promise(resolve => setTimeout(resolve, 1000));
revalidatePath('/dynamic');
}
return (
<form action={serverAction}>
<button>Submit</button>
</form>
);
}

View File

@@ -0,0 +1,15 @@
import { revalidatePath } from 'next/cache';
export default async function Page() {
async function serverAction() {
'use server';
await new Promise(resolve => setTimeout(resolve, 1000));
revalidatePath('/dynamic');
}
return (
<form action={serverAction}>
<button>Submit</button>
</form>
);
}

View File

@@ -0,0 +1,19 @@
import { revalidatePath } from 'next/cache';
export default async function Page() {
async function serverAction() {
'use server';
await new Promise(resolve => setTimeout(resolve, 1000));
revalidatePath('/dynamic');
}
return (
<form action={serverAction}>
<button>Submit</button>
</form>
);
}
export function generateStaticParams() {
return [{ slug: 'pre-generated' }];
}

View File

@@ -0,0 +1,5 @@
export const dynamic = 'force-static';
export default function Layout({ children }) {
return children;
}

View File

@@ -0,0 +1,15 @@
import { revalidatePath } from 'next/cache'
export default async function Page() {
async function serverAction() {
'use server';
await new Promise((resolve) => setTimeout(resolve, 1000));
revalidatePath('/dynamic');
}
return (
<form action={serverAction}>
<button>Submit</button>
</form>
);
}

View File

@@ -0,0 +1,280 @@
/* eslint-env jest */
const path = require('path');
const { deployAndTest } = require('../../utils');
const fetch = require('../../../../../test/lib/deployment/fetch-retry');
const ctx = {};
function findActionId(page, runtime) {
page = `app${page}/page`; // add /app prefix and /page suffix
for (const [actionId, details] of Object.entries(
ctx.actionManifest[runtime]
)) {
if (details.workers[page]) {
return actionId;
}
}
return null;
}
function generateFormDataPayload(actionId) {
return {
method: 'POST',
body: `------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"1_$ACTION_ID_${actionId}\"\r\n\r\n\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"0\"\r\n\r\n[\"$K1\"]\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ--\r\n`,
headers: {
'Content-Type':
'multipart/form-data; boundary=----WebKitFormBoundaryHcVuFa30AN0QV3uZ',
'Next-Action': actionId,
},
};
}
describe(`${__dirname.split(path.sep).pop()}`, () => {
beforeAll(async () => {
const info = await deployAndTest(__dirname);
const actionManifest = await fetch(
`${info.deploymentUrl}/server-reference-manifest.json`
).then(res => res.json());
ctx.actionManifest = actionManifest;
Object.assign(ctx, info);
});
describe.each(['node', 'edge'])('runtime: %s', runtime => {
const basePath = runtime === 'edge' ? '/edge' : '';
describe('client component', () => {
it('should bypass the static cache for a server action', async () => {
const path = `${basePath}/client/static`;
const actionId = findActionId(path, runtime);
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
method: 'POST',
body: JSON.stringify([1337]),
headers: {
'Content-Type': 'text/plain;charset=UTF-8',
'Next-Action': actionId,
},
});
expect(res.status).toEqual(200);
const body = await res.text();
expect(body).toContain('1338');
expect(res.headers.get('x-matched-path')).toBe(path + '.action');
if (runtime === 'node') {
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
} else {
// in edge runtime, x-vercel-cache is not returned on MISSes for some reason.
// this checks to ensure it was routed to the edge function instead.
expect(res.headers.get('x-edge-runtime')).toBe('1');
}
});
it('should bypass the static cache for a server action on a page with dynamic params', async () => {
const path = `${basePath}/client/static/[dynamic-static]`;
const actionId = findActionId(path, runtime);
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
method: 'POST',
body: JSON.stringify([1337]),
headers: {
'Content-Type': 'text/plain;charset=UTF-8',
'Next-Action': actionId,
},
});
expect(res.status).toEqual(200);
const body = await res.text();
expect(body).toContain('1338');
expect(res.headers.get('x-matched-path')).toBe(path + '.action');
if (runtime === 'node') {
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
} else {
expect(res.headers.get('x-edge-runtime')).toBe('1');
}
});
it('should bypass the static cache for a multipart request (no action header)', async () => {
const path = `${basePath}/client/static`;
const actionId = findActionId(path, runtime);
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
method: 'POST',
body: `------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"1_$ACTION_ID_${actionId}\"\r\n\r\n\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"0\"\r\n\r\n[\"$K1\"]\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ--\r\n`,
headers: {
'Content-Type':
'multipart/form-data; boundary=----WebKitFormBoundaryHcVuFa30AN0QV3uZ',
},
});
expect(res.status).toEqual(200);
expect(res.headers.get('content-type')).toBe(
'text/html; charset=utf-8'
);
if (runtime === 'node') {
// This is a "BYPASS" because no action ID was provided, so it'll fall back to
// `experimentalBypassFor` handling.
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
} else {
expect(res.headers.get('x-edge-runtime')).toBe('1');
}
expect(res.headers.get('x-matched-path')).toBe(path);
});
it('should properly invoke the action on a dynamic page', async () => {
const path = `${basePath}/client/dynamic/[id]`;
const actionId = findActionId(path, runtime);
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
method: 'POST',
body: JSON.stringify([1337]),
headers: {
'Content-Type': 'text/plain;charset=UTF-8',
'Next-Action': actionId,
},
});
expect(res.status).toEqual(200);
const body = await res.text();
expect(body).toContain('1338');
expect(res.headers.get('x-matched-path')).toBe(path + '.action');
if (runtime === 'node') {
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
} else {
expect(res.headers.get('x-edge-runtime')).toBe('1');
}
});
});
describe('server component', () => {
it('should bypass the static cache for a server action', async () => {
const path = `${basePath}/rsc/static`;
const actionId = findActionId(path, runtime);
const res = await fetch(
`${ctx.deploymentUrl}${path}`,
generateFormDataPayload(actionId)
);
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(path + '.action');
expect(res.headers.get('content-type')).toBe('text/x-component');
if (runtime === 'node') {
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
} else {
expect(res.headers.get('x-edge-runtime')).toBe('1');
}
});
it('should bypass the static cache for a server action on a page with dynamic params', async () => {
const path = `${basePath}/rsc/static/[dynamic-static]`;
const actionId = findActionId(path, runtime);
const res = await fetch(
`${ctx.deploymentUrl}${path}`,
generateFormDataPayload(actionId)
);
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(path + '.action');
expect(res.headers.get('content-type')).toBe('text/x-component');
if (runtime === 'node') {
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
} else {
expect(res.headers.get('x-edge-runtime')).toBe('1');
}
});
it('should properly invoke the action on a dynamic page', async () => {
const path = `${basePath}/rsc/dynamic`;
const actionId = findActionId(path, runtime);
const res = await fetch(
`${ctx.deploymentUrl}${path}`,
generateFormDataPayload(actionId)
);
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(path + '.action');
expect(res.headers.get('content-type')).toBe('text/x-component');
if (runtime === 'node') {
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
} else {
expect(res.headers.get('x-edge-runtime')).toBe('1');
}
});
describe('generateStaticParams', () => {
it('should bypass the static cache for a server action when pre-generated', async () => {
const path = `${basePath}/rsc/static/generate-static-params/pre-generated`;
const dynamicPath = `${basePath}/rsc/static/generate-static-params/[slug]`;
const actionId = findActionId(dynamicPath, runtime);
const res = await fetch(
`${ctx.deploymentUrl}${path}`,
generateFormDataPayload(actionId)
);
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(
dynamicPath + '.action'
);
expect(res.headers.get('content-type')).toBe('text/x-component');
if (runtime === 'node') {
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
} else {
expect(res.headers.get('x-edge-runtime')).toBe('1');
}
});
it('should bypass the static cache for a server action when not pre-generated', async () => {
const page = `${basePath}/rsc/static/generate-static-params/[slug]`;
const actionId = findActionId(page, runtime);
const res = await fetch(
`${ctx.deploymentUrl}/${basePath}/rsc/static/generate-static-params/not-pre-generated`,
generateFormDataPayload(actionId)
);
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(page + '.action');
expect(res.headers.get('content-type')).toBe('text/x-component');
if (runtime === 'node') {
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
} else {
expect(res.headers.get('x-edge-runtime')).toBe('1');
}
});
});
});
});
describe('pages', () => {
it('should not attempt to rewrite the action path for a server action (POST)', async () => {
const res = await fetch(`${ctx.deploymentUrl}/api/test`, {
method: 'POST',
headers: {
'Content-Type':
'multipart/form-data; boundary=----WebKitFormBoundaryHcVuFa30AN0QV3uZ',
},
});
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe('/api/test');
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
const body = await res.json();
expect(body).toEqual({ message: 'Hello from Next.js!' });
});
it('should not attempt to rewrite the action path for a server action (GET)', async () => {
const res = await fetch(`${ctx.deploymentUrl}/api/test`);
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe('/api/test');
const body = await res.json();
expect(body).toEqual({ message: 'Hello from Next.js!' });
});
});
});

View File

@@ -0,0 +1 @@
module.exports = {};

View File

@@ -0,0 +1,9 @@
{
"dependencies": {
"next": "canary"
},
"scripts": {
"build": "next build && cp .next/server/server-reference-manifest.json public/"
},
"ignoreNextjsUpdates": true
}

View File

@@ -0,0 +1,3 @@
export default function handler(req, res) {
res.status(200).json({ message: 'Hello from Next.js!' });
}

View File

@@ -0,0 +1,14 @@
{
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
}
],
"build": {
"env": {
"NEXT_EXPERIMENTAL_STREAMING_ACTIONS": "1"
}
},
"probes": []
}

View File

@@ -7,4 +7,4 @@ export default function Root({ children }) {
<body>{children}</body>
</html>
);
}
}

View File

@@ -0,0 +1,45 @@
'use client';
// @ts-ignore
import { useCallback, useState } from 'react';
function request(method) {
return fetch('/api/test', {
method,
headers: {
'content-type': 'multipart/form-data;.*',
},
});
}
export default function Home() {
const [result, setResult] = useState('Press submit');
const onClick = useCallback(async method => {
const res = await request(method);
const text = await res.text();
setResult(res.ok ? `${method} ${text}` : 'Error: ' + res.status);
}, []);
return (
<>
<div className="flex flex-col items-center justify-center h-screen">
<div className="flex flex-row space-x-2 items-center justify-center">
<button
className="border border-white rounded-sm p-4 mb-4"
onClick={() => onClick('GET')}
>
Submit GET
</button>
<button
className="border border-white rounded-sm p-4 mb-4"
onClick={() => onClick('POST')}
>
Submit POST
</button>
</div>
<div className="text-white">{result}</div>
</div>
</>
);
}

View File

@@ -37,6 +37,7 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
).then(res => res.json());
ctx.actionManifest = actionManifest;
Object.assign(ctx, info);
});
@@ -81,6 +82,25 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
});
it('should bypass the static cache for a multipart request (no action header)', async () => {
const path = '/client/static';
const actionId = findActionId(path);
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
method: 'POST',
body: `------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"1_$ACTION_ID_${actionId}\"\r\n\r\n\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"0\"\r\n\r\n[\"$K1\"]\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ--\r\n`,
headers: {
'Content-Type':
'multipart/form-data; boundary=----WebKitFormBoundaryHcVuFa30AN0QV3uZ',
},
});
expect(res.status).toEqual(200);
expect(res.headers.get('content-type')).toBe('text/html; charset=utf-8');
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
expect(res.headers.get('x-matched-path')).toBe(path);
});
it('should properly invoke the action on a dynamic page', async () => {
const path = '/client/dynamic/[id]';
const actionId = findActionId(path);
@@ -98,6 +118,7 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
const body = await res.text();
expect(body).toContain('1338');
expect(res.headers.get('x-matched-path')).toBe(path);
// This isn't a "BYPASS" because the action wasn't part of a static prerender
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
});
});
@@ -145,6 +166,7 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(path);
expect(res.headers.get('content-type')).toBe('text/x-component');
// This isn't a "BYPASS" because the action wasn't part of a static prerender
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
});
@@ -161,7 +183,9 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
);
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(path);
expect(res.headers.get('x-matched-path')).toBe(
'/rsc/static/generate-static-params/pre-generated'
);
expect(res.headers.get('content-type')).toBe('text/x-component');
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
});
@@ -178,8 +202,36 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe(page);
expect(res.headers.get('content-type')).toBe('text/x-component');
// This isn't a "BYPASS" because the action wasn't part of a static prerender
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
});
});
});
describe('pages', () => {
it('should not attempt to rewrite the action path for a server action (POST)', async () => {
const res = await fetch(`${ctx.deploymentUrl}/api/test`, {
method: 'POST',
headers: {
'Content-Type':
'multipart/form-data; boundary=----WebKitFormBoundaryHcVuFa30AN0QV3uZ',
},
});
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe('/api/test');
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
const body = await res.json();
expect(body).toEqual({ message: 'Hello from Next.js!' });
});
it('should not attempt to rewrite the action path for a server action (GET)', async () => {
const res = await fetch(`${ctx.deploymentUrl}/api/test`);
expect(res.status).toEqual(200);
expect(res.headers.get('x-matched-path')).toBe('/api/test');
const body = await res.json();
expect(body).toEqual({ message: 'Hello from Next.js!' });
});
});
});

View File

@@ -0,0 +1 @@
module.exports = {};

View File

@@ -0,0 +1,3 @@
export default function handler(req, res) {
res.status(200).json({ message: 'Hello from Next.js!' });
}

View File

@@ -18,6 +18,31 @@
}
],
"probes": [
{
"path": "/hello/world",
"status": 200,
"mustContain": "index app page"
},
{
"path": "/hello/world",
"status": 200,
"mustContain": "index app page",
"mustNotContain": "<html",
"headers": {
"RSC": 1
}
},
{
"path": "/hello/world",
"status": 200,
"mustContain": ":",
"mustNotContain": "<html",
"headers": {
"RSC": 1,
"Next-Router-Prefetch": 1
}
},
{
"path": "/hello/world/dashboard/hello",
"status": 200,
@@ -42,14 +67,14 @@
"status": 200,
"mustContain": "hello from /ssg",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/hello/world/ssg",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
@@ -82,14 +107,14 @@
"status": 200,
"mustContain": "hello from app/dashboard/deployments/[id]/settings",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/hello/world/dashboard/deployments/123/settings",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
@@ -102,14 +127,14 @@
"status": 200,
"mustContain": "catchall",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/hello/world/dashboard/deployments/catchall/something",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
@@ -122,7 +147,7 @@
"status": 200,
"mustContain": "hello from app/dashboard",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
@@ -142,7 +167,7 @@
},
"responseHeaders": {
"content-type": "text/x-component",
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true,
"scripts": {

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -7,8 +7,8 @@
},
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -0,0 +1,3 @@
export { default } from '../page';
export const experimental_ppr = false;

View File

@@ -0,0 +1,7 @@
export default function Layout({ children }) {
return (
<html>
<body>{children}</body>
</html>
);
}

View File

@@ -0,0 +1,17 @@
import { unstable_noStore } from 'next/cache';
import { Suspense } from 'react';
export const experimental_ppr = true;
function Dynamic() {
unstable_noStore();
return <div id="sentinel:dynamic">Dynamic</div>;
}
export default function Page() {
return (
<Suspense fallback={<div id="sentinel:loading">Loading...</div>}>
<Dynamic />
</Suspense>
);
}

View File

@@ -0,0 +1,13 @@
/* eslint-env jest */
const path = require('path');
const { deployAndTest } = require('../../utils');
const ctx = {};
// TODO: investigate invariant
describe.skip(`${__dirname.split(path.sep).pop()}`, () => {
it('should deploy and pass probe checks', async () => {
const info = await deployAndTest(__dirname);
Object.assign(ctx, info);
});
});

View File

@@ -0,0 +1,5 @@
module.exports = {
experimental: {
ppr: 'incremental',
},
};

View File

@@ -0,0 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -0,0 +1,77 @@
{
"builds": [
{
"src": "package.json",
"use": "@vercel/next",
"config": {
"functions": {
"app/**/*": {
"maxDuration": 5,
"memory": 512
},
"pages/api/**/*": {
"maxDuration": 5,
"memory": 512
}
}
}
}
],
"probes": [
{
"path": "/",
"status": 200,
"mustContain": "sentinel:loading"
},
{
"path": "/",
"status": 200,
"mustContain": "sentinel:dynamic"
},
{
"path": "/",
"headers": {
"RSC": "1",
"Next-Router-Prefetch": "1"
},
"status": 200,
"mustContain": "sentinel:loading",
"mustNotContain": "sentinel:dynamic"
},
{
"path": "/",
"headers": {
"RSC": "1"
},
"status": 200,
"mustContain": "sentinel:dynamic"
},
{
"path": "/disabled",
"status": 200,
"mustContain": "sentinel:loading"
},
{
"path": "/disabled",
"status": 200,
"mustContain": "sentinel:dynamic"
},
{
"path": "/disabled",
"headers": {
"RSC": "1",
"Next-Router-Prefetch": "1"
},
"status": 200,
"mustContain": "sentinel:dynamic"
},
{
"path": "/disabled",
"headers": {
"RSC": "1"
},
"status": 200,
"mustContain": "sentinel:dynamic"
}
]
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -0,0 +1,5 @@
export default function Page() {
return (
<p>nested app router catch-all</p>
)
}

View File

@@ -1,5 +1,3 @@
export const dynamic = 'force-dynamic'
export default function Page() {
return <p>index app page {Date.now()}</p>;
}

View File

@@ -0,0 +1,7 @@
export default function Page() {
return (
<>
product page
</>
)
}

View File

@@ -13,6 +13,10 @@ module.exports = {
source: '/rewritten-to-index',
destination: '/?fromRewrite=1',
},
{
source: '/to-product/:productId.html',
destination: '/products/:productId',
},
];
},
};

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "13.5.6",
"react": "experimental",
"react-dom": "experimental"
"next": "canary",
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -0,0 +1,24 @@
export default function Page(props) {
return (
<>
<p>hello from /nested/blog-fallback-false/[slug]</p>
</>
);
}
export function getStaticProps() {
return {
props: {
now: Date.now()
}
}
}
export function getStaticPaths() {
return {
paths: [
{ params: { slug: 'first' } },
],
fallback: false
}
}

View File

@@ -18,6 +18,74 @@
}
],
"probes": [
{
"path": "/to-product/product-id.html",
"status": 200,
"mustContain": "<html"
},
{
"path": "/to-product/product-id.html",
"status": 200,
"mustNotContain": "<html",
"mustContain": ":",
"headers": {
"RSC": 1
}
},
{
"path": "/to-product/product-id.html",
"status": 200,
"mustNotContain": ".prefetch",
"mustContain": ":",
"headers": {
"RSC": 1,
"Next-Router-Prefetch": 1
}
},
{
"path": "/nested/blog-fallback-false/first",
"status": 200,
"mustContain": "hello from /nested/blog-fallback-false"
},
{
"path": "/nested/blog-fallback-false/first",
"status": 200,
"mustContain": "{}",
"mustNotContain": ":",
"responseHeaders": {
"x-matched-path": "/nested/blog-fallback-false/first.rsc"
},
"headers": {
"RSC": 1,
"Next-Router-Prefetch": 1
}
},
{
"path": "/nested/blog-fallback-false/first",
"status": 200,
"mustContain": "{}",
"mustNotContain": ":",
"responseHeaders": {
"x-matched-path": "/nested/blog-fallback-false/first.rsc"
},
"headers": {
"RSC": 1
}
},
{
"path": "/nested/blog-fallback-false/non-existent",
"status": 200,
"mustContain": "nested app router catch-all"
},
{
"path": "/nested/blog-fallback-false/non-existent",
"status": 200,
"mustContain": ":",
"headers": {
"RSC": 1,
"Next-Router-Prefetch": 1
}
},
{
"path": "/dynamic-index/hello/index",
"status": 200,
@@ -156,7 +224,7 @@
"status": 200,
"mustContain": "hello from /ssg",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
@@ -185,7 +253,7 @@
"path": "/ssg",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
@@ -197,7 +265,7 @@
"path": "/ssg",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url",
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch",
"content-type": "text/x-component"
},
"headers": {
@@ -232,14 +300,14 @@
"status": 200,
"mustContain": "hello from app/dashboard/deployments/[id]/settings",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard/deployments/123/settings",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
@@ -252,14 +320,14 @@
"status": 200,
"mustContain": "catchall",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
"path": "/dashboard/deployments/catchall/something",
"status": 200,
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
},
"headers": {
"RSC": "1"
@@ -272,7 +340,7 @@
"status": 200,
"mustContain": "hello from app/dashboard",
"responseHeaders": {
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{
@@ -292,7 +360,7 @@
},
"responseHeaders": {
"content-type": "text/x-component",
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch"
}
},
{

View File

@@ -7,8 +7,8 @@
},
"dependencies": {
"next": "canary",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"devDependencies": {
"@types/node": "20.4.5",

View File

@@ -0,0 +1,10 @@
import Link from 'next/link'
export default function Modal() {
return (
<>
<p>cart modal!!</p>
<Link href='/'>to index</Link>
</>
)
}

View File

@@ -0,0 +1,3 @@
export default function Modal() {
return null
}

View File

@@ -0,0 +1,10 @@
import Link from 'next/link'
export default function Cart() {
return (
<>
<p>normal cart page</p>
<Link href='/'>to index</Link>
</>
)
}

View File

@@ -0,0 +1,8 @@
export default function Layout({ modal, children }) {
return (
<>
{modal}
{children}
</>
)
}

View File

@@ -1,10 +1,14 @@
import Link from 'next/link'
import React, { Suspense } from 'react'
import { Dynamic } from '../components/dynamic'
export default () => {
return (
<Suspense fallback={<Dynamic pathname="/" fallback />}>
<Dynamic pathname="/" />
</Suspense>
<>
<Suspense fallback={<Dynamic pathname="/" fallback />}>
<Dynamic pathname="/" />
</Suspense>
<Link href='/cart'>to /cart</Link>
</>
)
}

View File

@@ -19,14 +19,6 @@ const links = [
{ href: '/no-suspense/nested/c', tag: 'no suspense, on-demand' },
{ href: '/dynamic/force-dynamic', tag: "dynamic = 'force-dynamic'" },
{ href: '/dynamic/force-static', tag: "dynamic = 'force-static'" },
{ href: '/edge/suspense', tag: 'edge, pre-generated' },
{ href: '/edge/suspense/a', tag: 'edge, pre-generated' },
{ href: '/edge/suspense/b', tag: 'edge, on-demand' },
{ href: '/edge/suspense/c', tag: 'edge, on-demand' },
{ href: '/edge/no-suspense', tag: 'edge, no suspense, pre-generated' },
{ href: '/edge/no-suspense/a', tag: 'edge, no suspense, pre-generated' },
{ href: '/edge/no-suspense/b', tag: 'edge, no suspense, on-demand' },
{ href: '/edge/no-suspense/c', tag: 'edge, no suspense, on-demand' },
];
export const Links = () => {

View File

@@ -44,6 +44,45 @@ describe(`${__dirname.split(path.sep).pop()}`, () => {
Object.assign(ctx, info);
});
it('should handle interception route properly', async () => {
const res = await fetch(`${ctx.deploymentUrl}/cart`);
expect(res.status).toBe(200);
expect(await res.text()).toContain('normal cart page');
const res2 = await fetch(`${ctx.deploymentUrl}/cart`, {
headers: {
RSC: '1',
},
});
const res2Body = await res2.text();
expect(res2.status).toBe(200);
expect(res2Body).toContain(':');
expect(res2Body).not.toContain('<html');
const res3 = await fetch(`${ctx.deploymentUrl}/cart`, {
headers: {
RSC: '1',
'Next-Url': '/cart',
'Next-Router-Prefetch': '1',
},
});
const res3Body = await res3.text();
expect(res3.status).toBe(200);
expect(res3Body).toContain(':');
expect(res3Body).not.toContain('<html');
const res4 = await fetch(`${ctx.deploymentUrl}/cart`, {
headers: {
RSC: '1',
'Next-Url': '/cart',
},
});
const res4Body = await res4.text();
expect(res4.status).toBe(200);
expect(res4Body).toContain(':');
expect(res4Body).not.toContain('<html');
});
describe('dynamic pages should resume', () => {
it.each(pages.filter(p => p.dynamic === true))(
'should resume $pathname',

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "18.2.0",
"react-dom": "18.2.0"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -10,8 +10,8 @@
},
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -5,8 +5,8 @@
},
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -6,8 +6,8 @@
},
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -9,8 +9,8 @@
},
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
"react": "19.0.0-beta-4508873393-20240430",
"react-dom": "19.0.0-beta-4508873393-20240430"
},
"ignoreNextjsUpdates": true
}

Some files were not shown because too many files have changed in this diff Show More