Compare commits

...

49 Commits

Author SHA1 Message Date
Andy McKay
a567d047d1 Publish Stable
- @vercel/build-utils@5.5.7
 - vercel@28.4.14
 - @vercel/client@12.2.15
 - @vercel/edge@0.1.1
 - @vercel/error-utils@1.0.2
 - @vercel/frameworks@1.1.10
 - @vercel/fs-detectors@3.4.7
 - @vercel/go@2.2.15
 - @vercel/hydrogen@0.0.28
 - @vercel/next@3.2.8
 - @vercel/node-bridge@3.1.1
 - @vercel/node@2.6.1
 - @vercel/python@3.1.24
 - @vercel/redwood@1.0.33
 - @vercel/remix@1.0.34
 - @vercel/routing-utils@2.1.1
 - @vercel/ruby@1.3.41
 - @vercel/static-build@1.0.34
 - @vercel/static-config@2.0.5
2022-11-02 13:14:37 -07:00
Steven
fdfb3a385e [error-utils] Allow publishing to npm (#8839)
> If you set "private": true in your package.json, then npm will refuse
to publish it.


https://docs.npmjs.com/cli/v8/configuring-npm/package-json#private
2022-11-02 16:12:09 -04:00
Andy McKay
a630e19896 Publish Stable
- @vercel/build-utils@5.5.6
 - vercel@28.4.13
 - @vercel/client@12.2.14
 - @vercel/edge@0.1.0
 - @vercel/error-utils@1.0.1
 - @vercel/frameworks@1.1.9
 - @vercel/fs-detectors@3.4.6
 - @vercel/go@2.2.14
 - @vercel/hydrogen@0.0.27
 - @vercel/next@3.2.7
 - @vercel/node-bridge@3.1.0
 - @vercel/node@2.6.0
 - @vercel/python@3.1.23
 - @vercel/redwood@1.0.32
 - @vercel/remix@1.0.33
 - @vercel/routing-utils@2.1.0
 - @vercel/ruby@1.3.40
 - @vercel/static-build@1.0.33
 - @vercel/static-config@2.0.4
2022-11-02 12:59:38 -07:00
Steven
00430eeabf [tests] Update turbo cache key (#8830)
This ensures we don't share cache results between node major versions
like 14.x vs 16.x (as well as OS and Arch)
2022-11-02 15:43:27 -04:00
Ethan Arrowood
49f453742b [error-utils] Create @vercel/error-utils package (#8828)
While working on #8742 i found some useful error checking code buried in
the CLI. This PR adds a new private package `@vercel/errors` that moves
those helpful utilities into its own package so it can be used
throughout the monorepo.
2022-11-02 15:18:00 -04:00
Chris Barber
bcb8d4f812 [next] Prepend basePath to edge function destination (#8835)
For Next.js apps, a custom `basePath` in the `next.config.js` file was being prepended to all paths except edge functions of which this PR resolves.

### Related Issues

> Fixes [#853](https://github.com/vercel/customer-issues/issues/853)

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-02 15:32:00 +00:00
Steven
d42a8a6588 [build-utils] Experimental flag to allow nodejs18.x (#8836)
Add a new experimental flag to allow `nodejs18.x`
2022-11-02 10:56:14 -04:00
Nathan Rajlich
301bcf58fb [node-bridge] Support streaming response for Serverless Function (#8795)
Adds streaming response support for React Server Components with Next 13.
2022-11-02 02:16:37 +00:00
Steven
11d0091393 [cli] Fix middleware 500 in vc dev (#8833)
The status code no longer prints the generic error page.
2022-11-01 18:43:18 -07:00
Steven
6405fb51a1 [tests] Fix next & react version for dev test (#8832)
https://nextjs.org/docs/upgrading#upgrading-from-12-to-13
2022-11-01 18:42:35 -04:00
github-actions[bot]
edd477e602 [examples] Upgrade Next.js to version 13.0.1 (#8820)
This auto-generated PR updates Next.js to version 13.0.1

Co-authored-by: Vercel Team Bot <team@zeit.co>
2022-11-01 11:20:16 -04:00
Tobias Lins
4f8f8a5b98 [routing-utils] Allow to appendRoutes add null phase (#8814)
For Analytics V2 we need to append routes to the `null` phase instead of
`filesystem`.
2022-11-01 11:17:50 -04:00
chloetedder
a8e66eef41 Remove output directory placeholder (#8817)
### Related Issues

Related - https://github.com/vercel/api/pull/15027
Removing setting the output directory placeholder - it is not extremely reliable https://vercel.slack.com/archives/C02HEJASXGD/p1667234137439189?thread_ts=1667232443.320769&cid=C02HEJASXGD

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-01 03:23:38 +00:00
Kiko Beats
23dd29e269 [node] upgrade edge-runtime to v2.0.0 (#8811)
Drop node12 support
2022-10-31 10:52:40 -04:00
Seiya Nuta
4eb4d2b355 [dev] Support request headers override in middleware (#8752)
Implements request headers override in middlewares.

#### New middleware headers

- `x-middleware-override-headers`: A comma separated list of *all* request header names. Headers not listed will be deleted.
- `x-middleware-request-<name>`: A new value for the header `<name>`.

### Related Issues

- #8724: Add helper functions for non-Next.js middlewares
- https://github.com/vercel/next.js/pull/41380: Next.js' implementation

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-27 06:51:27 +00:00
Tim Paine
3590ea06a4 [docs][typo] Update license link in readme (#8779)
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2022-10-26 10:00:31 -05:00
github-actions[bot]
314a97b318 [examples] Upgrade Next.js to version 13.0.0 (#8778)
This auto-generated PR updates Next.js to version 13.0.0
2022-10-26 01:42:21 -04:00
JJ Kasper
d41d9e7374 [next] Ensure static app paths have vary correctly (#8771)
### Related Issues

x-ref: https://github.com/vercel/vercel/pull/8763
x-ref: [slack
thread](https://vercel.slack.com/archives/C035J346QQL/p1666645450798089)

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-24 14:49:41 -07:00
Kiko Beats
80b211fb4a [node] upgrade edge-runtime to v1.1.0 (#8765) 2022-10-24 22:12:31 +02:00
JJ Kasper
ffaf5c9143 [next] Add leveraging initialHeaders/status for prerender (#8763)
### Related Issues

x-ref: https://github.com/vercel/vercel/pull/8757
Fixes: [slack thread](https://vercel.slack.com/archives/C035J346QQL/p1666278547705429)

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-23 16:12:03 +00:00
Andy McKay
ba1c2a7e54 Cope with socket hang up (#8760)
### Related Issues

Copes with `socket hang up` errors.

I tested with https://github.com/Shopify/toxiproxy and set up a proxy that generated a socket hangup error. With a test URL etc it looks like this:

<img width="935" alt="Screen Shot 2022-10-21 at 2 13 48 PM" src="https://user-images.githubusercontent.com/74699/197289788-9467ebef-d4dd-4fae-bf41-f635b7857d03.png">

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2022-10-21 22:26:41 +00:00
Baruch-Adi Hen
30a9183836 [ENV] Use new env/pull endpoint for vc env pull, vc pull (#8751)
### Related Issues

We introduced a new endpoint: `/env/pull/:projectId/:target?/:gitBranch?` which will return a complete list of key/value pairs (environment variables) for the proper target environment, using the same logic as we use for deployments. 

This means that we don't need to fetch env variables from multiple sources (system, project, shared) and stitch them on the client anymore. 

- removes unused logic for fetching & merging environment variables on the CLI client
- offloads env variables generation to the API
  - new: shared env variables are now supported in vc env pull! 


### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2022-10-21 20:18:15 +00:00
Seiya Nuta
df9accfd6c [@vercel/edge] Support overriding request headers (#8724)
<!--
Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change that you're making:
-->

This PR adds a feature in middleware to add, modify, or delete request
headers. This feature is quite useful to pass data from middleware to
Serverless/Edge API routes.

### New APIs

Adds a new option `request.headers` to the `MiddlewareResponseInit`
parameter in `NextResponse.next()` and `NextResponse.rewrite()`. It's a
[`Header`](https://developer.mozilla.org/en-US/docs/Web/API/Headers)
object holding *all* request headers. Specifically:

```ts
interface ExtraResponseInit extends ResponseInit {
  request?: {
    headers?: Headers
  }
}
```

### Example

```ts
// api/hello.ts
export default (req, res) => {
  const valueFromMiddleware = req.headers['x-hello-from-middleware']
  return res.send(valueFromMiddleware)
}

// middleware.ts
import { next } from '@vercel/edge'

export default function middleware(request: NextRequest) {
  // Clone request headers
  const headers = new Headers(request.headers);
  // Add a new request header
  headers.set('x-hello-from-middleware', 'foo');
  // Delete a request header from the client
  headers.delete('x-from-client');

  return next({
    request: {
      headers
    }
  });
}
```

### New middleware headers

- `x-middleware-override-headers`: A comma separated list of *all*
request header names. Headers not listed will be deleted.
- `x-middleware-request-<name>`: A new value for the header `<name>`.

### Related Issues

- Next.js' implementation: https://github.com/vercel/next.js/pull/41380

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR

Co-authored-by: Gal Schlezinger <gal@spitfire.co.il>
2022-10-21 11:27:37 +09:00
JJ Kasper
b388357c0b [build-utils] Fix check for initialHeaders on prerender (#8757)
### Related Issues

x-ref: https://github.com/vercel/vercel/pull/8743

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-20 15:48:01 -07:00
Seiya Nuta
90291525c2 [edge] Fix the region field of geolocation (#8517) 2022-10-20 17:59:20 +09:00
Gal Schlezinger
812dd43b6a [build-utils] allow EdgeFunction constructor to receive any string as a region (#8747)
### Related Issues

This will allow a shorthand form of `export const config = { regions:
'iad1' }`

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR

Co-authored-by: Damien Simonin Feugas <damien@vercel.com>
2022-10-19 14:27:30 -05:00
Gal Schlezinger
9e97e0fd58 [node] allow to extract Edge Function regions from source code (#8733)
Co-authored-by: Steven <steven@ceriously.com>
2022-10-19 18:38:07 +03:00
Chris Barber
74528c2160 [cli] Use ensureLink() in link and pull commands (#8670)
The `link` and `pull` commands have essentially duplicate code from `ensureLink()`. This

Card: https://linear.app/vercel/issue/VCCLI-224/adopt-ensurelink-across-commands

The `dev` command should also be updated to use `ensureLink()`, however that may interfere with this in progress PR: https://github.com/vercel/vercel/pull/8666.

### 📋 Checklist

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2022-10-18 23:32:58 +00:00
JJ Kasper
82fd2b8068 [build-utils] Allow initialHeaders and initialStatus for Prerender (#8743)
### Related Issues

This allows `initialHeaders` and `initialStatus` for Prender as has been discussed quite a bit so that the proper headers can be applied when serving the fallback.

x-ref: [slack thread](https://vercel.slack.com/archives/C035J346QQL/p1666130102396799?thread_ts=1666122861.189349&cid=C035J346QQL)
x-ref: follow-up to https://github.com/vercel/vercel/pull/8737
x-ref: dd94dcab32/packages/next/src/server-build.ts (L970)

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-18 22:50:25 +00:00
JJ Kasper
dd94dcab32 Publish Stable
- vercel@28.4.12
 - @vercel/edge@0.0.5
 - @vercel/next@3.2.6
 - @vercel/node@2.5.26
2022-10-18 09:50:29 -07:00
JJ Kasper
300e6c6ebb [next] Ensure vary header is set for static app paths (#8737)
### Related Issues

x-ref: [slack thread](https://vercel.slack.com/archives/C035J346QQL/p1666025811694049?thread_ts=1666008214.416389&cid=C035J346QQL)
x-ref: https://github.com/vercel/next.js/pull/41479

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-18 14:47:56 +00:00
Kiko Beats
cfe6550ac8 [node] upgrade edge-runtime to v1.1.0-beta.40 (#8740) 2022-10-18 12:28:14 +00:00
Andy McKay
dfe009ffe2 [tests] Update workflow to examine both labels and use annotations (#8736)
### Related Issues

I couldn't find an issue related to this, but just noticed it when I
submitted a request. I got a build failing because I failed to add in an
`area` label, so added that label and it failed again this time on
`semver`. This change makes it so the workflow looks at both labels,
rather than just one and exiting early. This might save some people
doing 2 builds, like I did 😄

Also changed it to use Actions annotations, which does colourization for
you. Docs on that are here:
https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-a-debug-message

But the output is now something like this ([test
repo](https://github.com/andymckay/test-config)). Missing both labels:

<img width="724" alt="Screen Shot 2022-10-17 at 4 01 35 PM"
src="https://user-images.githubusercontent.com/74699/196299860-9f8ec5e7-fe40-4fe4-8e36-486bf00b2d13.png">

And in the log:

<img width="805" alt="Screen Shot 2022-10-17 at 4 01 41 PM"
src="https://user-images.githubusercontent.com/74699/196299903-149a56e0-2538-419d-8e9f-8e459c7fb42a.png">

And if you've got the labels:

<img width="576" alt="Screen Shot 2022-10-17 at 4 02 59 PM"
src="https://user-images.githubusercontent.com/74699/196299931-88f7f623-f08f-4189-83ec-cd75c466e6a5.png">
2022-10-17 19:16:53 -04:00
Steven
40f38948a0 Publish Stable
- @vercel/build-utils@5.5.5
 - vercel@28.4.11
 - @vercel/client@12.2.13
 - @vercel/frameworks@1.1.8
 - @vercel/fs-detectors@3.4.5
 - @vercel/go@2.2.13
 - @vercel/hydrogen@0.0.26
 - @vercel/next@3.2.5
 - @vercel/node@2.5.25
 - @vercel/python@3.1.22
 - @vercel/redwood@1.0.31
 - @vercel/remix@1.0.32
 - @vercel/ruby@1.3.39
 - @vercel/static-build@1.0.32
2022-10-16 14:11:02 -04:00
Sean Massa
87eba56063 [node] update typescript detection message (#8727)
When deploying a project that uses typescript, but typescript is not a dependency, the default typescript is used. The message that's logged when this happens says:

> Using TypeScript 4.3.4 (no local tsconfig.json)

which is not necessarily true. You can have a `tsconfig.json` file with no typescript dependency.

This message leads to bad debugging paths.

This PR updates the message to be more specific. Now the message will say:

> Using TypeScript 4.3.4 (no local "typescript" package detected)
2022-10-14 21:31:13 +00:00
JJ Kasper
d0a5676c26 [next] Fix index rsc route handling (#8729)
### Related Issues

This ensures we probably route the `/` rsc route properly and adds a regression test along with enabling some now patched tests that were skipped. 

Fixes: [slack thread](https://vercel.slack.com/archives/C043ANYDB24/p1665746921485109)

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-14 21:18:32 +00:00
Siarhei
da9fa997ed [tests] Update the checkout action to V3 in publish.yml (#8721) 2022-10-11 19:57:55 -04:00
Peter van der Zee
3d79a9d4d4 [build-utils] Cleanup getNodeVersion (#8720)
- `meta.isDev` is unconditionally read after the first check (even though it could technically still be `null`, ignoring typescript)
- `meta.isDev` is explicitly asserted to be falsy, since the first `if` will return early if it's truthy, so the later checks are all redundant
- added `?.` for the `packageJson?.engines?.node` read

The rest is auto formatting (and if my local eslint warnings are correct then I'm just gonna close this PR...)
2022-10-11 23:47:52 +00:00
Sean Massa
ae13c5ee92 Publish Stable
- vercel@28.4.10
 - @vercel/frameworks@1.1.7
 - @vercel/fs-detectors@3.4.4
 - @vercel/node@2.5.24
 - @vercel/static-build@1.0.31
2022-10-11 12:43:42 -05:00
Jacob Ford
ad654139df [frameworks] Detect Hydrogen TypeScript config (#8711)
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2022-10-11 12:04:53 -05:00
Jacob Ford
74f8414e12 [frameworks] Detect additional valid Sanity v3 configs (#8710)
Co-authored-by: Steven <steven@ceriously.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2022-10-11 11:02:27 -05:00
Kiko Beats
65c2860e14 [node] upgrade edge-runtime to v1.1.0-beta.38 (#8715)
It fixes an implementation detail issue users are facing when you try to redirect
2022-10-11 13:02:34 +00:00
JJ Kasper
3b5b397b35 Publish Stable
- vercel@28.4.9
 - @vercel/fs-detectors@3.4.3
 - @vercel/next@3.2.4
 - @vercel/node@2.5.23
2022-10-10 17:52:11 -07:00
JJ Kasper
051e061176 [next] Fix optional catch-all data route with middleware (#8713)
### Related Issues

This ensures we properly normalize the optional catch-all data route correctly with middleware as currently the route is expecting a repeated slash in the regex `(?:/(?` which it should just be `(?:(?`. This also adds a regression test for this specific case. 

Fixes: [slack thread](https://vercel.slack.com/archives/C045FKE5P51/p1665153317613089)

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-10 23:50:20 +00:00
chloetedder
30d46321cc [fs-detectors] Add dark/light turborepo logo (#8694)
### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-07 12:22:41 -05:00
Christian Rackerseder
c2563535ea [node] Allow VercelApiHandler to also return a Promise (#8690)
### Related Issues

Otherwise you are not able to write a serverless function that returns a
Promise when using the ESLint rule
[@typescript-eslint/no-misused-promises](https://typescript-eslint.io/rules/no-misused-promises/)

Following function

```typescript
const handler: VercelApiHandler = async () => {};
```

will report a `Promise-returning function provided to variable where a
void return was expected`

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with
tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [x] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-07 12:59:12 -04:00
Steven
5f2bed4f24 [tests] Enable corepack for monorepo (#8691)
This will ensure yarn 1 is used by contributors
2022-10-06 18:07:22 +00:00
chloetedder
cfb7946f4b Publish Stable
- vercel@28.4.8
 - @vercel/fs-detectors@3.4.2
2022-10-06 09:51:35 -05:00
chloetedder
05c5b3a80d [fs-detectors] Add setting placeholders for monorepos (#8688)
### Related Issues

Add in placeholder settings for monorepos

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-05 21:34:00 +00:00
265 changed files with 5507 additions and 2715 deletions

View File

@@ -13,6 +13,7 @@ To get started, execute the following:
```
git clone https://github.com/vercel/vercel
cd vercel
corepack enable
yarn install
yarn bootstrap
yarn build

View File

@@ -18,7 +18,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Check Release
id: check-release
run: |

View File

@@ -10,13 +10,17 @@ jobs:
uses: actions/github-script@v6
with:
script: |
let missing = false;
const labels = context.payload.pull_request.labels.map(l => l.name);
if (labels.filter(l => l.startsWith('area:')).length === 0) {
console.error('\u001b[31mMissing label: Please add at least one "area" label.');
process.exit(1);
console.error('::error::Missing label: Please add at least one "area" label.');
missing = true;
}
if (labels.filter(l => l.startsWith('semver:')).length !== 1) {
console.error('\u001b[31mMissing label: Please add exactly one "semver" label.');
console.error('::error::Missing label: Please add exactly one "semver" label.');
missing = true;
}
if (missing) {
process.exit(1);
}
console.log('\u001b[32mSuccess: This pull request has correct labels, thanks!');
console.log('::notice::Success: This pull request has correct labels, thanks!');

View File

@@ -82,11 +82,11 @@ jobs:
- run: yarn install --network-timeout 1000000
- name: Build ${{matrix.packageName}} and all its dependencies
run: node_modules/.bin/turbo run build --cache-dir=".turbo" --scope=${{matrix.packageName}} --include-dependencies --no-deps
run: node utils/gen.js && node_modules/.bin/turbo run build --cache-dir=".turbo" --scope=${{matrix.packageName}} --include-dependencies --no-deps
env:
FORCE_COLOR: '1'
- name: Test ${{matrix.packageName}}
run: node_modules/.bin/turbo run test --cache-dir=".turbo" --scope=${{matrix.packageName}} --no-deps -- ${{ join(matrix.testPaths, ' ') }}
run: node utils/gen.js && node_modules/.bin/turbo run test --cache-dir=".turbo" --scope=${{matrix.packageName}} --no-deps -- ${{ join(matrix.testPaths, ' ') }}
shell: bash
env:
VERCEL_CLI_VERSION: ${{ needs.setup.outputs.dplUrl }}/tarballs/vercel.tgz

1
.gitignore vendored
View File

@@ -28,3 +28,4 @@ test/lib/deployment/failed-page.txt
__pycache__
.vercel
.turbo
turbo-cache-key.json

View File

@@ -35,4 +35,4 @@ For details on how to use Vercel, check out our [documentation](https://vercel.c
- [Code of Conduct](./.github/CODE_OF_CONDUCT.md)
- [Contributing Guidelines](./.github/CONTRIBUTING.md)
- [MIT License](./LICENSE)
- [Apache 2.0 License](./LICENSE)

View File

@@ -18,6 +18,12 @@ async function main() {
{ recursive: true, force: true }
);
await fs.cp(
join(repoRoot, 'packages', 'fs-detectors', 'logos'),
join(pubDir, 'monorepo-logos'),
{ recursive: true, force: true }
);
const examples = await getExampleList();
const pathListAll = join(pubDir, 'list-all.json');
await fs.writeFile(pathListAll, JSON.stringify(examples));

View File

@@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}

View File

@@ -1,7 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
}
module.exports = nextConfig

637
examples/nextjs/package-lock.json generated Normal file
View File

@@ -0,0 +1,637 @@
{
"name": "nextjs",
"version": "0.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "nextjs",
"version": "0.1.0",
"dependencies": {
"next": "13.0.1",
"react": "18.2.0",
"react-dom": "18.2.0"
}
},
"node_modules/@next/env": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.1.tgz",
"integrity": "sha512-gK60YoFae3s8qi5UgIzbvxOhsh5gKyEaiKH5+kLBUYXLlrPyWJR2xKBj2WqvHkO7wDX7/Hed3DAqjSpU4ijIvQ=="
},
"node_modules/@next/swc-android-arm-eabi": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.1.tgz",
"integrity": "sha512-M28QSbohZlNXNn//HY6lV2T3YaMzG58Jwr0YwOdVmOQv6i+7lu6xe3GqQu4kdqInqhLrBXnL+nabFuGTVSHtTg==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-android-arm64": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.1.tgz",
"integrity": "sha512-szmO/i6GoHcPXcbhUKhwBMETWHNXH3ITz9wfxwOOFBNKdDU8pjKsHL88lg28aOiQYZSU1sxu1v1p9KY5kJIZCg==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-darwin-arm64": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.1.tgz",
"integrity": "sha512-O1RxCaiDNOjGZmdAp6SQoHUITt9aVDQXoR3lZ/TloI/NKRAyAV4u0KUUofK+KaZeHOmVTnPUaQuCyZSc3i1x5Q==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.1.tgz",
"integrity": "sha512-8E6BY/VO+QqQkthhoWgB8mJMw1NcN9Vhl2OwEwxv8jy2r3zjeU+WNRxz4y8RLbcY0R1h+vHlXuP0mLnuac84tQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-freebsd-x64": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.1.tgz",
"integrity": "sha512-ocwoOxm2KVwF50RyoAT+2RQPLlkyoF7sAqzMUVgj+S6+DTkY3iwH+Zpo0XAk2pnqT9qguOrKnEpq9EIx//+K7Q==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm-gnueabihf": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.1.tgz",
"integrity": "sha512-yO7e3zITfGol/N6lPQnmIRi0WyuILBMXrvH6EdmWzzqMDJFfTCII6l+B6gMO5WVDCTQUGQlQRNZ7sFqWR4I71g==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.1.tgz",
"integrity": "sha512-OEs6WDPDI8RyM8SjOqTDMqMBfOlU97VnW6ZMXUvzUTyH0K9c7NF+cn7UMu+I4tKFN0uJ9WQs/6TYaFBGkgoVVA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.1.tgz",
"integrity": "sha512-y5ypFK0Y3urZSFoQxbtDqvKsBx026sz+Fm+xHlPWlGHNZrbs3Q812iONjcZTo09QwRMk5X86iMWBRxV18xMhaw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.1.tgz",
"integrity": "sha512-XDIHEE6SU8VCF+dUVntD6PDv6RK31N0forx9kucZBYirbe8vCZ+Yx8hYgvtIaGrTcWtGxibxmND0pIuHDq8H5g==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.1.tgz",
"integrity": "sha512-yxIOuuz5EOx0F1FDtsyzaLgnDym0Ysxv8CWeJyDTKKmt9BVyITg6q/cD+RP9bEkT1TQi+PYXIMATSz675Q82xw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.1.tgz",
"integrity": "sha512-+ucLe2qgQzP+FM94jD4ns6LDGyMFaX9k3lVHqu/tsQCy2giMymbport4y4p77mYcXEMlDaHMzlHgOQyHRniWFA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.1.tgz",
"integrity": "sha512-Krr/qGN7OB35oZuvMAZKoXDt2IapynIWLh5A5rz6AODb7f/ZJqyAuZSK12vOa2zKdobS36Qm4IlxxBqn9c00MA==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.1.tgz",
"integrity": "sha512-t/0G33t/6VGWZUGCOT7rG42qqvf/x+MrFp1CU+8CN6PrjSSL57R5bqkXfubV9t4eCEnUxVP+5Hn3MoEXEebtEw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@swc/helpers": {
"version": "0.4.11",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.11.tgz",
"integrity": "sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==",
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001427",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001427.tgz",
"integrity": "sha512-lfXQ73oB9c8DP5Suxaszm+Ta2sr/4tf8+381GkIm1MLj/YdLf+rEDyDSRCzeltuyTVGm+/s18gdZ0q+Wmp8VsQ==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
}
]
},
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/next": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/next/-/next-13.0.1.tgz",
"integrity": "sha512-ErCNBPIeZMKFn6hX+ZBSlqZVgJIeitEqhGTuQUNmYXJ07/A71DZ7AJI8eyHYUdBb686LUpV1/oBdTq9RpzRVPg==",
"dependencies": {
"@next/env": "13.0.1",
"@swc/helpers": "0.4.11",
"caniuse-lite": "^1.0.30001406",
"postcss": "8.4.14",
"styled-jsx": "5.1.0",
"use-sync-external-store": "1.2.0"
},
"bin": {
"next": "dist/bin/next"
},
"engines": {
"node": ">=14.6.0"
},
"optionalDependencies": {
"@next/swc-android-arm-eabi": "13.0.1",
"@next/swc-android-arm64": "13.0.1",
"@next/swc-darwin-arm64": "13.0.1",
"@next/swc-darwin-x64": "13.0.1",
"@next/swc-freebsd-x64": "13.0.1",
"@next/swc-linux-arm-gnueabihf": "13.0.1",
"@next/swc-linux-arm64-gnu": "13.0.1",
"@next/swc-linux-arm64-musl": "13.0.1",
"@next/swc-linux-x64-gnu": "13.0.1",
"@next/swc-linux-x64-musl": "13.0.1",
"@next/swc-win32-arm64-msvc": "13.0.1",
"@next/swc-win32-ia32-msvc": "13.0.1",
"@next/swc-win32-x64-msvc": "13.0.1"
},
"peerDependencies": {
"fibers": ">= 3.1.0",
"node-sass": "^6.0.0 || ^7.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.3.0"
},
"peerDependenciesMeta": {
"fibers": {
"optional": true
},
"node-sass": {
"optional": true
},
"sass": {
"optional": true
}
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"node_modules/postcss": {
"version": "8.4.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
}
],
"dependencies": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"dependencies": {
"loose-envify": "^1.1.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.0"
},
"peerDependencies": {
"react": "^18.2.0"
}
},
"node_modules/scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
"dependencies": {
"loose-envify": "^1.1.0"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/styled-jsx": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.0.tgz",
"integrity": "sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==",
"dependencies": {
"client-only": "0.0.1"
},
"engines": {
"node": ">= 12.0.0"
},
"peerDependencies": {
"react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
},
"peerDependenciesMeta": {
"@babel/core": {
"optional": true
},
"babel-plugin-macros": {
"optional": true
}
}
},
"node_modules/tslib": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
},
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
}
},
"dependencies": {
"@next/env": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.1.tgz",
"integrity": "sha512-gK60YoFae3s8qi5UgIzbvxOhsh5gKyEaiKH5+kLBUYXLlrPyWJR2xKBj2WqvHkO7wDX7/Hed3DAqjSpU4ijIvQ=="
},
"@next/swc-android-arm-eabi": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.1.tgz",
"integrity": "sha512-M28QSbohZlNXNn//HY6lV2T3YaMzG58Jwr0YwOdVmOQv6i+7lu6xe3GqQu4kdqInqhLrBXnL+nabFuGTVSHtTg==",
"optional": true
},
"@next/swc-android-arm64": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.1.tgz",
"integrity": "sha512-szmO/i6GoHcPXcbhUKhwBMETWHNXH3ITz9wfxwOOFBNKdDU8pjKsHL88lg28aOiQYZSU1sxu1v1p9KY5kJIZCg==",
"optional": true
},
"@next/swc-darwin-arm64": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.1.tgz",
"integrity": "sha512-O1RxCaiDNOjGZmdAp6SQoHUITt9aVDQXoR3lZ/TloI/NKRAyAV4u0KUUofK+KaZeHOmVTnPUaQuCyZSc3i1x5Q==",
"optional": true
},
"@next/swc-darwin-x64": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.1.tgz",
"integrity": "sha512-8E6BY/VO+QqQkthhoWgB8mJMw1NcN9Vhl2OwEwxv8jy2r3zjeU+WNRxz4y8RLbcY0R1h+vHlXuP0mLnuac84tQ==",
"optional": true
},
"@next/swc-freebsd-x64": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.1.tgz",
"integrity": "sha512-ocwoOxm2KVwF50RyoAT+2RQPLlkyoF7sAqzMUVgj+S6+DTkY3iwH+Zpo0XAk2pnqT9qguOrKnEpq9EIx//+K7Q==",
"optional": true
},
"@next/swc-linux-arm-gnueabihf": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.1.tgz",
"integrity": "sha512-yO7e3zITfGol/N6lPQnmIRi0WyuILBMXrvH6EdmWzzqMDJFfTCII6l+B6gMO5WVDCTQUGQlQRNZ7sFqWR4I71g==",
"optional": true
},
"@next/swc-linux-arm64-gnu": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.1.tgz",
"integrity": "sha512-OEs6WDPDI8RyM8SjOqTDMqMBfOlU97VnW6ZMXUvzUTyH0K9c7NF+cn7UMu+I4tKFN0uJ9WQs/6TYaFBGkgoVVA==",
"optional": true
},
"@next/swc-linux-arm64-musl": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.1.tgz",
"integrity": "sha512-y5ypFK0Y3urZSFoQxbtDqvKsBx026sz+Fm+xHlPWlGHNZrbs3Q812iONjcZTo09QwRMk5X86iMWBRxV18xMhaw==",
"optional": true
},
"@next/swc-linux-x64-gnu": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.1.tgz",
"integrity": "sha512-XDIHEE6SU8VCF+dUVntD6PDv6RK31N0forx9kucZBYirbe8vCZ+Yx8hYgvtIaGrTcWtGxibxmND0pIuHDq8H5g==",
"optional": true
},
"@next/swc-linux-x64-musl": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.1.tgz",
"integrity": "sha512-yxIOuuz5EOx0F1FDtsyzaLgnDym0Ysxv8CWeJyDTKKmt9BVyITg6q/cD+RP9bEkT1TQi+PYXIMATSz675Q82xw==",
"optional": true
},
"@next/swc-win32-arm64-msvc": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.1.tgz",
"integrity": "sha512-+ucLe2qgQzP+FM94jD4ns6LDGyMFaX9k3lVHqu/tsQCy2giMymbport4y4p77mYcXEMlDaHMzlHgOQyHRniWFA==",
"optional": true
},
"@next/swc-win32-ia32-msvc": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.1.tgz",
"integrity": "sha512-Krr/qGN7OB35oZuvMAZKoXDt2IapynIWLh5A5rz6AODb7f/ZJqyAuZSK12vOa2zKdobS36Qm4IlxxBqn9c00MA==",
"optional": true
},
"@next/swc-win32-x64-msvc": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.1.tgz",
"integrity": "sha512-t/0G33t/6VGWZUGCOT7rG42qqvf/x+MrFp1CU+8CN6PrjSSL57R5bqkXfubV9t4eCEnUxVP+5Hn3MoEXEebtEw==",
"optional": true
},
"@swc/helpers": {
"version": "0.4.11",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.11.tgz",
"integrity": "sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==",
"requires": {
"tslib": "^2.4.0"
}
},
"caniuse-lite": {
"version": "1.0.30001427",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001427.tgz",
"integrity": "sha512-lfXQ73oB9c8DP5Suxaszm+Ta2sr/4tf8+381GkIm1MLj/YdLf+rEDyDSRCzeltuyTVGm+/s18gdZ0q+Wmp8VsQ=="
},
"client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
},
"next": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/next/-/next-13.0.1.tgz",
"integrity": "sha512-ErCNBPIeZMKFn6hX+ZBSlqZVgJIeitEqhGTuQUNmYXJ07/A71DZ7AJI8eyHYUdBb686LUpV1/oBdTq9RpzRVPg==",
"requires": {
"@next/env": "13.0.1",
"@next/swc-android-arm-eabi": "13.0.1",
"@next/swc-android-arm64": "13.0.1",
"@next/swc-darwin-arm64": "13.0.1",
"@next/swc-darwin-x64": "13.0.1",
"@next/swc-freebsd-x64": "13.0.1",
"@next/swc-linux-arm-gnueabihf": "13.0.1",
"@next/swc-linux-arm64-gnu": "13.0.1",
"@next/swc-linux-arm64-musl": "13.0.1",
"@next/swc-linux-x64-gnu": "13.0.1",
"@next/swc-linux-x64-musl": "13.0.1",
"@next/swc-win32-arm64-msvc": "13.0.1",
"@next/swc-win32-ia32-msvc": "13.0.1",
"@next/swc-win32-x64-msvc": "13.0.1",
"@swc/helpers": "0.4.11",
"caniuse-lite": "^1.0.30001406",
"postcss": "8.4.14",
"styled-jsx": "5.1.0",
"use-sync-external-store": "1.2.0"
}
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"postcss": {
"version": "8.4.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
"requires": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
}
},
"react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"requires": {
"loose-envify": "^1.1.0"
}
},
"react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
"requires": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.0"
}
},
"scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
"requires": {
"loose-envify": "^1.1.0"
}
},
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
},
"styled-jsx": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.0.tgz",
"integrity": "sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==",
"requires": {
"client-only": "0.0.1"
}
},
"tslib": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
"integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
},
"use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"requires": {}
}
}
}

View File

@@ -9,12 +9,8 @@
"lint": "next lint"
},
"dependencies": {
"next": "12.3.1",
"next": "13.0.1",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"eslint": "8.23.1",
"eslint-config-next": "12.3.1"
}
}

View File

@@ -42,6 +42,8 @@ export default function Home() {
<a
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
className={styles.card}
>
<h2>Deploy &rarr;</h2>

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
"version": "0.0.0",
"private": true,
"license": "Apache-2.0",
"packageManager": "yarn@1.22.19",
"workspaces": {
"packages": [
"packages/*"
@@ -43,14 +44,14 @@
"publish-canary": "git checkout main && git pull && lerna version prerelease --preid canary --message \"Publish Canary\" --exact",
"publish-from-github": "./utils/publish.sh",
"changelog": "node utils/changelog.js",
"build": "turbo run build",
"build": "node utils/gen.js && turbo run build",
"vercel-build": "yarn build && yarn run pack && cd api && node -r ts-eager/register ./_lib/script/build.ts",
"pre-commit": "lint-staged",
"test": "jest --rootDir=\"test\" --testPathPattern=\"\\.test.js\"",
"test-unit": "yarn test && turbo run test-unit",
"test-integration-cli": "turbo run test-integration-cli",
"test-integration-once": "turbo run test-integration-once",
"test-integration-dev": "turbo run test-integration-dev",
"test-unit": "yarn test && node utils/gen.js && turbo run test-unit",
"test-integration-cli": "node utils/gen.js && turbo run test-integration-cli",
"test-integration-once": "node utils/gen.js && turbo run test-integration-once",
"test-integration-dev": "node utils/gen.js && turbo run test-integration-dev",
"lint": "eslint . --ext .ts,.js",
"prepare": "husky install",
"pack": "cd utils && node -r ts-eager/register ./pack.ts"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "5.5.4",
"version": "5.5.7",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",

View File

@@ -39,7 +39,7 @@ export class EdgeFunction {
assets?: { name: string; path: string }[];
/** The regions where the edge function will be executed on */
regions?: 'auto' | string[] | 'all' | 'default';
regions?: string | string[];
constructor(params: Omit<EdgeFunction, 'type'>) {
this.type = 'EdgeFunction';

View File

@@ -3,28 +3,37 @@ import { NodeVersion } from '../types';
import { NowBuildError } from '../errors';
import debug from '../debug';
const allOptions = [
{ major: 16, range: '16.x', runtime: 'nodejs16.x' },
{ major: 14, range: '14.x', runtime: 'nodejs14.x' },
{
major: 12,
range: '12.x',
runtime: 'nodejs12.x',
discontinueDate: new Date('2022-10-03'),
},
{
major: 10,
range: '10.x',
runtime: 'nodejs10.x',
discontinueDate: new Date('2021-04-20'),
},
{
major: 8,
range: '8.10.x',
runtime: 'nodejs8.10',
discontinueDate: new Date('2020-01-06'),
},
] as const;
function getOptions() {
const options = [
{ major: 16, range: '16.x', runtime: 'nodejs16.x' },
{ major: 14, range: '14.x', runtime: 'nodejs14.x' },
{
major: 12,
range: '12.x',
runtime: 'nodejs12.x',
discontinueDate: new Date('2022-10-03'),
},
{
major: 10,
range: '10.x',
runtime: 'nodejs10.x',
discontinueDate: new Date('2021-04-20'),
},
{
major: 8,
range: '8.10.x',
runtime: 'nodejs8.10',
discontinueDate: new Date('2020-01-06'),
},
] as const;
if (process.env.VERCEL_ALLOW_NODEJS18 === '1') {
return [
{ major: 18, range: '18.x', runtime: 'nodejs18.x' },
...options,
] as const;
}
return options;
}
function getHint(isAuto = false) {
const { major, range } = getLatestNodeVersion();
@@ -34,11 +43,11 @@ function getHint(isAuto = false) {
}
export function getLatestNodeVersion() {
return allOptions[0];
return getOptions()[0];
}
export function getDiscontinuedNodeVersions(): NodeVersion[] {
return allOptions.filter(isDiscontinued);
return getOptions().filter(isDiscontinued);
}
export async function getSupportedNodeVersion(
@@ -50,7 +59,7 @@ export async function getSupportedNodeVersion(
if (engineRange) {
const found =
validRange(engineRange) &&
allOptions.some(o => {
getOptions().some(o => {
// the array is already in order so return the first
// match which will be the newest version of node
selection = o;

View File

@@ -251,33 +251,24 @@ export async function getNodeVersion(
meta: Meta = {}
): Promise<NodeVersion> {
const latest = getLatestNodeVersion();
if (meta && meta.isDev) {
if (meta.isDev) {
// Use the system-installed version of `node` in PATH for `vercel dev`
return { ...latest, runtime: 'nodejs' };
}
const { packageJson } = await scanParentDirs(destPath, true);
let { nodeVersion } = config;
let isAuto = true;
if (packageJson && packageJson.engines && packageJson.engines.node) {
if (packageJson?.engines?.node) {
const { node } = packageJson.engines;
if (
nodeVersion &&
validRange(node) &&
!intersects(nodeVersion, node) &&
!meta.isDev
) {
if (nodeVersion && validRange(node) && !intersects(nodeVersion, node)) {
console.warn(
`Warning: Due to "engines": { "node": "${node}" } in your \`package.json\` file, the Node.js Version defined in your Project Settings ("${nodeVersion}") will not apply. Learn More: http://vercel.link/node-version`
);
} else if (coerce(node)?.raw === node && !meta.isDev) {
} else if (coerce(node)?.raw === node) {
console.warn(
`Warning: Detected "engines": { "node": "${node}" } in your \`package.json\` with major.minor.patch, but only major Node.js Version can be selected. Learn More: http://vercel.link/node-version`
);
} else if (
validRange(node) &&
intersects(`${latest.major + 1}.x`, node) &&
!meta.isDev
) {
} else if (validRange(node) && intersects(`${latest.major + 1}.x`, node)) {
console.warn(
`Warning: Detected "engines": { "node": "${node}" } in your \`package.json\` that will automatically upgrade when a new major Node.js Version is released. Learn More: http://vercel.link/node-version`
);

View File

@@ -23,6 +23,7 @@ export interface LambdaOptionsBase {
regions?: string[];
supportsMultiPayloads?: boolean;
supportsWrapper?: boolean;
experimentalResponseStreaming?: boolean;
}
export interface LambdaOptionsWithFiles extends LambdaOptionsBase {
@@ -60,6 +61,7 @@ export class Lambda {
zipBuffer?: Buffer;
supportsMultiPayloads?: boolean;
supportsWrapper?: boolean;
experimentalResponseStreaming?: boolean;
constructor(opts: LambdaOptions) {
const {
@@ -72,6 +74,7 @@ export class Lambda {
regions,
supportsMultiPayloads,
supportsWrapper,
experimentalResponseStreaming,
} = opts;
if ('files' in opts) {
assert(typeof opts.files === 'object', '"files" must be an object');
@@ -132,6 +135,7 @@ export class Lambda {
this.zipBuffer = 'zipBuffer' in opts ? opts.zipBuffer : undefined;
this.supportsMultiPayloads = supportsMultiPayloads;
this.supportsWrapper = supportsWrapper;
this.experimentalResponseStreaming = experimentalResponseStreaming;
}
async createZip(): Promise<Buffer> {

View File

@@ -8,6 +8,8 @@ interface PrerenderOptions {
group?: number;
bypassToken?: string | null /* optional to be non-breaking change */;
allowQuery?: string[];
initialHeaders?: Record<string, string>;
initialStatus?: number;
}
export class Prerender {
@@ -18,6 +20,8 @@ export class Prerender {
public group?: number;
public bypassToken: string | null;
public allowQuery?: string[];
public initialHeaders?: Record<string, string>;
public initialStatus?: number;
constructor({
expiration,
@@ -26,6 +30,8 @@ export class Prerender {
group,
bypassToken,
allowQuery,
initialHeaders,
initialStatus,
}: PrerenderOptions) {
this.type = 'Prerender';
this.expiration = expiration;
@@ -64,6 +70,30 @@ export class Prerender {
}
this.fallback = fallback;
if (initialHeaders !== undefined) {
if (
!initialHeaders ||
typeof initialHeaders !== 'object' ||
Object.entries(initialHeaders).some(
([key, value]) => typeof key !== 'string' || typeof value !== 'string'
)
) {
throw new Error(
`The \`initialHeaders\` argument for \`Prerender\` must be an object with string key/values`
);
}
this.initialHeaders = initialHeaders;
}
if (initialStatus !== undefined) {
if (initialStatus <= 0 || !Number.isInteger(initialStatus)) {
throw new Error(
`The \`initialStatus\` argument for \`Prerender\` must be a natural number.`
);
}
this.initialStatus = initialStatus;
}
if (allowQuery !== undefined) {
if (!Array.isArray(allowQuery)) {
throw new Error(

View File

@@ -285,6 +285,23 @@ it('should match all semver ranges', async () => {
);
});
it('should only allow nodejs18.x when env var is set', async () => {
try {
expect(getLatestNodeVersion()).toHaveProperty('major', 16);
expect(getSupportedNodeVersion('18.x')).rejects.toThrow();
process.env.VERCEL_ALLOW_NODEJS18 = '1';
expect(getLatestNodeVersion()).toHaveProperty('major', 18);
expect(await getSupportedNodeVersion('18.x')).toHaveProperty('major', 18);
expect(await getSupportedNodeVersion('18')).toHaveProperty('major', 18);
expect(await getSupportedNodeVersion('18.1.0')).toHaveProperty('major', 18);
expect(await getSupportedNodeVersion('>=16')).toHaveProperty('major', 18);
} finally {
delete process.env.VERCEL_ALLOW_NODEJS18;
}
});
it('should ignore node version in vercel dev getNodeVersion()', async () => {
expect(
await getNodeVersion(
@@ -433,6 +450,38 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
global.Date.now = realDateNow;
});
it('should support initialHeaders and initialStatus correctly', async () => {
const { Prerender } = require('@vercel/build-utils/dist/prerender.js');
new Prerender({
expiration: 1,
fallback: null,
group: 1,
bypassToken: 'some-long-bypass-token-to-make-it-work',
initialHeaders: {
'content-type': 'application/json',
'x-initial': 'true',
},
initialStatus: 308,
});
new Prerender({
expiration: 1,
fallback: null,
group: 1,
bypassToken: 'some-long-bypass-token-to-make-it-work',
initialStatus: 308,
});
new Prerender({
expiration: 1,
fallback: null,
group: 1,
bypassToken: 'some-long-bypass-token-to-make-it-work',
initialHeaders: {
'content-type': 'application/json',
'x-initial': 'true',
},
});
});
it('should support require by path for legacy builders', () => {
const index = require('@vercel/build-utils');

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.4.7",
"version": "28.4.14",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -41,16 +41,16 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "5.5.4",
"@vercel/go": "2.2.12",
"@vercel/hydrogen": "0.0.25",
"@vercel/next": "3.2.3",
"@vercel/node": "2.5.22",
"@vercel/python": "3.1.21",
"@vercel/redwood": "1.0.30",
"@vercel/remix": "1.0.31",
"@vercel/ruby": "1.3.38",
"@vercel/static-build": "1.0.30",
"@vercel/build-utils": "5.5.7",
"@vercel/go": "2.2.15",
"@vercel/hydrogen": "0.0.28",
"@vercel/next": "3.2.8",
"@vercel/node": "2.6.1",
"@vercel/python": "3.1.24",
"@vercel/redwood": "1.0.33",
"@vercel/remix": "1.0.34",
"@vercel/ruby": "1.3.41",
"@vercel/static-build": "1.0.34",
"update-notifier": "5.1.0"
},
"devDependencies": {
@@ -95,9 +95,10 @@
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.2.12",
"@vercel/frameworks": "1.1.6",
"@vercel/fs-detectors": "3.4.1",
"@vercel/client": "12.2.15",
"@vercel/error-utils": "1.0.2",
"@vercel/frameworks": "1.1.10",
"@vercel/fs-detectors": "3.4.7",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@zeit/source-map-support": "0.6.2",

View File

@@ -70,7 +70,7 @@ import getPrebuiltJson from '../../util/deploy/get-prebuilt-json';
import { createGitMeta } from '../../util/create-git-meta';
import { isValidArchive } from '../../util/deploy/validate-archive-format';
import { parseEnv } from '../../util/parse-env';
import { errorToString, isErrnoException, isError } from '../../util/is-error';
import { errorToString, isErrnoException, isError } from '@vercel/error-utils';
import { pickOverrides } from '../../util/projects/project-settings';
export default async (client: Client): Promise<number> => {

View File

@@ -3,16 +3,14 @@ import fs from 'fs-extra';
import DevServer from '../../util/dev/server';
import { parseListen } from '../../util/dev/parse-listen';
import { ProjectEnvVariable } from '../../types';
import Client from '../../util/client';
import { getLinkedProject } from '../../util/projects/link';
import { ProjectSettings } from '../../types';
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
import setupAndLink from '../../util/link/setup-and-link';
import getSystemEnvValues from '../../util/env/get-system-env-values';
import { getCommandName } from '../../util/pkg-name';
import param from '../../util/output/param';
import { OUTPUT_DIR } from '../../util/build/write-build-result';
import { pullEnvRecords } from '../../util/env/get-env-records';
type Options = {
'--listen': string;
@@ -57,8 +55,7 @@ export default async function dev(
}
let projectSettings: ProjectSettings | undefined;
let projectEnvs: ProjectEnvVariable[] = [];
let systemEnvValues: string[] = [];
let envValues: Record<string, string> = {};
if (link.status === 'linked') {
const { project, org } = link;
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
@@ -69,19 +66,15 @@ export default async function dev(
cwd = join(cwd, project.rootDirectory);
}
[{ envs: projectEnvs }, { systemEnvValues }] = await Promise.all([
getDecryptedEnvRecords(output, client, project.id, 'vercel-cli:dev'),
project.autoExposeSystemEnvs
? getSystemEnvValues(output, client, project.id)
: { systemEnvValues: [] },
]);
envValues = (
await pullEnvRecords(output, client, project.id, 'vercel-cli:dev')
).env;
}
const devServer = new DevServer(cwd, {
output,
projectSettings,
projectEnvs,
systemEnvValues,
envValues,
});
// If there is no Development Command, we must delete the

View File

@@ -15,7 +15,7 @@ import readConfig from '../../util/config/read-config';
import readJSONFile from '../../util/read-json-file';
import { getPkgName, getCommandName } from '../../util/pkg-name';
import { CantParseJSONFile } from '../../util/errors-ts';
import { isErrnoException } from '../../util/is-error';
import { isErrnoException } from '@vercel/error-utils';
const COMMAND_CONFIG = {
dev: ['dev'],

View File

@@ -11,7 +11,7 @@ import promptBool from '../../util/input/prompt-bool';
import purchaseDomain from '../../util/domains/purchase-domain';
import stamp from '../../util/output/stamp';
import { getCommandName } from '../../util/pkg-name';
import { errorToString } from '../../util/is-error';
import { errorToString } from '@vercel/error-utils';
type Options = {};

View File

@@ -4,21 +4,21 @@ import { closeSync, openSync, readSync } from 'fs';
import { resolve } from 'path';
import { Project, ProjectEnvTarget } from '../../types';
import Client from '../../util/client';
import exposeSystemEnvs from '../../util/dev/expose-system-envs';
import { emoji, prependEmoji } from '../../util/emoji';
import getSystemEnvValues from '../../util/env/get-system-env-values';
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
import confirm from '../../util/input/confirm';
import { Output } from '../../util/output';
import param from '../../util/output/param';
import stamp from '../../util/output/stamp';
import { getCommandName } from '../../util/pkg-name';
import { EnvRecordsSource } from '../../util/env/get-env-records';
import {
EnvRecordsSource,
pullEnvRecords,
} from '../../util/env/get-env-records';
import {
buildDeltaString,
createEnvObject,
} from '../../util/env/diff-env-files';
import { isErrnoException } from '../../util/is-error';
import { isErrnoException } from '@vercel/error-utils';
const CONTENTS_PREFIX = '# Created by Vercel CLI\n';
@@ -97,20 +97,11 @@ export default async function pull(
const pullStamp = stamp();
output.spinner('Downloading');
const [{ envs: projectEnvs }, { systemEnvValues }] = await Promise.all([
getDecryptedEnvRecords(output, client, project.id, source, environment),
project.autoExposeSystemEnvs
? getSystemEnvValues(output, client, project.id)
: { systemEnvValues: [] },
]);
const records = exposeSystemEnvs(
projectEnvs,
systemEnvValues,
project.autoExposeSystemEnvs,
undefined,
environment
);
const records = (
await pullEnvRecords(output, client, project.id, source, {
target: environment || ProjectEnvTarget.Development,
})
).env;
let deltaString = '';
let oldEnv;

View File

@@ -1,6 +1,6 @@
import chalk from 'chalk';
import Client from '../../util/client';
import { ensureLink } from '../../util/ensure-link';
import { ensureLink } from '../../util/link/ensure-link';
import getArgs from '../../util/get-args';
import getInvalidSubcommand from '../../util/get-invalid-subcommand';
import handleError from '../../util/handle-error';
@@ -80,7 +80,7 @@ export default async function main(client: Client) {
argv._ = argv._.slice(1);
subcommand = argv._[0];
const args = argv._.slice(1);
const confirm = Boolean(argv['--yes']);
const autoConfirm = Boolean(argv['--yes']);
const { output } = client;
let paths = [process.cwd()];
@@ -90,7 +90,7 @@ export default async function main(client: Client) {
}
const { path } = pathValidation;
const linkedProject = await ensureLink('git', client, path, confirm);
const linkedProject = await ensureLink('git', client, path, { autoConfirm });
if (typeof linkedProject === 'number') {
return linkedProject;
}

View File

@@ -7,7 +7,7 @@ import handleError from '../../util/handle-error';
import logo from '../../util/output/logo';
import init from './init';
import { getPkgName } from '../../util/pkg-name';
import { isError } from '../../util/is-error';
import { isError } from '@vercel/error-utils';
const COMMAND_CONFIG = {
init: ['init'],

View File

@@ -13,7 +13,7 @@ import { getDeployment } from '../util/get-deployment';
import { Deployment } from '@vercel/client';
import { Build } from '../types';
import title from 'title';
import { isErrnoException } from '../util/is-error';
import { isErrnoException } from '@vercel/error-utils';
import { isAPIError } from '../util/errors-ts';
import { URL } from 'url';

View File

@@ -3,9 +3,7 @@ import Client from '../../util/client';
import getArgs from '../../util/get-args';
import logo from '../../util/output/logo';
import { getPkgName } from '../../util/pkg-name';
import setupAndLink from '../../util/link/setup-and-link';
import { getCommandName } from '../../util/pkg-name';
import param from '../../util/output/param';
import { ensureLink } from '../../util/link/ensure-link';
const help = () => {
console.log(`
@@ -70,31 +68,16 @@ export default async function main(client: Client) {
}
const cwd = argv._[1] || process.cwd();
const link = await setupAndLink(client, cwd, {
const link = await ensureLink('link', client, cwd, {
autoConfirm: !!argv['--yes'],
forceDelete: true,
autoConfirm: argv['--yes'],
projectName: argv['--project'],
successEmoji: 'success',
setupMsg: 'Set up',
});
if (link.status === 'error') {
if (link.reason === 'HEADLESS') {
client.output.error(
`Command ${getCommandName(
'link'
)} requires confirmation. Use option ${param('--yes')} to confirm.`
);
}
return link.exitCode;
} else if (link.status === 'not_linked') {
// User aborted project linking questions
return 0;
} else if (link.status === 'linked') {
// Successfully linked
return 0;
} else {
const err: never = link;
throw new Error('Unknown link status: ' + err);
if (typeof link === 'number') {
return link;
}
return 0;
}

View File

@@ -17,10 +17,10 @@ import Client from '../util/client';
import { Deployment } from '@vercel/client';
import validatePaths from '../util/validate-paths';
import { getLinkedProject } from '../util/projects/link';
import { ensureLink } from '../util/ensure-link';
import { ensureLink } from '../util/link/ensure-link';
import getScope from '../util/get-scope';
import { isAPIError } from '../util/errors-ts';
import { isErrnoException } from '../util/is-error';
import { isErrnoException } from '@vercel/error-utils';
const help = () => {
console.log(`
@@ -56,7 +56,7 @@ const help = () => {
${chalk.gray('')} List all deployments for the project ${chalk.dim(
'`my-app`'
)} in the team of the currently linked project
${chalk.cyan(`$ ${getPkgName()} ls my-app`)}
${chalk.gray('')} Filter deployments by metadata
@@ -112,7 +112,7 @@ export default async function main(client: Client) {
return 2;
}
const yes = !!argv['--yes'];
const autoConfirm = !!argv['--yes'];
const prod = argv['--prod'] || false;
const meta = parseMeta(argv['--meta']);
@@ -145,7 +145,9 @@ export default async function main(client: Client) {
// If there's no linked project and user doesn't pass `app` arg,
// prompt to link their current directory.
if (status === 'not_linked' && !app) {
const linkedProject = await ensureLink('list', client, path, yes);
const linkedProject = await ensureLink('list', client, path, {
autoConfirm,
});
if (typeof linkedProject === 'number') {
return linkedProject;
}

View File

@@ -6,7 +6,7 @@ import getArgs from '../util/get-args';
import Client from '../util/client';
import { getCommandName, getPkgName } from '../util/pkg-name';
import { isAPIError } from '../util/errors-ts';
import { errorToString } from '../util/is-error';
import { errorToString } from '@vercel/error-utils';
const help = () => {
console.log(`

View File

@@ -4,24 +4,18 @@ import Client from '../util/client';
import { ProjectEnvTarget } from '../types';
import { emoji, prependEmoji } from '../util/emoji';
import getArgs from '../util/get-args';
import setupAndLink from '../util/link/setup-and-link';
import logo from '../util/output/logo';
import stamp from '../util/output/stamp';
import { getPkgName } from '../util/pkg-name';
import {
getLinkedProject,
VERCEL_DIR,
VERCEL_DIR_PROJECT,
} from '../util/projects/link';
import { VERCEL_DIR, VERCEL_DIR_PROJECT } from '../util/projects/link';
import { writeProjectSettings } from '../util/projects/project-settings';
import envPull from './env/pull';
import { getCommandName } from '../util/pkg-name';
import param from '../util/output/param';
import type { Project, Org } from '../types';
import type { Project } from '../types';
import {
isValidEnvTarget,
getEnvTargetPlaceholder,
} from '../util/env/env-target';
import { ensureLink } from '../util/link/ensure-link';
const help = () => {
return console.log(`
@@ -83,43 +77,6 @@ function parseArgs(client: Client) {
return argv;
}
type LinkResult = {
org: Org;
project: Project;
};
async function ensureLink(
client: Client,
cwd: string,
yes: boolean
): Promise<LinkResult | number> {
let link = await getLinkedProject(client, cwd);
if (link.status === 'not_linked') {
link = await setupAndLink(client, cwd, {
autoConfirm: yes,
successEmoji: 'link',
setupMsg: 'Set up',
});
if (link.status === 'not_linked') {
// User aborted project linking questions
return 0;
}
}
if (link.status === 'error') {
if (link.reason === 'HEADLESS') {
client.output.error(
`Command ${getCommandName(
'pull'
)} requires confirmation. Use option ${param('--yes')} to confirm.`
);
}
return link.exitCode;
}
return { org: link.org, project: link.project };
}
async function pullAllEnvFiles(
environment: ProjectEnvTarget,
client: Client,
@@ -140,7 +97,9 @@ async function pullAllEnvFiles(
);
}
function parseEnvironment(environment = 'development'): ProjectEnvTarget {
export function parseEnvironment(
environment = 'development'
): ProjectEnvTarget {
if (!isValidEnvTarget(environment)) {
throw new Error(
`environment "${environment}" not supported; must be one of ${getEnvTargetPlaceholder()}`
@@ -156,10 +115,10 @@ export default async function main(client: Client) {
}
const cwd = argv._[1] || process.cwd();
const yes = Boolean(argv['--yes']);
const autoConfirm = Boolean(argv['--yes']);
const environment = parseEnvironment(argv['--environment'] || undefined);
const link = await ensureLink(client, cwd, yes);
const link = await ensureLink('pull', client, cwd, { autoConfirm });
if (typeof link === 'number') {
return link;
}

View File

@@ -11,7 +11,7 @@ import { getPkgName, getCommandName } from '../../util/pkg-name';
import Client from '../../util/client';
import createTeam from '../../util/teams/create-team';
import patchTeam from '../../util/teams/patch-team';
import { errorToString, isError } from '../../util/is-error';
import { errorToString, isError } from '@vercel/error-utils';
const validateSlugKeypress = (data: string, value: string) =>
// TODO: the `value` here should contain the current value + the keypress

View File

@@ -12,7 +12,7 @@ import { email as regexEmail } from '../../util/input/regexes';
import getTeams from '../../util/teams/get-teams';
import inviteUserToTeam from '../../util/teams/invite-user-to-team';
import { isAPIError } from '../../util/errors-ts';
import { errorToString, isError } from '../../util/is-error';
import { errorToString, isError } from '@vercel/error-utils';
const validateEmail = (data: string) =>
regexEmail.test(data.trim()) || data.length === 0;

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env node
import { isErrnoException, isError, errorToString } from './util/is-error';
import { isErrnoException, isError, errorToString } from '@vercel/error-utils';
try {
// Test to see if cwd has been deleted before
@@ -610,6 +610,21 @@ const main = async () => {
return 1;
}
if (isErrnoException(err) && err.code === 'ECONNRESET') {
// Error message will look like the following:
// request to https://api.vercel.com/v2/user failed, reason: socket hang up
const matches = /request to https:\/\/(.*?)\//.exec(err.message || '');
const hostname = matches?.[1];
if (hostname) {
output.error(
`Connection to ${highlight(
hostname
)} interrupted. Please verify your internet connectivity and DNS configuration.`
);
}
return 1;
}
if (
isErrnoException(err) &&
(err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED')

View File

@@ -16,7 +16,7 @@ import { VERCEL_DIR } from '../projects/link';
import { Output } from '../output';
import readJSONFile from '../read-json-file';
import { CantParseJSONFile } from '../errors-ts';
import { errorToString, isErrnoException, isError } from '../is-error';
import { errorToString, isErrnoException, isError } from '@vercel/error-utils';
import cmd from '../output/cmd';
import code from '../output/code';

View File

@@ -2,7 +2,7 @@ import { readFileSync } from 'fs';
import { resolve } from 'path';
import Client from '../client';
import { Cert } from '../../types';
import { isErrnoException } from '../is-error';
import { isErrnoException } from '@vercel/error-utils';
import { isAPIError } from '../errors-ts';
export default async function createCertFromFile(

View File

@@ -2,7 +2,7 @@ import retry from 'async-retry';
import { Cert } from '../../types';
import Client from '../client';
import { isAPIError } from '../errors-ts';
import { isError } from '../is-error';
import { isError } from '@vercel/error-utils';
// When it's a configuration error we should retry because of the DNS propagation
// otherwise we bail to handle the error in the upper level

View File

@@ -23,7 +23,7 @@ import type {
} from '../types';
import { sharedPromise } from './promise';
import { APIError } from './errors-ts';
import { normalizeError } from './is-error';
import { normalizeError } from '@vercel/error-utils';
const isSAMLError = (v: any): v is SAMLError => {
return v && v.saml;

View File

@@ -10,7 +10,7 @@ import error from '../output/error';
import highlight from '../output/highlight';
import { VercelConfig } from '../dev/types';
import { AuthConfig, GlobalConfig } from '../../types';
import { isErrnoException, isError } from '../is-error';
import { isErrnoException, isError } from '@vercel/error-utils';
const VERCEL_DIR = getGlobalPathConfig();
const CONFIG_FILE_PATH = join(VERCEL_DIR, 'config.json');

View File

@@ -5,7 +5,7 @@ import git from 'git-last-commit';
import { exec } from 'child_process';
import { GitMetadata, Project } from '../types';
import { Output } from './output';
import { errorToString } from './is-error';
import { errorToString } from '@vercel/error-utils';
export async function createGitMeta(
directory: string,

View File

@@ -1,46 +0,0 @@
import {
ProjectEnvType,
ProjectEnvVariable,
ProjectEnvTarget,
} from '../../types';
import { Env } from '@vercel/build-utils';
function getSystemEnvValue(
systemEnvRef: string,
{ vercelUrl }: { vercelUrl?: string }
) {
if (systemEnvRef === 'VERCEL_URL') {
return vercelUrl || '';
}
return '';
}
export default function exposeSystemEnvs(
projectEnvs: ProjectEnvVariable[],
systemEnvValues: string[],
autoExposeSystemEnvs: boolean | undefined,
vercelUrl?: string,
target?: ProjectEnvTarget
) {
const envs: Env = {};
if (autoExposeSystemEnvs) {
envs['VERCEL'] = '1';
envs['VERCEL_ENV'] = target || 'development';
for (const key of systemEnvValues) {
envs[key] = getSystemEnvValue(key, { vercelUrl });
}
}
for (let env of projectEnvs) {
if (env.type === ProjectEnvType.System) {
envs[env.key] = getSystemEnvValue(env.value, { vercelUrl });
} else {
envs[env.key] = env.value;
}
}
return envs;
}

View File

@@ -16,3 +16,75 @@ export function nodeHeadersToFetchHeaders(
}
return headers;
}
/**
* Request headers that are not allowed to be overridden by a middleware.
*/
const NONOVERRIDABLE_HEADERS: Set<string> = new Set([
'host',
'connection',
'content-length',
'transfer-encoding',
'keep-alive',
'transfer-encoding',
'te',
'upgrade',
'trailer',
]);
/**
* Adds/Updates/Deletes headers in `reqHeaders` based on the response headers
* from a middleware (`respHeaders`).
*
* `x-middleware-override-headers` is a comma-separated list of *all* header
* names that should appear in new request headers. Names not in this list
* will be deleted.
*
* `x-middleware-request-*` is the new value for each header. This can't be
* omitted, even if the header is not being modified.
*
*/
export function applyOverriddenHeaders(
reqHeaders: { [k: string]: string | string[] | undefined },
respHeaders: Headers
) {
const overriddenHeaders = respHeaders.get('x-middleware-override-headers');
if (!overriddenHeaders) {
return;
}
const overriddenKeys: Set<string> = new Set();
for (const key of overriddenHeaders.split(',')) {
overriddenKeys.add(key.trim());
}
respHeaders.delete('x-middleware-override-headers');
// Delete headers.
for (const key of Object.keys(reqHeaders)) {
if (!NONOVERRIDABLE_HEADERS.has(key) && !overriddenKeys.has(key)) {
delete reqHeaders[key];
}
}
// Update or add headers.
for (const key of overriddenKeys.keys()) {
if (NONOVERRIDABLE_HEADERS.has(key)) {
continue;
}
const valueKey = 'x-middleware-request-' + key;
const newValue = respHeaders.get(valueKey);
const oldValue = reqHeaders[key];
if (oldValue !== newValue) {
if (newValue) {
reqHeaders[key] = newValue;
} else {
delete reqHeaders[key];
}
}
respHeaders.delete(valueKey);
}
}

View File

@@ -85,17 +85,16 @@ import {
HttpHeadersConfig,
EnvConfigs,
} from './types';
import { ProjectEnvVariable, ProjectSettings } from '../../types';
import exposeSystemEnvs from './expose-system-envs';
import { ProjectSettings } from '../../types';
import { treeKill } from '../tree-kill';
import { nodeHeadersToFetchHeaders } from './headers';
import { applyOverriddenHeaders, nodeHeadersToFetchHeaders } from './headers';
import { formatQueryString, parseQueryString } from './parse-query-string';
import {
errorToString,
isErrnoException,
isError,
isSpawnError,
} from '../is-error';
} from '@vercel/error-utils';
import isURL from './is-url';
import { pickOverrides } from '../projects/project-settings';
import { replaceLocalhost } from './parse-listen';
@@ -168,15 +167,13 @@ export default class DevServer {
private blockingBuildsPromise: Promise<void> | null;
private startPromise: Promise<void> | null;
private systemEnvValues: string[];
private projectEnvs: ProjectEnvVariable[];
private envValues: Record<string, string>;
constructor(cwd: string, options: DevServerOptions) {
this.cwd = cwd;
this.output = options.output;
this.envConfigs = { buildEnv: {}, runEnv: {}, allEnv: {} };
this.systemEnvValues = options.systemEnvValues || [];
this.projectEnvs = options.projectEnvs || [];
this.envValues = options.envValues || {};
this.files = {};
this.originalProjectSettings = options.projectSettings;
this.projectSettings = options.projectSettings;
@@ -684,16 +681,13 @@ export default class DevServer {
// If no .env/.build.env is present, use cloud environment variables
if (Object.keys(allEnv).length === 0) {
const cloudEnv = exposeSystemEnvs(
this.projectEnvs || [],
this.systemEnvValues || [],
this.projectSettings?.autoExposeSystemEnvs,
this.address.host
);
allEnv = { ...cloudEnv };
runEnv = { ...cloudEnv };
buildEnv = { ...cloudEnv };
const envValues = { ...this.envValues };
if (this.address.host) {
envValues['VERCEL_URL'] = this.address.host;
}
allEnv = { ...envValues };
runEnv = { ...envValues };
buildEnv = { ...envValues };
}
// legacy NOW_REGION env variable
@@ -1454,7 +1448,9 @@ export default class DevServer {
}
);
if (middlewareRes.status === 500) {
const middlewareBody = await middlewareRes.buffer();
if (middlewareRes.status === 500 && middlewareBody.byteLength === 0) {
await this.sendError(
req,
res,
@@ -1478,6 +1474,9 @@ export default class DevServer {
'content-length',
'transfer-encoding',
]);
applyOverriddenHeaders(req.headers, middlewareRes.headers);
for (const [name, value] of middlewareRes.headers) {
if (name === 'x-middleware-next') {
shouldContinue = value === '1';
@@ -1496,7 +1495,6 @@ export default class DevServer {
}
if (!shouldContinue) {
const middlewareBody = await middlewareRes.buffer();
this.setResponseHeaders(res, requestId);
if (middlewareBody.length > 0) {
res.setHeader('content-length', middlewareBody.length);

View File

@@ -16,7 +16,7 @@ import {
import { VercelConfig } from '@vercel/client';
import { HandleValue, Route } from '@vercel/routing-utils';
import { Output } from '../output';
import { ProjectEnvVariable, ProjectSettings } from '../../types';
import { ProjectSettings } from '../../types';
import { BuilderWithPkg } from '../build/import-builders';
export { VercelConfig };
@@ -24,8 +24,7 @@ export { VercelConfig };
export interface DevServerOptions {
output: Output;
projectSettings?: ProjectSettings;
systemEnvValues?: string[];
projectEnvs?: ProjectEnvVariable[];
envValues?: Record<string, string>;
}
export interface EnvConfigs {

View File

@@ -1,45 +0,0 @@
import { Org, Project } from '../types';
import Client from './client';
import setupAndLink from './link/setup-and-link';
import param from './output/param';
import { getCommandName } from './pkg-name';
import { getLinkedProject } from './projects/link';
type LinkResult = {
org: Org;
project: Project;
};
export async function ensureLink(
commandName: string,
client: Client,
cwd: string,
yes: boolean
): Promise<LinkResult | number> {
let link = await getLinkedProject(client, cwd);
if (link.status === 'not_linked') {
link = await setupAndLink(client, cwd, {
autoConfirm: yes,
successEmoji: 'link',
setupMsg: 'Set up',
});
if (link.status === 'not_linked') {
// User aborted project linking questions
return 0;
}
}
if (link.status === 'error') {
if (link.reason === 'HEADLESS') {
client.output.error(
`Command ${getCommandName(
commandName
)} requires confirmation. Use option ${param('--yes')} to confirm.`
);
}
return link.exitCode;
}
return { org: link.org, project: link.project };
}

View File

@@ -2,6 +2,7 @@ import { Output } from '../output';
import Client from '../client';
import { ProjectEnvVariable, ProjectEnvTarget } from '../../types';
import { URLSearchParams } from 'url';
import * as path from 'path';
/** The CLI command that was used that needs the environment variables. */
export type EnvRecordsSource =
@@ -49,3 +50,40 @@ export default async function getEnvRecords(
return client.fetch<{ envs: ProjectEnvVariable[] }>(url);
}
interface PullEnvOptions {
target?: ProjectEnvTarget | string;
gitBranch?: string;
}
export async function pullEnvRecords(
output: Output,
client: Client,
projectId: string,
source: EnvRecordsSource,
{ target, gitBranch }: PullEnvOptions = {}
) {
output.debug(
`Fetching Environment Variables of project ${projectId} and target ${target}`
);
const query = new URLSearchParams();
let url = `/v1/env/pull/${projectId}`;
if (target) {
url = path.join(url, target, gitBranch ?? '');
}
if (source) {
query.set('source', source);
}
if (Array.from(query).length > 0) {
url += `?${query}`;
}
return client.fetch<{
env: Record<string, string>;
buildEnv: Record<string, string>;
}>(url);
}

View File

@@ -1,12 +0,0 @@
import { Output } from '../output';
import Client from '../client';
export default async function getSystemEnvValues(
output: Output,
client: Client,
projectId: string
) {
output.debug(`Fetching System Environment Values of project ${projectId}`);
const url = `/v6/projects/${projectId}/system-env-values`;
return client.fetch<{ systemEnvValues: string[] }>(url);
}

View File

@@ -1,4 +1,4 @@
import { isErrnoException } from '../is-error';
import { isErrnoException } from '@vercel/error-utils';
const knownErrorsCodes = new Set([
'PAYMENT_REQUIRED',

View File

@@ -5,7 +5,7 @@ import { NowError } from './now-error';
import code from './output/code';
import { getCommandName } from './pkg-name';
import chalk from 'chalk';
import { isError } from './is-error';
import { isError } from '@vercel/error-utils';
/**
* This error is thrown when there is an API error with a payload. The error

View File

@@ -10,7 +10,7 @@ import humanizePath from './humanize-path';
import readJSONFile from './read-json-file';
import { VercelConfig } from './dev/types';
import { Output } from './output';
import { isErrnoException } from './is-error';
import { isErrnoException } from '@vercel/error-utils';
let config: VercelConfig;

View File

@@ -1,66 +0,0 @@
import Client from './client';
import { Output } from './output/create-output';
import {
ProjectEnvTarget,
ProjectEnvType,
ProjectEnvVariable,
Secret,
} from '../types';
import getEnvRecords, { EnvRecordsSource } from './env/get-env-records';
import { isAPIError } from './errors-ts';
export default async function getDecryptedEnvRecords(
output: Output,
client: Client,
projectId: string,
source: EnvRecordsSource,
target?: ProjectEnvTarget
): Promise<{ envs: ProjectEnvVariable[] }> {
const { envs } = await getEnvRecords(output, client, projectId, source, {
target: target || ProjectEnvTarget.Development,
decrypt: true,
});
const envsWithDecryptedSecrets = await Promise.all(
envs.map(async ({ id, type, key, value }) => {
// it's not possible to create secret env variables for development
// anymore but we keep this because legacy env variables with "decryptable"
// secret values still exit in our system
if (type === ProjectEnvType.Secret) {
try {
const secretIdOrName = value;
if (!secretIdOrName) {
return { id, type, key, value: '', found: true };
}
output.debug(`Fetching decrypted secret ${secretIdOrName}`);
const secret = await client.fetch<Secret>(
`/v2/now/secrets/${secretIdOrName}?decrypt=true`
);
return { id, type, key, value: secret.value, found: true };
} catch (err: unknown) {
if (isAPIError(err) && err.status === 404) {
return { id, type, key, value: '', found: false };
}
throw err;
}
}
return { id, type, key, value, found: true };
})
);
for (let env of envsWithDecryptedSecrets) {
if (!env.found) {
output.print('');
output.warn(
`Unable to download variable ${env.key} because associated secret was deleted`
);
}
}
return { envs: envsWithDecryptedSecrets };
}

View File

@@ -0,0 +1,58 @@
import { Org, Project } from '../../types';
import Client from '../client';
import setupAndLink from '../link/setup-and-link';
import param from '../output/param';
import { getCommandName } from '../pkg-name';
import { getLinkedProject } from '../projects/link';
import type { SetupAndLinkOptions } from '../link/setup-and-link';
type LinkResult = {
org: Org;
project: Project;
};
/**
* Checks if a project is already linked and if not, links the project and
* validates the link response.
*
* @param commandName - The name of the current command to print in the
* event of an error
* @param client - The Vercel Node.js client instance
* @param cwd - The current working directory
* @param opts.forceDelete - When `true`, deletes the project's `.vercel`
* directory
* @param opts.projectName - The project name to use when linking, otherwise
* the current directory
* @returns {Promise<LinkResult|number>} Returns a numeric exit code when aborted or
* error, otherwise an object containing the org an project
*/
export async function ensureLink(
commandName: string,
client: Client,
cwd: string,
opts: SetupAndLinkOptions
): Promise<LinkResult | number> {
let link = await getLinkedProject(client, cwd);
if (link.status === 'not_linked') {
link = await setupAndLink(client, cwd, opts);
if (link.status === 'not_linked') {
// User aborted project linking questions
return 0;
}
}
if (link.status === 'error') {
if (link.reason === 'HEADLESS') {
client.output.error(
`Command ${getCommandName(
commandName
)} requires confirmation. Use option ${param('--yes')} to confirm.`
);
}
return link.exitCode;
}
return { org: link.org, project: link.project };
}

View File

@@ -32,8 +32,8 @@ import { isAPIError } from '../errors-ts';
export interface SetupAndLinkOptions {
forceDelete?: boolean;
autoConfirm?: boolean;
successEmoji: EmojiLabel;
setupMsg: string;
successEmoji?: EmojiLabel;
setupMsg?: string;
projectName?: string;
}
@@ -43,8 +43,8 @@ export default async function setupAndLink(
{
forceDelete = false,
autoConfirm = false,
successEmoji,
setupMsg,
successEmoji = 'link',
setupMsg = 'Set up',
projectName,
}: SetupAndLinkOptions
): Promise<ProjectLinkResult> {

View File

@@ -7,7 +7,7 @@ import executeLogin from './login';
import Client from '../client';
import { LoginResult } from './types';
import { isAPIError } from '../errors-ts';
import { errorToString } from '../is-error';
import { errorToString } from '@vercel/error-utils';
export default async function doEmailLogin(
client: Client,

View File

@@ -1,6 +1,6 @@
import Client from '../client';
import { InvalidEmail, AccountNotFound, isAPIError } from '../errors-ts';
import { errorToString } from '../is-error';
import { errorToString } from '@vercel/error-utils';
import { LoginData } from './types';
export default async function login(

View File

@@ -2,7 +2,7 @@ import chalk from 'chalk';
import renderLink from './link';
import wait, { StopSpinner } from './wait';
import type { WritableTTY } from '../../types';
import { errorToString } from '../is-error';
import { errorToString } from '@vercel/error-utils';
const IS_TEST = process.env.NODE_ENV === 'test';

View File

@@ -18,7 +18,7 @@ import { prependEmoji, emoji, EmojiLabel } from '../emoji';
import { isDirectory } from '../config/global-path';
import { NowBuildError, getPlatformEnv } from '@vercel/build-utils';
import outputCode from '../output/code';
import { isErrnoException, isError } from '../is-error';
import { isErrnoException, isError } from '@vercel/error-utils';
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);

View File

@@ -1,7 +1,7 @@
import Client from './client';
import getScope from './get-scope';
import getArgs from './get-args';
import { isError } from './is-error';
import { isError } from '@vercel/error-utils';
import type { Team, User } from '../types';
export default async function reportError(

View File

@@ -6,7 +6,7 @@
},
"dependencies": {
"next": "latest",
"react": "^17.0.0",
"react-dom": "^17.0.0"
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}

View File

@@ -1 +1 @@
export default () => new Response(null, { status: 500 });
export default () => new Response('Example Error', { status: 500 });

View File

@@ -0,0 +1,3 @@
export default (req, res) => {
res.json(req.headers);
};

View File

@@ -0,0 +1,18 @@
export default () => {
return new Response(null, {
headers: {
'x-middleware-next': '1',
'x-middleware-override-headers':
'x-from-client-a,x-from-client-b,x-from-middleware-a,x-from-middleware-b,transfer-encoding',
// Headers to be preserved.
'x-middleware-request-x-from-client-a': 'hello from client',
// Headers to be modified by the middleware.
'x-middleware-request-x-from-client-b': 'hello from middleware',
// Headers to be added by the middleware.
'x-middleware-request-x-from-middleware-a': 'hello a!',
'x-middleware-request-x-from-middleware-b': 'hello b!',
// Headers not allowed by the dev server: will be ignored.
'transfer-encoding': 'gzip, chunked',
},
});
};

View File

@@ -2,6 +2,7 @@ import ms from 'ms';
import fs from 'fs-extra';
import { isIP } from 'net';
import { join } from 'path';
import { Response } from 'node-fetch';
const {
fetch,
@@ -550,7 +551,7 @@ test(
test(
'[vercel dev] Middleware with an explicit 500 response',
testFixtureStdio('middleware-500-response', async (testPath: any) => {
await testPath(500, '/', /EDGE_FUNCTION_INVOCATION_FAILED/);
await testPath(500, '/', 'Example Error');
})
);
@@ -613,3 +614,72 @@ test(
{ skipDeploy: true }
)
);
test(
'[vercel dev] Middleware can override request headers',
testFixtureStdio(
'middleware-request-headers-override',
async (testPath: any) => {
await testPath(
200,
'/api/dump-headers',
(actual: string, res: Response) => {
// Headers sent to the API route.
const headers = JSON.parse(actual);
// Preserved headers.
expect(headers).toHaveProperty(
'x-from-client-a',
'hello from client'
);
// Headers added/modified by the middleware.
expect(headers).toHaveProperty(
'x-from-client-b',
'hello from middleware'
);
expect(headers).toHaveProperty('x-from-middleware-a', 'hello a!');
expect(headers).toHaveProperty('x-from-middleware-b', 'hello b!');
// Headers deleted by the middleware.
expect(headers).not.toHaveProperty('x-from-client-c');
// Internal headers should not be visible from API routes.
expect(headers).not.toHaveProperty('x-middleware-override-headers');
expect(headers).not.toHaveProperty(
'x-middleware-request-from-middleware-a'
);
expect(headers).not.toHaveProperty(
'x-middleware-request-from-middleware-b'
);
// Request headers should not be visible from clients.
const respHeaders = Object.fromEntries(res.headers.entries());
expect(respHeaders).not.toHaveProperty(
'x-middleware-override-headers'
);
expect(respHeaders).not.toHaveProperty(
'x-middleware-request-from-middleware-a'
);
expect(respHeaders).not.toHaveProperty(
'x-middleware-request-from-middleware-b'
);
expect(respHeaders).not.toHaveProperty('from-middleware-a');
expect(respHeaders).not.toHaveProperty('from-middleware-b');
expect(respHeaders).not.toHaveProperty('x-from-client-a');
expect(respHeaders).not.toHaveProperty('x-from-client-b');
expect(respHeaders).not.toHaveProperty('x-from-client-c');
},
/*expectedHeaders=*/ {},
{
headers: {
'x-from-client-a': 'hello from client',
'x-from-client-b': 'hello from client',
'x-from-client-c': 'hello from client',
},
}
);
},
{ skipDeploy: true }
)
);

View File

@@ -1,37 +1,44 @@
import { client } from './client';
import { Project } from '../../src/types';
import {
Project,
ProjectEnvTarget,
ProjectEnvType,
ProjectEnvVariable,
} from '../../src/types';
import { formatProvider } from '../../src/util/git/connect-git-provider';
import { parseEnvironment } from '../../src/commands/pull';
import { Env } from '@vercel/build-utils/dist';
const envs = [
const envs: ProjectEnvVariable[] = [
{
type: 'encrypted',
type: ProjectEnvType.Encrypted,
id: '781dt89g8r2h789g',
key: 'REDIS_CONNECTION_STRING',
value: 'redis://abc123@redis.example.com:6379',
target: ['production', 'preview'],
gitBranch: null,
target: [ProjectEnvTarget.Production, ProjectEnvTarget.Preview],
gitBranch: undefined,
configurationId: null,
updatedAt: 1557241361455,
createdAt: 1557241361455,
},
{
type: 'encrypted',
type: ProjectEnvType.Encrypted,
id: 'r124t6frtu25df16',
key: 'SQL_CONNECTION_STRING',
value: 'Server=sql.example.com;Database=app;Uid=root;Pwd=P455W0RD;',
target: ['production'],
gitBranch: null,
target: [ProjectEnvTarget.Production],
gitBranch: undefined,
configurationId: null,
updatedAt: 1557241361445,
createdAt: 1557241361445,
},
{
type: 'encrypted',
type: ProjectEnvType.Encrypted,
id: 'a235l6frtu25df32',
key: 'SPECIAL_FLAG',
value: '1',
target: ['development'],
gitBranch: null,
target: [ProjectEnvTarget.Development],
gitBranch: undefined,
configurationId: null,
updatedAt: 1557241361445,
createdAt: 1557241361445,
@@ -206,6 +213,43 @@ export function useProject(project: Partial<Project> = defaultProject) {
Object.assign(project, req.body);
res.json(project);
});
client.scenario.get(
`/v1/env/pull/${project.id}/:target?/:gitBranch?`,
(req, res) => {
const target: ProjectEnvTarget | undefined =
typeof req.params.target === 'string'
? parseEnvironment(req.params.target)
: undefined;
let projectEnvs = envs;
if (target) {
projectEnvs = projectEnvs.filter(env => {
if (typeof env.target === 'string') {
return env.target === target;
}
if (Array.isArray(env.target)) {
return env.target.includes(target);
}
return false;
});
}
const allEnvs = Object.entries(
exposeSystemEnvs(
projectEnvs,
systemEnvs.map(env => env.key),
project.autoExposeSystemEnvs,
undefined,
target
)
);
const env: Record<string, string> = {};
allEnvs.forEach(([k, v]) => {
env[k] = v ?? '';
});
res.json({ env: env });
}
);
client.scenario.get(
`/v6/projects/${project.id}/system-env-values`,
(_req, res) => {
@@ -222,15 +266,26 @@ export function useProject(project: Partial<Project> = defaultProject) {
});
}
);
client.scenario.get(`/v8/projects/${project.id}/env`, (_req, res) => {
const target = _req.query.target;
if (typeof target === 'string') {
const targetEnvs = envs.filter(env => env.target.includes(target));
res.json({ envs: targetEnvs });
return;
client.scenario.get(`/v8/projects/${project.id}/env`, (req, res) => {
const target: ProjectEnvTarget | undefined =
typeof req.query.target === 'string'
? parseEnvironment(req.query.target)
: undefined;
let targetEnvs = envs;
if (target) {
targetEnvs = targetEnvs.filter(env => {
if (typeof env.target === 'string') {
return env.target === target;
}
if (Array.isArray(env.target)) {
return env.target.includes(target);
}
return false;
});
}
res.json({ envs });
res.json({ envs: targetEnvs });
});
client.scenario.post(`/v8/projects/${project.id}/env`, (req, res) => {
const envObj = req.body;
@@ -310,3 +365,43 @@ export function useProject(project: Partial<Project> = defaultProject) {
return { project, envs };
}
function getSystemEnvValue(
systemEnvRef: string,
{ vercelUrl }: { vercelUrl?: string }
) {
if (systemEnvRef === 'VERCEL_URL') {
return vercelUrl || '';
}
return '';
}
function exposeSystemEnvs(
projectEnvs: ProjectEnvVariable[],
systemEnvValues: string[],
autoExposeSystemEnvs: boolean | undefined,
vercelUrl?: string,
target?: ProjectEnvTarget
) {
const envs: Env = {};
if (autoExposeSystemEnvs) {
envs['VERCEL'] = '1';
envs['VERCEL_ENV'] = target || 'development';
for (const key of systemEnvValues) {
envs[key] = getSystemEnvValue(key, { vercelUrl });
}
}
for (let env of projectEnvs) {
if (env.type === ProjectEnvType.System) {
envs[env.key] = getSystemEnvValue(env.value, { vercelUrl });
} else {
envs[env.key] = env.value;
}
}
return envs;
}

View File

@@ -0,0 +1,77 @@
import { Headers } from 'node-fetch';
import { applyOverriddenHeaders } from '../../../../src/util/dev/headers';
describe('applyOverriddenHeaders', () => {
it('do nothing if x-middleware-override-headers is not set', async () => {
const reqHeaders = { a: '1' };
const respHeaders = new Headers();
applyOverriddenHeaders(reqHeaders, respHeaders);
expect(reqHeaders).toStrictEqual({ a: '1' });
});
it('adds a new header', async () => {
const reqHeaders = { a: '1' };
const respHeaders = new Headers({
// Define a new header 'b' and keep the existing header 'a'
'x-middleware-override-headers': 'a,b',
'x-middleware-request-a': '1',
'x-middleware-request-b': '2',
});
applyOverriddenHeaders(reqHeaders, respHeaders);
expect(reqHeaders).toStrictEqual({ a: '1', b: '2' });
});
it('delete the header if x-middleware-request-* is undefined', async () => {
const reqHeaders = { a: '1', b: '2' };
const respHeaders = new Headers({
// Deletes a new header 'c' and keep the existing headers `a` and `b`
'x-middleware-override-headers': 'a,b,c',
'x-middleware-request-a': '1',
'x-middleware-request-b': '2',
});
applyOverriddenHeaders(reqHeaders, respHeaders);
expect(reqHeaders).toStrictEqual({ a: '1', b: '2' });
});
it('updates an existing header', async () => {
const reqHeaders = { a: '1', b: '2' };
const respHeaders = new Headers({
// Modifies the header 'b' and keep the existing header 'a'
'x-middleware-override-headers': 'a,b',
'x-middleware-request-a': '1',
'x-middleware-request-b': 'modified',
});
applyOverriddenHeaders(reqHeaders, respHeaders);
expect(reqHeaders).toStrictEqual({ a: '1', b: 'modified' });
});
it('ignores headers listed in NONOVERRIDABLE_HEADERS', async () => {
const reqHeaders = { a: '1', host: 'example.com' };
const respHeaders = new Headers({
// Define a new header 'b' and 'content-length'
'x-middleware-override-headers': 'a,b,content-length',
'x-middleware-request-a': '1',
'x-middleware-request-b': '2',
'x-middleware-request-content-length': '128',
});
applyOverriddenHeaders(reqHeaders, respHeaders);
expect(reqHeaders).toStrictEqual({ a: '1', b: '2', host: 'example.com' });
});
it('deletes an existing header', async () => {
const reqHeaders = { a: '1', b: '2' };
const respHeaders = new Headers({
// Deletes the header 'a' and keep the existing header 'b'
'x-middleware-override-headers': 'b',
'x-middleware-request-b': '2',
});
applyOverriddenHeaders(reqHeaders, respHeaders);
expect(reqHeaders).toStrictEqual({ b: '2' });
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "12.2.12",
"version": "12.2.15",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -43,8 +43,8 @@
]
},
"dependencies": {
"@vercel/build-utils": "5.5.4",
"@vercel/routing-utils": "2.0.2",
"@vercel/build-utils": "5.5.7",
"@vercel/routing-utils": "2.1.1",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
"async-sema": "3.0.0",

View File

@@ -6,6 +6,7 @@
- [ExtraResponseInit](interfaces/ExtraResponseInit.md)
- [Geo](interfaces/Geo.md)
- [ModifiedRequest](interfaces/ModifiedRequest.md)
### Variables
@@ -15,6 +16,7 @@
- [LATITUDE_HEADER_NAME](README.md#latitude_header_name)
- [LONGITUDE_HEADER_NAME](README.md#longitude_header_name)
- [REGION_HEADER_NAME](README.md#region_header_name)
- [REQUEST_ID_HEADER_NAME](README.md#request_id_header_name)
### Functions
@@ -89,11 +91,25 @@ Longitude of the original client IP as calculated by Vercel Proxy.
`Const` **REGION_HEADER_NAME**: `"x-vercel-ip-country-region"`
Region of the original client IP as calculated by Vercel Proxy.
Country region of the original client IP calculated by Vercel Proxy.
See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region).
#### Defined in
[src/edge-headers.ts:24](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L24)
[src/edge-headers.ts:26](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L26)
---
### REQUEST_ID_HEADER_NAME
`Const` **REQUEST_ID_HEADER_NAME**: `"x-vercel-id"`
The request ID for each request generated by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:30](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L30)
## Functions
@@ -123,7 +139,7 @@ Returns the location information for the incoming request.
#### Defined in
[src/edge-headers.ts:80](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L80)
[src/edge-headers.ts:106](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L106)
---
@@ -149,7 +165,7 @@ Returns the IP address of the request from the headers.
#### Defined in
[src/edge-headers.ts:66](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L66)
[src/edge-headers.ts:77](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L77)
---
@@ -197,7 +213,7 @@ export default function middleware(_req: Request) {
#### Defined in
[src/middleware-helpers.ts:94](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L94)
[src/middleware-helpers.ts:145](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L145)
---
@@ -259,4 +275,4 @@ export const config = { matcher: '/api/users/:path*' };
#### Defined in
[src/middleware-helpers.ts:53](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L53)
[src/middleware-helpers.ts:101](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L101)

View File

@@ -11,6 +11,7 @@
### Properties
- [headers](ExtraResponseInit.md#headers)
- [request](ExtraResponseInit.md#request)
- [status](ExtraResponseInit.md#status)
- [statusText](ExtraResponseInit.md#statustext)
@@ -25,7 +26,19 @@ along with the response headers from the origin.
#### Defined in
[src/middleware-helpers.ts:6](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L6)
[src/middleware-helpers.ts:31](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L31)
---
### request
`Optional` **request**: [`ModifiedRequest`](ModifiedRequest.md)
Fields to rewrite for the upstream request.
#### Defined in
[src/middleware-helpers.ts:35](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L35)
---

View File

@@ -8,6 +8,7 @@ The location information of a given request.
- [city](Geo.md#city)
- [country](Geo.md#country)
- [countryRegion](Geo.md#countryregion)
- [latitude](Geo.md#latitude)
- [longitude](Geo.md#longitude)
- [region](Geo.md#region)
@@ -22,7 +23,7 @@ The city that the request originated from.
#### Defined in
[src/edge-headers.ts:41](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L41)
[src/edge-headers.ts:47](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L47)
---
@@ -34,7 +35,20 @@ The country that the request originated from.
#### Defined in
[src/edge-headers.ts:44](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L44)
[src/edge-headers.ts:50](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L50)
---
### countryRegion
`Optional` **countryRegion**: `string`
The region part of the ISO 3166-2 code of the client IP.
See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region).
#### Defined in
[src/edge-headers.ts:58](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L58)
---
@@ -46,7 +60,7 @@ The latitude of the client.
#### Defined in
[src/edge-headers.ts:50](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L50)
[src/edge-headers.ts:61](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L61)
---
@@ -58,7 +72,7 @@ The longitude of the client.
#### Defined in
[src/edge-headers.ts:53](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L53)
[src/edge-headers.ts:64](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L64)
---
@@ -70,4 +84,4 @@ The [Vercel Edge Network region](https://vercel.com/docs/concepts/edge-network/r
#### Defined in
[src/edge-headers.ts:47](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L47)
[src/edge-headers.ts:53](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L53)

View File

@@ -0,0 +1,38 @@
# Interface: ModifiedRequest
## Table of contents
### Properties
- [headers](ModifiedRequest.md#headers)
## Properties
### headers
`Optional` **headers**: `Headers`
If set, overwrites the incoming headers to the origin request.
This is useful when you want to pass data between a Middleware and a
Serverless or Edge Function.
**`Example`**
<caption>Add a `x-user-id` header and remove the `Authorization` header</caption>
```ts
import { rewrite } from '@vercel/edge';
export default async function middleware(request: Request): Promise<Response> {
const newHeaders = new Headers(request.headers);
newHeaders.set('x-user-id', 'user_123');
newHeaders.delete('authorization');
return rewrite(request.url, {
request: { headers: newHeaders },
});
}
```
#### Defined in
[src/middleware-helpers.ts:23](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L23)

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/edge",
"version": "0.0.4",
"version": "0.1.1",
"license": "MIT",
"main": "dist/index.js",
"module": "dist/index.mjs",
@@ -12,7 +12,7 @@
"build:docs": "typedoc && prettier --write docs/**/*.md docs/*.md"
},
"devDependencies": {
"@edge-runtime/jest-environment": "1.1.0-beta.7",
"@edge-runtime/jest-environment": "2.0.0",
"@types/jest": "27.4.1",
"ts-node": "8.9.1",
"tsup": "6.1.2",

View File

@@ -19,9 +19,15 @@ export const LATITUDE_HEADER_NAME = 'x-vercel-ip-latitude';
*/
export const LONGITUDE_HEADER_NAME = 'x-vercel-ip-longitude';
/**
* Region of the original client IP as calculated by Vercel Proxy.
* Country region of the original client IP calculated by Vercel Proxy.
*
* See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region).
*/
export const REGION_HEADER_NAME = 'x-vercel-ip-country-region';
/**
* The request ID for each request generated by Vercel Proxy.
*/
export const REQUEST_ID_HEADER_NAME = 'x-vercel-id';
/**
* We define a new type so this function can be reused with
@@ -46,6 +52,11 @@ export interface Geo {
/** The [Vercel Edge Network region](https://vercel.com/docs/concepts/edge-network/regions) that received the request. */
region?: string;
/** The region part of the ISO 3166-2 code of the client IP.
* See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region).
*/
countryRegion?: string;
/** The latitude of the client. */
latitude?: string;
@@ -67,6 +78,21 @@ export function ipAddress(request: Request): string | undefined {
return getHeader(request, IP_HEADER_NAME);
}
/**
* Extracts the Vercel Edge Network region name from the request ID.
*
* @param requestId The request ID (`x-vercel-id`).
* @returns The first region received the client request.
*/
function getRegionFromRequestId(requestId?: string): string | undefined {
if (!requestId) {
return 'dev1';
}
// The request ID is in the format of `region::id` or `region1:region2:...::id`.
return requestId.split(':')[0];
}
/**
* Returns the location information for the incoming request.
*
@@ -81,7 +107,8 @@ export function geolocation(request: Request): Geo {
return {
city: getHeader(request, CITY_HEADER_NAME),
country: getHeader(request, COUNTRY_HEADER_NAME),
region: getHeader(request, REGION_HEADER_NAME),
countryRegion: getHeader(request, REGION_HEADER_NAME),
region: getRegionFromRequestId(getHeader(request, REQUEST_ID_HEADER_NAME)),
latitude: getHeader(request, LATITUDE_HEADER_NAME),
longitude: getHeader(request, LONGITUDE_HEADER_NAME),
};

View File

@@ -1,9 +1,57 @@
export interface ModifiedRequest {
/**
* If set, overwrites the incoming headers to the origin request.
*
* This is useful when you want to pass data between a Middleware and a
* Serverless or Edge Function.
*
* @example
* <caption>Add a `x-user-id` header and remove the `Authorization` header</caption>
*
* ```ts
* import { rewrite } from '@vercel/edge';
* export default async function middleware(request: Request): Promise<Response> {
* const newHeaders = new Headers(request.headers);
* newHeaders.set('x-user-id', 'user_123');
* newHeaders.delete('authorization');
* return rewrite(request.url, {
* request: { headers: newHeaders }
* })
* }
* ```
*/
headers?: Headers;
}
export interface ExtraResponseInit extends Omit<ResponseInit, 'headers'> {
/**
* These headers will be sent to the user response
* along with the response headers from the origin.
*/
headers?: HeadersInit;
/**
* Fields to rewrite for the upstream request.
*/
request?: ModifiedRequest;
}
function handleMiddlewareField(
init: ExtraResponseInit | undefined,
headers: Headers
) {
if (init?.request?.headers) {
if (!(init.request.headers instanceof Headers)) {
throw new Error('request.headers must be an instance of Headers');
}
const keys = [];
for (const [key, value] of init.request.headers) {
headers.set('x-middleware-request-' + key, value);
keys.push(key);
}
headers.set('x-middleware-override-headers', keys.join(','));
}
}
/**
@@ -56,6 +104,9 @@ export function rewrite(
): Response {
const headers = new Headers(init?.headers ?? {});
headers.set('x-middleware-rewrite', String(destination));
handleMiddlewareField(init, headers);
return new Response(null, {
...init,
headers,
@@ -94,6 +145,9 @@ export function rewrite(
export function next(init?: ExtraResponseInit): Response {
const headers = new Headers(init?.headers ?? {});
headers.set('x-middleware-next', '1');
handleMiddlewareField(init, headers);
return new Response(null, {
...init,
headers,

View File

@@ -12,6 +12,7 @@ import {
LATITUDE_HEADER_NAME,
LONGITUDE_HEADER_NAME,
REGION_HEADER_NAME,
REQUEST_ID_HEADER_NAME,
} from '../src';
test('`ipAddress` returns the value from the header', () => {
@@ -24,9 +25,16 @@ test('`ipAddress` returns the value from the header', () => {
});
describe('`geolocation`', () => {
test('returns an empty object if headers are not found', () => {
test('returns an object with lots of undefined if headers are not found', () => {
const req = new Request('https://example.vercel.sh');
expect(geolocation(req)).toEqual({});
expect(geolocation(req)).toEqual({
city: undefined,
country: undefined,
countryRegion: undefined,
latitude: undefined,
longitude: undefined,
region: 'dev1',
});
});
test('reads values from headers', () => {
@@ -36,7 +44,8 @@ describe('`geolocation`', () => {
[COUNTRY_HEADER_NAME]: 'Israel',
[LATITUDE_HEADER_NAME]: '32.109333',
[LONGITUDE_HEADER_NAME]: '34.855499',
[REGION_HEADER_NAME]: 'fra1',
[REGION_HEADER_NAME]: 'TA', // https://en.wikipedia.org/wiki/ISO_3166-2:IL
[REQUEST_ID_HEADER_NAME]: 'fra1::kpwjx-123455678-c0ffee',
},
});
expect(geolocation(req)).toEqual<Geo>({
@@ -45,6 +54,48 @@ describe('`geolocation`', () => {
latitude: '32.109333',
longitude: '34.855499',
region: 'fra1',
countryRegion: 'TA',
});
});
test('reads values from headers (with a request ID containing two edge regions)', () => {
const req = new Request('https://example.vercel.sh', {
headers: {
[CITY_HEADER_NAME]: 'Tokyo',
[COUNTRY_HEADER_NAME]: 'Japan',
[LATITUDE_HEADER_NAME]: '37.1233',
[LONGITUDE_HEADER_NAME]: '30.733399',
[REGION_HEADER_NAME]: '13',
[REQUEST_ID_HEADER_NAME]: 'hnd1:iad1::kpwjx-123455678-c0ffee',
},
});
expect(geolocation(req)).toEqual<Geo>({
city: 'Tokyo',
country: 'Japan',
latitude: '37.1233',
longitude: '30.733399',
region: 'hnd1',
countryRegion: '13',
});
});
test('reads values from headers (without a request ID)', () => {
const req = new Request('https://example.vercel.sh', {
headers: {
[CITY_HEADER_NAME]: 'Tokyo',
[COUNTRY_HEADER_NAME]: 'Japan',
[LATITUDE_HEADER_NAME]: '37.1233',
[LONGITUDE_HEADER_NAME]: '30.733399',
[REGION_HEADER_NAME]: '13',
},
});
expect(geolocation(req)).toEqual<Geo>({
city: 'Tokyo',
country: 'Japan',
latitude: '37.1233',
longitude: '30.733399',
region: 'dev1',
countryRegion: '13',
});
});
});

View File

@@ -22,6 +22,32 @@ describe('rewrite', () => {
},
});
});
test('receives new request headers', () => {
const headers = new Headers();
headers.set('x-from-middleware1', 'hello1');
headers.set('x-from-middleware2', 'hello2');
const resp = rewrite(new URL('https://example.vercel.sh/'), {
headers: {
'x-custom-header': 'custom-value',
},
request: { headers },
});
expect({
status: resp.status,
headers: Object.fromEntries(resp.headers),
}).toMatchObject({
status: 200,
headers: {
'x-middleware-rewrite': 'https://example.vercel.sh/',
'x-custom-header': 'custom-value',
'x-middleware-override-headers':
'x-from-middleware1,x-from-middleware2',
'x-middleware-request-x-from-middleware2': 'hello2',
'x-middleware-request-x-from-middleware1': 'hello1',
},
});
});
});
describe('next', () => {
@@ -42,4 +68,30 @@ describe('next', () => {
},
});
});
test('receives new request headers', () => {
const headers = new Headers();
headers.set('x-from-middleware1', 'hello1');
headers.set('x-from-middleware2', 'hello2');
const resp = next({
headers: {
'x-custom-header': 'custom-value',
},
request: { headers },
});
expect({
status: resp.status,
headers: Object.fromEntries(resp.headers),
}).toMatchObject({
status: 200,
headers: {
'x-middleware-next': '1',
'x-custom-header': 'custom-value',
'x-middleware-override-headers':
'x-from-middleware1,x-from-middleware2',
'x-middleware-request-x-from-middleware2': 'hello2',
'x-middleware-request-x-from-middleware1': 'hello1',
},
});
});
});

View File

@@ -0,0 +1,13 @@
/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
coverageThreshold: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
};

View File

@@ -0,0 +1,25 @@
{
"name": "@vercel/error-utils",
"version": "1.0.2",
"description": "A collection of error utilities for vercel/vercel",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/vercel/vercel.git",
"directory": "packages/error-utils"
},
"scripts": {
"build": "tsc",
"test": "jest --coverage --env node --verbose",
"test-unit": "yarn test"
},
"license": "MIT",
"devDependencies": {
"@types/jest": "29.2.1",
"@types/node": "16.11.7",
"jest": "29.2.2",
"ts-jest": "29.0.3",
"typescript": "^4.8.4"
}
}

View File

@@ -25,7 +25,6 @@ export const isError = (error: unknown): error is Error => {
// Walk the prototype tree until we find a matching object.
while (error) {
if (Object.prototype.toString.call(error) === '[object Error]') return true;
// eslint-disable-next-line no-param-reassign -- TODO: Fix eslint error following @vercel/style-guide migration
error = Object.getPrototypeOf(error);
}

148
packages/error-utils/test/index.test.ts vendored Normal file
View File

@@ -0,0 +1,148 @@
import fs from 'node:fs';
import {
isObject,
isError,
isErrnoException,
isErrorLike,
normalizeError,
isSpawnError,
errorToString,
} from '../src';
const ARRAY: any[] = [];
const BIGINT = 1n;
const BOOLEAN = true;
const FUNCTION = () => {};
const NULL = null;
const NUMBER = 0;
const OBJECT = {};
const STRING = '';
const SYMBOL = Symbol('');
const UNDEFINED = undefined;
class CLASS {} // `CLASS` is a function and `new CLASS()` is an Object
test('isObject returns true for objects only', () => {
for (const item of [ARRAY, new CLASS(), OBJECT]) {
expect(isObject(item)).toBe(true);
}
for (const item of [
BIGINT,
BOOLEAN,
CLASS,
FUNCTION,
NULL,
NUMBER,
STRING,
SYMBOL,
UNDEFINED,
]) {
expect(isObject(item)).toBe(false);
}
});
test('isError returns true for Error instances only', () => {
for (const error of [
new Error(),
new EvalError(),
new RangeError(),
new ReferenceError(),
new SyntaxError(),
new TypeError(),
new URIError(),
]) {
expect(isError(error)).toBe(true);
}
for (const item of [
ARRAY,
BIGINT,
BOOLEAN,
CLASS,
new CLASS(),
FUNCTION,
NULL,
NUMBER,
OBJECT,
STRING,
SYMBOL,
UNDEFINED,
]) {
expect(isError(item)).toBe(false);
}
});
test('isError returns true for objects with a nested Error prototype', () => {
class Foo {}
const err = new Error();
Object.setPrototypeOf(err, Foo.prototype);
expect(isError(err)).toBe(true);
});
test('isErrnoException returns true for NodeJS.ErrnoException only', () => {
try {
fs.statSync('./i-definitely-do-not-exist');
fail();
} catch (err) {
expect(isErrnoException(err)).toBe(true);
}
});
test('isErrorLike returns true when object is like an error', () => {
expect(isErrorLike(new Error())).toBe(true);
expect(isErrorLike({ message: '' })).toBe(true);
expect(isErrorLike({})).toBe(false);
});
describe('errorToString', () => {
const message = 'message';
test('return `message` when first argument is an error', () => {
expect(errorToString(new Error(message))).toStrictEqual(message);
});
test('returns `message` when first argument is error like', () => {
expect(errorToString({ message })).toStrictEqual(message);
});
test('returns first argument when it is a string', () => {
expect(errorToString(message)).toStrictEqual(message);
});
test('returns second argument when first argument is not an error, error like, nor a string', () => {
expect(errorToString(null, message)).toStrictEqual(message);
});
test('returns default fallback message when first argument is not an error, error like, nor a string, and the second argument is not provided', () => {
expect(errorToString(null)).toStrictEqual('An unknown error has ocurred.');
});
});
describe('normalizeError', () => {
const message = 'message';
test('returns first argument if it is an error', () => {
expect(normalizeError(new Error(message))).toStrictEqual(
new Error(message)
);
});
test('returns a new error if argument is not error like', () => {
expect(normalizeError(message)).toStrictEqual(new Error(message));
});
test('returns a new error if argument is not error like', () => {
expect(normalizeError({ message })).toStrictEqual(new Error(message));
});
test('returns a new error with fallback message if argument is not error like nor a string.', () => {
expect(normalizeError(null)).toStrictEqual(
new Error('An unknown error has ocurred.')
);
});
test('returns an Error with the input object assigned to it', () => {
expect(normalizeError({ message, prop: 'value' })).toStrictEqual(
Object.assign(new Error(message), { prop: 'value' })
);
});
});
test('isSpawnError', () => {
const spawnError = new Error('spawn error');
Object.assign(spawnError, {
code: 'SPAWN_ERROR',
spawnargs: ['a', 'b', 'c'],
});
expect(isSpawnError(spawnError)).toBe(true);
expect(isSpawnError(new Error('not spawn error'))).toBe(false);
});

View File

@@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"types": ["node", "jest"]
},
"include": ["*.test.ts"]
}

View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"lib": ["ES2020"],
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "./dist",
"types": ["node", "jest"],
"strict": true,
"sourceMap": true,
"target": "ES2020"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "1.1.6",
"version": "1.1.10",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [
@@ -21,7 +21,7 @@
"@types/js-yaml": "3.12.1",
"@types/node": "12.0.4",
"@types/node-fetch": "2.5.8",
"@vercel/routing-utils": "2.0.2",
"@vercel/routing-utils": "2.1.1",
"ajv": "6.12.2",
"typescript": "4.3.4"
}

View File

@@ -1792,9 +1792,15 @@ export const frameworks = [
{
path: 'sanity.config.js',
},
{
path: 'sanity.config.jsx',
},
{
path: 'sanity.config.ts',
},
{
path: 'sanity.config.tsx',
},
],
},
settings: {
@@ -1834,10 +1840,13 @@ export const frameworks = [
website: 'https://hydrogen.shopify.dev',
useRuntime: { src: 'package.json', use: '@vercel/hydrogen' },
detectors: {
every: [
some: [
{
path: 'hydrogen.config.js',
},
{
path: 'hydrogen.config.ts',
},
],
},
settings: {

View File

@@ -0,0 +1,8 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M49.8779 58.813L37.2099 39.0114L37.1908 55.6489L15.3588 18.8931H0V80.9427H15.187L15.2366 44.2328L36.8435 79.2977L49.8779 58.813Z" fill="white"/>
<path d="M37.2137 34.8435H52.3817L52.416 34.6679V18.8931H37.229L37.2099 34.6679L37.2137 34.8435Z" fill="white"/>
<path d="M75.8244 52.0458C72.1908 51.9769 68.9079 54.2048 67.6298 57.6069C69.7576 54.4249 73.809 53.1463 77.3779 54.5305C79.0267 55.2023 81.1947 56.2863 82.9122 55.7061C81.2787 53.4137 78.6393 52.0506 75.8244 52.0458Z" fill="#96D8E9"/>
<path d="M93.0343 59.5114C93.0343 57.6527 92.0229 57.2214 89.9237 56.458C88.3664 55.916 86.5992 55.3053 85.313 53.6259C85.0611 53.2939 84.8282 52.9198 84.5802 52.5229C84.0244 51.4948 83.2707 50.5868 82.3626 49.8511C81.1374 48.9504 79.5305 48.5114 77.4466 48.5114C73.265 48.5144 69.4978 51.0384 67.9046 54.9046C69.9187 52.1099 73.2659 50.5956 76.6947 50.928C80.1235 51.2605 83.1175 53.3895 84.5572 56.5191C85.2766 57.706 86.6952 58.2728 88.0343 57.9084C90.084 57.4695 89.9427 59.4084 93.0382 60.0649L93.0343 59.5114Z" fill="#48C4E5"/>
<path d="M99.7939 59.1145V59.0725C99.7481 46.7672 89.7595 36.2405 77.4466 36.2405C69.9264 36.2352 62.9091 40.0172 58.7786 46.3015L58.6985 46.1641L52.3779 36.2405L37.2137 36.2404L51.6221 58.8053L37.5611 80.9427H52.2901L58.6985 71.0992L65.2214 80.9427H79.9542L66.5954 59.9198C66.3737 59.5102 66.2533 59.0535 66.2443 58.5878C66.2433 55.6185 67.4224 52.7705 69.522 50.6708C71.6216 48.5712 74.4696 47.3921 77.4389 47.3931C83.6298 47.3931 84.6641 51.0954 86 52.8359C88.6374 56.271 93.9046 54.7672 93.9046 59.2481C93.9203 60.2981 94.4949 61.2599 95.4121 61.7713C96.3292 62.2827 97.4495 62.266 98.351 61.7274C99.2525 61.1889 99.7981 60.2103 99.7824 59.1603V59.1145H99.7939Z" fill="white"/>
<path d="M99.8092 62.2824C99.9589 63.3289 99.8107 64.3964 99.3817 65.3626C98.542 67.3053 97.1412 66.6565 97.1412 66.6565C97.1412 66.6565 95.9351 66.0687 96.6679 64.9427C97.4809 63.687 99.0878 63.8321 99.8092 62.2824Z" fill="#022F56"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -0,0 +1,35 @@
<svg width="100" height="125" viewBox="0 0 100 125" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M40 0.0374222C25.8531 -0.46154 11.7078 4.02912 0 13.5094L7.80471 18C17.0735 11.0146 28.7797 8.02081 40 8.51977V0.0374222Z" fill="url(#paint0_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M82 19.1224C72.2699 8.83673 59.6211 2.46939 46 1V9.32653C57.6764 11.2857 67.8927 16.6735 76.1626 25L82 19.1224Z" fill="url(#paint1_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M100 60C99.5253 47 94.7753 34 86.701 23L81 29C87.6503 38.5001 91.451 49 91.9256 60H100Z" fill="url(#paint2_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M86.701 102C94.7753 91.2973 99.5253 78.6487 100 66H91.9256C91.451 76.7027 87.6503 86.9189 81 96.1621L86.701 102Z" fill="url(#paint3_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M46 124C59.135 122.531 72.2699 116.163 82 105.878L76.1626 100C67.8927 108.816 57.1886 114.204 46 115.673V124Z" fill="url(#paint4_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 111.491C11.7078 120.971 25.8531 125.461 40 124.963V116.48C28.7797 116.979 17.561 113.985 7.80471 107L0 111.491Z" fill="url(#paint5_linear_8_11)"/>
<path d="M19.9222 63C19.9222 65.6382 20.4182 68.0713 21.4084 70.299C22.3987 72.5267 23.7403 74.4614 25.4299 76.103C27.1196 77.7446 29.0732 79.0343 31.2875 79.9724C33.5605 80.8518 35.9495 81.2915 38.4563 81.2915C40.963 81.2915 43.3235 80.8518 45.5377 79.9724C47.8108 79.0343 49.793 77.7446 51.4826 76.103C53.2311 74.4614 54.6014 72.5267 55.5916 70.299C56.5819 68.0713 57.0778 65.6382 57.0778 63C57.0778 60.3618 56.5819 57.9288 55.5916 55.701C54.6014 53.4733 53.2311 51.5386 51.4826 49.897C49.793 48.2554 47.8108 46.995 45.5377 46.1156C43.3235 45.1775 40.963 44.7085 38.4563 44.7085C35.9495 44.7085 33.5605 45.1775 31.2875 46.1156C29.0732 46.995 27.1196 48.2554 25.4299 49.897C23.7403 51.5386 22.3987 53.4733 21.4084 55.701C20.4182 57.9288 19.9222 60.3618 19.9222 63ZM2 63C2 58.0754 2.90283 53.5025 4.71018 49.2814C6.51753 45.0016 9.02263 41.2789 12.2288 38.1131C15.4349 34.9472 19.2513 32.4849 23.6814 30.7261C28.1687 28.9087 33.0947 28 38.4563 28C43.7606 28 48.6565 28.9087 53.1437 30.7261C57.631 32.4849 61.4777 34.9472 64.6838 38.1131C67.9471 41.2789 70.4825 45.0016 72.2899 49.2814C74.0972 53.5025 75 58.0754 75 63C75 67.9246 74.0972 72.5267 72.2899 76.8065C70.4825 81.0276 67.9471 84.7211 64.6838 87.8869C61.4777 91.0528 57.631 93.5443 53.1437 95.3618C48.6565 97.1206 43.7606 98 38.4563 98C33.0947 98 28.1687 97.1206 23.6814 95.3618C19.2513 93.5443 15.4349 91.0528 12.2288 87.8869C9.02263 84.7211 6.51753 81.0276 4.71018 76.8065C2.90283 72.5267 2 67.9246 2 63Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_8_11" x1="49.5117" y1="-6.94291e-08" x2="49.5117" y2="125.813" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint1_linear_8_11" x1="50.135" y1="0.473494" x2="50.135" y2="123.975" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint2_linear_8_11" x1="51.7888" y1="-0.0373877" x2="51.7888" y2="126.037" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint3_linear_8_11" x1="51.7888" y1="1.74712" x2="51.7888" y2="124.415" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint4_linear_8_11" x1="50.135" y1="1.02464" x2="50.135" y2="124.526" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint5_linear_8_11" x1="49.5117" y1="-0.812955" x2="49.5117" y2="125" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,35 @@
<svg width="100" height="125" viewBox="0 0 100 125" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M40 0.0374222C25.8531 -0.46154 11.7078 4.02912 0 13.5094L7.80471 18C17.0735 11.0146 28.7797 8.02081 40 8.51977V0.0374222Z" fill="url(#paint0_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M82 19.1224C72.2699 8.83673 59.6211 2.46939 46 1V9.32653C57.6764 11.2857 67.8927 16.6735 76.1626 25L82 19.1224Z" fill="url(#paint1_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M100 60C99.5253 47 94.7753 34 86.701 23L81 29C87.6503 38.5001 91.451 49 91.9256 60H100Z" fill="url(#paint2_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M86.701 102C94.7753 91.2973 99.5253 78.6487 100 66H91.9256C91.451 76.7027 87.6503 86.9189 81 96.1621L86.701 102Z" fill="url(#paint3_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M46 124C59.135 122.531 72.2699 116.163 82 105.878L76.1626 100C67.8927 108.816 57.1886 114.204 46 115.673V124Z" fill="url(#paint4_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 111.491C11.7078 120.971 25.8531 125.461 40 124.963V116.48C28.7797 116.979 17.561 113.985 7.80471 107L0 111.491Z" fill="url(#paint5_linear_8_11)"/>
<path d="M19.9222 63C19.9222 65.6382 20.4182 68.0713 21.4084 70.299C22.3987 72.5267 23.7403 74.4614 25.4299 76.103C27.1196 77.7446 29.0732 79.0343 31.2875 79.9724C33.5605 80.8518 35.9495 81.2915 38.4563 81.2915C40.963 81.2915 43.3235 80.8518 45.5377 79.9724C47.8108 79.0343 49.793 77.7446 51.4826 76.103C53.2311 74.4614 54.6014 72.5267 55.5916 70.299C56.5819 68.0713 57.0778 65.6382 57.0778 63C57.0778 60.3618 56.5819 57.9288 55.5916 55.701C54.6014 53.4733 53.2311 51.5386 51.4826 49.897C49.793 48.2554 47.8108 46.995 45.5377 46.1156C43.3235 45.1775 40.963 44.7085 38.4563 44.7085C35.9495 44.7085 33.5605 45.1775 31.2875 46.1156C29.0732 46.995 27.1196 48.2554 25.4299 49.897C23.7403 51.5386 22.3987 53.4733 21.4084 55.701C20.4182 57.9288 19.9222 60.3618 19.9222 63ZM2 63C2 58.0754 2.90283 53.5025 4.71018 49.2814C6.51753 45.0016 9.02263 41.2789 12.2288 38.1131C15.4349 34.9472 19.2513 32.4849 23.6814 30.7261C28.1687 28.9087 33.0947 28 38.4563 28C43.7606 28 48.6565 28.9087 53.1437 30.7261C57.631 32.4849 61.4777 34.9472 64.6838 38.1131C67.9471 41.2789 70.4825 45.0016 72.2899 49.2814C74.0972 53.5025 75 58.0754 75 63C75 67.9246 74.0972 72.5267 72.2899 76.8065C70.4825 81.0276 67.9471 84.7211 64.6838 87.8869C61.4777 91.0528 57.631 93.5443 53.1437 95.3618C48.6565 97.1206 43.7606 98 38.4563 98C33.0947 98 28.1687 97.1206 23.6814 95.3618C19.2513 93.5443 15.4349 91.0528 12.2288 87.8869C9.02263 84.7211 6.51753 81.0276 4.71018 76.8065C2.90283 72.5267 2 67.9246 2 63Z" fill="black"/>
<defs>
<linearGradient id="paint0_linear_8_11" x1="49.5117" y1="-6.94291e-08" x2="49.5117" y2="125.813" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint1_linear_8_11" x1="50.135" y1="0.473494" x2="50.135" y2="123.975" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint2_linear_8_11" x1="51.7888" y1="-0.0373877" x2="51.7888" y2="126.037" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint3_linear_8_11" x1="51.7888" y1="1.74712" x2="51.7888" y2="124.415" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint4_linear_8_11" x1="50.135" y1="1.02464" x2="50.135" y2="124.526" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint5_linear_8_11" x1="49.5117" y1="-0.812955" x2="49.5117" y2="125" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "3.4.1",
"version": "3.4.7",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -19,8 +19,8 @@
"test-unit": "yarn test"
},
"dependencies": {
"@vercel/frameworks": "1.1.6",
"@vercel/routing-utils": "2.0.2",
"@vercel/frameworks": "1.1.10",
"@vercel/routing-utils": "2.1.1",
"glob": "8.0.3",
"js-yaml": "4.1.0",
"json5": "2.2.1",

View File

@@ -14,11 +14,14 @@ import type { Framework } from '@vercel/frameworks';
*
*/
export const monorepoManagers: Array<
Omit<Framework, 'description' | 'logo' | 'settings' | 'getOutputDirName'>
Omit<Framework, 'description' | 'getOutputDirName'>
> = [
{
name: 'Turborepo',
slug: 'turbo',
logo: 'https://api-frameworks.vercel.sh/monorepo-logos/turborepo.svg',
darkModeLogo:
'https://api-frameworks.vercel.sh/monorepo-logos/turborepo-dark.svg',
detectors: {
some: [
{
@@ -30,10 +33,26 @@ export const monorepoManagers: Array<
},
],
},
settings: {
buildCommand: {
placeholder: 'Turborepo default',
value: null,
},
outputDirectory: {
value: null,
},
installCommand: {
value: null,
},
devCommand: {
value: null,
},
},
},
{
name: 'Nx',
slug: 'nx',
logo: 'https://api-frameworks.vercel.sh/monorepo-logos/nx.svg',
detectors: {
every: [
{
@@ -41,10 +60,26 @@ export const monorepoManagers: Array<
},
],
},
settings: {
buildCommand: {
placeholder: 'Nx default',
value: null,
},
outputDirectory: {
value: null,
},
installCommand: {
value: null,
},
devCommand: {
value: null,
},
},
},
{
name: 'Rush',
slug: 'rush',
logo: 'https://api-frameworks.vercel.sh/monorepo-logos/rush.svg',
detectors: {
every: [
{
@@ -52,6 +87,21 @@ export const monorepoManagers: Array<
},
],
},
settings: {
buildCommand: {
placeholder: 'Rush default',
value: null,
},
outputDirectory: {
value: null,
},
installCommand: {
placeholder: 'Rush default',
},
devCommand: {
value: null,
},
},
},
];

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "2.2.12",
"version": "2.2.15",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -35,7 +35,7 @@
"@types/jest": "28.1.6",
"@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0",
"@vercel/build-utils": "5.5.4",
"@vercel/build-utils": "5.5.7",
"@vercel/ncc": "0.24.0",
"async-retry": "1.3.1",
"execa": "^1.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/hydrogen",
"version": "0.0.25",
"version": "0.0.28",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -21,7 +21,8 @@
"devDependencies": {
"@types/jest": "27.5.1",
"@types/node": "*",
"@vercel/build-utils": "5.5.4",
"@vercel/build-utils": "5.5.7",
"@vercel/static-config": "2.0.5",
"typescript": "4.6.4"
}
}

View File

@@ -15,6 +15,8 @@ import {
scanParentDirs,
} from '@vercel/build-utils';
import type { BuildV2, PackageJson } from '@vercel/build-utils';
import { getConfig } from '@vercel/static-config';
import { Project } from 'ts-morph';
export const build: BuildV2 = async ({
entrypoint,
@@ -118,6 +120,15 @@ export const build: BuildV2 = async ({
deploymentTarget: 'v8-worker',
entrypoint: 'index.js',
files: edgeFunctionFiles,
regions: (() => {
try {
const project = new Project();
const config = getConfig(project, edgeFunctionFiles['index.js'].fsPath);
return config?.regions;
} catch {
return undefined;
}
})(),
});
// The `index.html` file is a template, but we want to serve the

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/next",
"version": "3.2.3",
"version": "3.2.8",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
@@ -44,9 +44,9 @@
"@types/semver": "6.0.0",
"@types/text-table": "0.2.1",
"@types/webpack-sources": "3.2.0",
"@vercel/build-utils": "5.5.4",
"@vercel/build-utils": "5.5.7",
"@vercel/nft": "0.22.1",
"@vercel/routing-utils": "2.0.2",
"@vercel/routing-utils": "2.1.1",
"async-sema": "3.0.1",
"buffer-crc32": "0.2.13",
"cheerio": "1.0.0-rc.10",

View File

@@ -14,7 +14,7 @@ import {
Files,
BuildResultV2Typical as BuildResult,
} from '@vercel/build-utils';
import { Route, RouteWithHandle, RouteWithSrc } from '@vercel/routing-utils';
import { Route, RouteWithHandle } from '@vercel/routing-utils';
import { MAX_AGE_ONE_YEAR } from '.';
import {
NextRequiredServerFilesManifest,
@@ -218,7 +218,8 @@ export async function serverBuild({
nonLambdaSsgPages,
route,
routesManifest.pages404,
routesManifest
routesManifest,
appDir
);
if (result && result.static404Page) {
@@ -337,6 +338,7 @@ export async function serverBuild({
const apiPages: string[] = [];
const nonApiPages: string[] = [];
const streamingPages: string[] = [];
lambdaPageKeys.forEach(page => {
if (
@@ -358,6 +360,8 @@ export async function serverBuild({
if (pageMatchesApi(page)) {
apiPages.push(page);
} else if (appDir && lambdaAppPaths[page]) {
streamingPages.push(page);
} else {
nonApiPages.push(page);
}
@@ -545,7 +549,12 @@ export async function serverBuild({
const compressedPages: {
[page: string]: PseudoFile;
} = {};
const mergedPageKeys = [...nonApiPages, ...apiPages, ...internalPages];
const mergedPageKeys = [
...nonApiPages,
...streamingPages,
...apiPages,
...internalPages,
];
const traceCache = {};
const getOriginalPagePath = (page: string) => {
@@ -703,6 +712,27 @@ export async function serverBuild({
pageExtensions,
});
const streamingPageLambdaGroups = await getPageLambdaGroups({
entryPath: requiredServerFilesManifest.appDir || entryPath,
config,
pages: streamingPages,
prerenderRoutes,
pageTraces,
compressedPages,
tracedPseudoLayer: tracedPseudoLayer.pseudoLayer,
initialPseudoLayer,
lambdaCompressedByteLimit,
initialPseudoLayerUncompressed: uncompressedInitialSize,
internalPages,
pageExtensions,
});
for (const group of streamingPageLambdaGroups) {
if (!group.isPrerenders) {
group.isStreaming = true;
}
}
const apiLambdaGroups = await getPageLambdaGroups({
entryPath: requiredServerFilesManifest.appDir || entryPath,
config,
@@ -732,13 +762,23 @@ export async function serverBuild({
pseudoLayerBytes: group.pseudoLayerBytes,
uncompressedLayerBytes: group.pseudoLayerUncompressedBytes,
})),
streamingPageLambdaGroups: streamingPageLambdaGroups.map(group => ({
pages: group.pages,
isPrerender: group.isPrerenders,
pseudoLayerBytes: group.pseudoLayerBytes,
uncompressedLayerBytes: group.pseudoLayerUncompressedBytes,
})),
nextServerLayerSize: initialPseudoLayer.pseudoLayerBytes,
},
null,
2
)
);
const combinedGroups = [...pageLambdaGroups, ...apiLambdaGroups];
const combinedGroups = [
...pageLambdaGroups,
...streamingPageLambdaGroups,
...apiLambdaGroups,
];
await detectLambdaLimitExceeding(
combinedGroups,
@@ -831,6 +871,7 @@ export async function serverBuild({
memory: group.memory,
runtime: nodeVersion.runtime,
maxDuration: group.maxDuration,
isStreaming: group.isStreaming,
});
for (const page of group.pages) {
@@ -963,59 +1004,6 @@ export async function serverBuild({
const { staticFiles, publicDirectoryFiles, staticDirectoryFiles } =
await getStaticFiles(entryPath, entryDirectory, outputDirectory);
const notFoundPreviewRoutes: RouteWithSrc[] = [];
if (prerenderManifest.notFoundRoutes?.length > 0 && canUsePreviewMode) {
// we combine routes into one src here to reduce the number of needed
// routes since only the status is being modified and we don't want
// to exceed the routes limit
const starterRouteSrc = `^${
entryDirectory !== '.' ? path.posix.join('/', entryDirectory, '()') : '()'
}`;
let currentRouteSrc = starterRouteSrc;
const pushRoute = (src: string) => {
notFoundPreviewRoutes.push({
src,
missing: [
{
type: 'cookie',
key: '__prerender_bypass',
value: prerenderManifest.bypassToken || undefined,
},
{
type: 'cookie',
key: '__next_preview_data',
},
],
status: 404,
});
};
for (let i = 0; i < prerenderManifest.notFoundRoutes.length; i++) {
const route = prerenderManifest.notFoundRoutes[i];
const isLastRoute = i === prerenderManifest.notFoundRoutes.length - 1;
if (prerenderManifest.staticRoutes[route]?.initialRevalidate === false) {
if (currentRouteSrc.length + route.length + 1 >= 4000) {
pushRoute(currentRouteSrc);
currentRouteSrc = starterRouteSrc;
}
// add to existing route src if src length limit isn't reached
currentRouteSrc = `${currentRouteSrc.substring(
0,
currentRouteSrc.length - 1
)}${
currentRouteSrc[currentRouteSrc.length - 2] === '(' ? '' : '|'
}${route}/?)`;
if (isLastRoute) {
pushRoute(currentRouteSrc);
}
}
}
}
const normalizeNextDataRoute = (isOverride = false) => {
return isNextDataServerResolving
? [
@@ -1142,7 +1130,8 @@ export async function serverBuild({
if (lambdas[route]) {
lambdas[`${route}.rsc`] = lambdas[route];
} else if (edgeFunctions[route]) {
}
if (edgeFunctions[route]) {
edgeFunctions[`${route}.rsc`] = edgeFunctions[route];
}
}
@@ -1326,10 +1315,6 @@ export async function serverBuild({
...beforeFilesRewrites,
// ensure prerender's for notFound: true static routes
// have 404 status code when not in preview mode
...notFoundPreviewRoutes,
// Make sure to 404 for the /404 path itself
...(i18n
? [
@@ -1400,6 +1385,17 @@ export async function serverBuild({
...(appDir
? [
{
src: `^${path.posix.join('/', entryDirectory, '/')}`,
has: [
{
type: 'header',
key: '__rsc__',
},
],
dest: path.posix.join('/', entryDirectory, '/index.rsc'),
check: true,
},
{
src: `^${path.posix.join('/', entryDirectory, '/(.*)$')}`,
has: [
@@ -1509,18 +1505,31 @@ export async function serverBuild({
dynamicRoutes
.map(route => {
route = Object.assign({}, route);
let normalizedSrc = route.src;
if (routesManifest.basePath) {
normalizedSrc = normalizedSrc.replace(
new RegExp(
`\\^${escapeStringRegexp(routesManifest.basePath)}`
),
'^'
);
}
route.src = path.posix.join(
'^/',
entryDirectory,
'_next/data/',
escapedBuildId,
route.src.replace(/(^\^|\$$)/g, '') + '.json$'
normalizedSrc
.replace(/\^\(\?:\/\(\?</, '(?:(?<')
.replace(/(^\^|\$$)/g, '') + '.json$'
);
const { pathname, search } = new URL(
route.dest || '/',
'http://n'
);
const parsedDestination = new URL(route.dest || '/', 'http://n');
let pathname = parsedDestination.pathname;
const search = parsedDestination.search;
let isPrerender = !!prerenders[path.join('./', pathname)];
if (routesManifest.i18n) {
@@ -1537,9 +1546,17 @@ export async function serverBuild({
}
if (isPrerender) {
route.dest = `/_next/data/${buildId}${pathname}.json${
search || ''
}`;
if (routesManifest.basePath) {
pathname = pathname.replace(
new RegExp(
`^${escapeStringRegexp(routesManifest.basePath)}`
),
''
);
}
route.dest = `${
routesManifest.basePath || ''
}/_next/data/${buildId}${pathname}.json${search || ''}`;
}
return route;
})

View File

@@ -748,6 +748,7 @@ export async function createPseudoLayer(files: {
interface CreateLambdaFromPseudoLayersOptions extends LambdaOptionsWithFiles {
layers: PseudoLayer[];
isStreaming?: boolean;
}
// measured with 1, 2, 5, 10, and `os.cpus().length || 5`
@@ -757,6 +758,7 @@ const createLambdaSema = new Sema(1);
export async function createLambdaFromPseudoLayers({
files: baseFiles,
layers,
isStreaming,
...lambdaOptions
}: CreateLambdaFromPseudoLayersOptions) {
await createLambdaSema.acquire();
@@ -791,6 +793,11 @@ export async function createLambdaFromPseudoLayers({
return new NodejsLambda({
...lambdaOptions,
...(isStreaming
? {
experimentalResponseStreaming: true,
}
: {}),
files,
shouldAddHelpers: false,
shouldAddSourcemapSupport: false,
@@ -1273,6 +1280,7 @@ export type LambdaGroup = {
pages: string[];
memory?: number;
maxDuration?: number;
isStreaming?: boolean;
isPrerenders?: boolean;
pseudoLayer: PseudoLayer;
pseudoLayerBytes: number;
@@ -1601,16 +1609,19 @@ export const onPrerenderRouteInitial = (
nonLambdaSsgPages: Set<string>,
routeKey: string,
hasPages404: boolean,
routesManifest?: RoutesManifest
routesManifest?: RoutesManifest,
appDir?: string | null
) => {
let static404Page: string | undefined;
let static500Page: string | undefined;
// Get the route file as it'd be mounted in the builder output
const pr = prerenderManifest.staticRoutes[routeKey];
const { initialRevalidate, srcRoute } = pr;
const { initialRevalidate, srcRoute, dataRoute } = pr;
const route = srcRoute || routeKey;
const isAppPathRoute = appDir && dataRoute?.endsWith('.rsc');
const routeNoLocale = routesManifest?.i18n
? normalizeLocalePath(routeKey, routesManifest.i18n.locales).pathname
: routeKey;
@@ -1626,6 +1637,9 @@ export const onPrerenderRouteInitial = (
}
if (
// App paths must be Prerenders to ensure Vary header is
// correctly added
!isAppPathRoute &&
initialRevalidate === false &&
(!canUsePreviewMode || (hasPages404 && routeNoLocale === '/404')) &&
!prerenderManifest.fallbackRoutes[route] &&
@@ -1836,14 +1850,26 @@ export const onPrerenderRoute =
),
});
const outputPathPage = normalizeIndexOutput(
path.posix.join(entryDirectory, routeFileNoExt),
isServerMode
);
if (isAppPathRoute) {
// for literal index routes we need to append an additional /index
// due to the proxy's normalizing for /index routes
if (routeKey !== '/index' && routeKey.endsWith('/index')) {
routeKey = `${routeKey}/index`;
routeFileNoExt = routeKey;
origRouteFileNoExt = routeKey;
}
}
let outputPathPage = path.posix.join(entryDirectory, routeFileNoExt);
if (!isAppPathRoute) {
outputPathPage = normalizeIndexOutput(outputPathPage, isServerMode);
}
const outputPathPageOrig = path.posix.join(
entryDirectory,
origRouteFileNoExt
);
let lambda: undefined | Lambda;
let outputPathData = path.posix.join(entryDirectory, dataRoute);
@@ -1887,7 +1913,7 @@ export const onPrerenderRoute =
lambda = lambdas[outputSrcPathPage];
}
if (!isNotFound && initialRevalidate === false) {
if (!isAppPathRoute && !isNotFound && initialRevalidate === false) {
if (htmlFsRef == null || jsonFsRef == null) {
throw new NowBuildError({
code: 'NEXT_HTMLFSREF_JSONFSREF',
@@ -1973,6 +1999,19 @@ export const onPrerenderRoute =
fallback: htmlFsRef,
group: prerenderGroup,
bypassToken: prerenderManifest.bypassToken,
...(isNotFound
? {
initialStatus: 404,
}
: {}),
...(isAppPathRoute
? {
initialHeaders: {
vary: '__rsc__, __next_router_state_tree__, __next_router_prefetch__',
},
}
: {}),
});
prerenders[outputPathData] = new Prerender({
expiration: initialRevalidate,
@@ -1981,6 +2020,21 @@ export const onPrerenderRoute =
fallback: jsonFsRef,
group: prerenderGroup,
bypassToken: prerenderManifest.bypassToken,
...(isNotFound
? {
initialStatus: 404,
}
: {}),
...(isAppPathRoute
? {
initialHeaders: {
'content-type': 'application/octet-stream',
vary: '__rsc__, __next_router_state_tree__, __next_router_prefetch__',
},
}
: {}),
});
++prerenderGroup;
@@ -2391,6 +2445,12 @@ export async function getMiddlewareBundle({
shortPath.replace(/^app\//, '').replace(/(^|\/)page$/, '') || 'index';
}
if (routesManifest?.basePath) {
shortPath = path.posix
.join(routesManifest.basePath, shortPath)
.replace(/^\//, '');
}
worker.edgeFunction.name = shortPath;
source.edgeFunctions[shortPath] = worker.edgeFunction;

View File

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

View File

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

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