Compare commits

..

83 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
Nathan Rajlich
45bd855250 Publish Stable
- @vercel/build-utils@5.5.4
 - vercel@28.4.7
 - @vercel/client@12.2.12
 - @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
2022-10-05 12:41:42 -07:00
Nathan Rajlich
49de8ad9a0 [node] Update edge-runtime to v1.1.0-beta.37 (#8687)
Fixes error:

```
ENOENT: no such file or directory, open 'querystring'
```

Unfortunately this issue would only manifest when installed externally. I.e. our tests didn't catch this since the `querystring` module is presumably present in the monorepo.
2022-10-05 19:33:53 +00:00
JJ Kasper
a1ea56fd67 [next] Update max route src check for generated route (#8689)
### Related Issues

This reduces the max length we check for when generating routes to ensure we stay under the 4096 limit after normalizing. 

x-ref: https://github.com/vercel/customer-issues/issues/779

### 📋 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 19:18:41 +00:00
JJ Kasper
e88addc9ed [next] Fix legacy pages/404 case (#8682)
### Related Issues

This ensures we handle the case were a lambda isn't present for `pages/404.js` with `getStaticProps` which can occur in older Next.js versions e.g. `v9.5.5`. This also adds a regression test for this specific version to ensure it is working as expected. 

x-ref: https://github.com/vercel/vercel/pull/8663
Fixes: [slack thread](https://vercel.slack.com/archives/C03DQ3QFV7C/p1664945825621409)

### 📋 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 14:21:10 +00:00
Peter van der Zee
5d50013f93 [build-utils] Allow file-ref sema to be controlled through env flag (#8681)
My IDE tells me `process` is unknown but mentions something about package.json so that may just be a superficial issue. I guess CI/CD will tell me soon enough.

This adds an env flag to override the file ref sema's so we can experiment with setting a higher sema.

One potential problem I'm seeing is that this is a generic sema for all the things that use this class. Not sure if that's going to work out as intended but in that case we'll have to find a different way :)
2022-10-05 13:21:27 +00:00
Lee Robinson
44e1eb3983 Update CLI README (#8675)
:another-one:
2022-10-04 04:48:10 +00:00
Lee Robinson
f8af013349 Update README (#8674)
Small changes 😄
2022-10-04 04:13:41 +00:00
Steven
972cc495ec [tests] Replace cancel-workflow-action with native cancel-in-progress (#8671)
This removes the `styfle/cancel-workflow-action` in favor of native GitHub Actions `cancel-in-progress`.

The cancel key is workflow+branch but we don't want to cancel on the `main` branch.

https://docs.github.com/en/actions/using-jobs/using-concurrency
2022-10-03 23:49:28 +00:00
Steven
1c580da3d8 [cli] Fix vc build to error early when runtime is discontinued (#8669)
This moves an existing error from the build container to `vercel build`.

Its rare, but [Vercel Runtimes](https://vercel.com/docs/runtimes) might target a discontinued [AWS Lambda Runtime](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) so we should fail fast when we know this has happened in `vercel build`.

A test has been added to demonstrate the failure using an old PHP version.
2022-10-03 22:07:02 +00:00
Steven
244554ab1b [tests] Remove nodejs12.x tests (#8667)
Now that `nodejs12.x` has passed the sunset date, new deployments will fail so we need to update a few tests.

https://vercel.com/changelog/node-js-12-is-being-deprecated
2022-10-03 20:59:05 +00:00
Steven
053c185481 Publish Stable
- vercel@28.4.6
 - @vercel/client@12.2.11
 - @vercel/next@3.2.2
2022-10-03 10:07:07 -04:00
JJ Kasper
8805b586ea [next] Allow revalidating ISR 404 path itself (#8663)
### Related Issues

Fixes: https://vercel.slack.com/archives/C03S8ED1DKM/p1664521958768189

### 📋 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-02 13:09:24 +00:00
Chris Barber
681070ffa0 [tests] Adding test for next builder OS path separator for serverless file refs (#8661)
Here's the test for https://github.com/vercel/vercel/pull/8657.

#### 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-09-30 18:29:39 +00:00
Chris Barber
362b17d60a [next] Use OS path separator to match serverless file references (#8657)
When running `vc build` for a Next.js app, the Next builder will execute the server build which performs several steps. One of the steps is to trace each serverless function for any referenced files, then the raw list of files is scrubbed and filtered. The filtering uses OS specific file path comparisons to see if a file is of interest. Since it's comparing OS specific paths, we need to use OS specific path separators.

During testing on Windows, the traced serverless functions file list was always empty.

### 📋 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

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-30 15:25:33 +00:00
JJ Kasper
c7c9b1a791 [next] Update RSC header in has routes (#8651)
### Related Issues

x-ref: https://github.com/vercel/next.js/pull/40979

### 📋 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-09-28 13:24:01 -07:00
Nathan Rajlich
c42f309463 [cli] Print upload progress in increments of 25% when non-TTY (#8650)
When running `vc deploy` in a non-TTY context (i.e. CI), limit the number of progress updates to 25% increments (for a total of 5).

```
Uploading [--------------------] (0.0B/71.9MB)
Uploading [=====---------------] (18.0MB/71.9MB)
Uploading [==========----------] (36.0MB/71.9MB)
Uploading [===============-----] (54.0MB/71.9MB)
Uploading [====================] (71.9MB/71.9MB)
```

This avoids spamming the user logs with many progress updates.
2022-09-28 19:33:33 +00:00
Sean Massa
a0ead28369 [tests] replace spinner messages with normal output during tests (#8634)
Convert spinner output to simple prints during test runs. This makes it easier to write tests against the output of commands.
2022-09-28 17:52:40 +00:00
chloetedder
8814fc1515 Publish Stable
- @vercel/build-utils@5.5.3
 - vercel@28.4.5
 - @vercel/client@12.2.10
 - @vercel/fs-detectors@3.4.1
 - @vercel/go@2.2.11
 - @vercel/hydrogen@0.0.24
 - @vercel/next@3.2.1
 - @vercel/node@2.5.21
 - @vercel/python@3.1.20
 - @vercel/redwood@1.0.29
 - @vercel/remix@1.0.30
 - @vercel/ruby@1.3.37
 - @vercel/static-build@1.0.29
2022-09-28 10:14:27 -05:00
chloetedder
0d044b4eac [fs-detectors] Use json5 parser for Rush to have valid json parsing (#8645)
### Related Issues

Parse `rush.json` files with `json5` because it is very common for these
to have comments in them

[Template for people to clone for
Rush](https://rushjs.io/pages/configs/rush_json/) which has comments in
it as a default which most people will clone
 
Docs of Rush showing to not use `JSON.parse`

https://rushjs.io/pages/help/faq/#why-do-rushs-json-config-files-contain--comments-that-github-shows-in-red

Added in tests with block comments and single line comments

### 📋 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: Sean Massa <EndangeredMassa@gmail.com>
2022-09-28 06:38:25 -05:00
Steven
f6bd1aa8c0 [tests] Remove console.log() from test (#8647)
Remove `console.log()` from test
2022-09-27 23:38:49 +00:00
Steven
8cd84ec066 Publish Stable
- @vercel/build-utils@5.5.2
 - vercel@28.4.4
 - @vercel/client@12.2.9
 - @vercel/go@2.2.10
 - @vercel/hydrogen@0.0.23
 - @vercel/next@3.2.0
 - @vercel/node@2.5.20
 - @vercel/python@3.1.19
 - @vercel/redwood@1.0.28
 - @vercel/remix@1.0.29
 - @vercel/ruby@1.3.36
 - @vercel/static-build@1.0.28
2022-09-27 16:35:00 -04:00
Steven
a8df231e4c [build-utils] Fix npm version detection for --legacy-peer-deps (#8646)
There was a case where the npm version wasn't decided base on Node.js version but instead based on the lockfile.
This PR fixes the case when a newer npm version is detected base on the lockfile.

- Follow up to #8598
- Follow up to #8550
2022-09-27 16:33:24 -04:00
Gal Schlezinger
f674842bed [next] read regions from middleware-manifest output (#8629)
### Related Issues

- Needs https://github.com/vercel/next.js/pull/40881 for this to be
useful
- Resolves EC-238

### 📋 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: Steven <steven@ceriously.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-09-27 10:46:02 -07:00
Steven
bf5cfa9a41 Publish Stable
- @vercel/build-utils@5.5.1
 - vercel@28.4.3
 - @vercel/client@12.2.8
 - @vercel/go@2.2.9
 - @vercel/hydrogen@0.0.22
 - @vercel/next@3.1.30
 - @vercel/node@2.5.19
 - @vercel/python@3.1.18
 - @vercel/redwood@1.0.27
 - @vercel/remix@1.0.28
 - @vercel/ruby@1.3.35
 - @vercel/static-build@1.0.27
2022-09-27 09:39:04 -04:00
JJ Kasper
12121b7a71 [next] Fix build time 404 route with trailingSlash: true (#8627)
This ensures we properly handle trailing slashes in the `notFound: true` build time routes. 

Fixes: [slack thread](https://vercel.slack.com/archives/C03S8ED1DKM/p1663691719703509)
2022-09-27 09:21:49 -04:00
Steven
baa56aed2c [tests] Fix timeout for actions/setup-node (#8639)
Try fixing the timeout again. For example: https://github.com/vercel/vercel/actions/runs/3130757219/jobs/5081381465

- Follow up to #8613 
- Related to https://github.com/actions/cache/issues/810 
- Related to https://github.com/Azure/azure-sdk-for-js/issues/22321
2022-09-26 18:33:43 -04:00
Steven
6f767367e4 [build-utils] Adjust nodejs12.x discontinueDate to Monday (#8638)
The previous date was on a Saturday so lets move it to the following Monday to ensure support tickets are quickly answered.
2022-09-26 22:22:32 +00:00
Sean Massa
0e4124f94c [cli] fix vc bisect off by one (#8399)
Co-authored-by: Nathan Rajlich <n@n8.io>
2022-09-26 14:47:15 -05:00
Sean Massa
30503d0a3f [go] remove unused, breaking watchlist from Go builder (#8633) 2022-09-26 14:31:36 -05:00
Steven
6c9164f67d [cli] Refactor doBuild to return void (#8626)
This PR refactor `doBuild()` to return `void`.

This will prevent accidental bugs like #8623 where an exit code number was returned instead of throwing on error.
2022-09-24 01:25:59 +00:00
Steven
906b7a8f2c [cli] Fix invalid vercel.json config error serialization (#8623)
Follow up to #8622 since we should be throwing errors so the error is correctly serialized to `builds.json`.
2022-09-23 22:08:53 +00:00
Steven
43499b13d8 [cli] Add vercel.json validation to vercel build (#8622)
We were doing this validation in `vercel dev` but not `vercel build`.

This PR adds `vercel.json` validation to `vercel build` too.

Note I am calling this a patch because invalid `vercel.json` was already failing when passed to the API so this allows a nice error message earlier in the process.
2022-09-23 20:33:28 +00:00
Sean Massa
7d6e56670f [cli][dev] Add strict mode to vc dev edge function handlers (#8616)
Add strict mode to `vc dev` edge function handlers. This is behind a flag in production, but that flag has been at 100% for a while. So, it seems safe to include it here unconditionally.

Also remove legal comments.

These changes bring `vc dev` edge function support closer to production.
2022-09-23 14:23:03 +00:00
Sean Massa
dba337f148 [cli][dev] extract edge/serverless handler logic into separate files (#8615) 2022-09-22 16:34:32 -05:00
337 changed files with 6800 additions and 3502 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

@@ -1,17 +0,0 @@
name: Cancel
on:
push:
branches:
- '**'
- '!main'
jobs:
cancel:
name: 'Cancel Previous Runs'
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- uses: styfle/cancel-workflow-action@0.9.1
with:
workflow_id: test.yml, test-integration-cli.yml, test-unit.yml
access_token: ${{ github.token }}

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: |
@@ -37,8 +39,7 @@ jobs:
- name: Setup Node
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
uses: actions/setup-node@v3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: 5 # https://github.com/actions/cache/issues/810
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
with:
node-version: 14
cache: 'yarn'

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

@@ -13,6 +13,10 @@ env:
TURBO_TEAM: 'vercel'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
test:
name: CLI
@@ -31,8 +35,7 @@ jobs:
with:
go-version: '1.13.15'
- uses: actions/setup-node@v3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: 5 # https://github.com/actions/cache/issues/810
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
with:
node-version: ${{ matrix.node }}
cache: 'yarn'

View File

@@ -13,6 +13,10 @@ env:
TURBO_TEAM: 'vercel'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
test:
name: Unit
@@ -31,8 +35,7 @@ jobs:
with:
fetch-depth: 2
- uses: actions/setup-node@v3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: 5 # https://github.com/actions/cache/issues/810
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
with:
node-version: ${{ matrix.node }}
cache: 'yarn'

View File

@@ -14,6 +14,10 @@ env:
TURBO_TEAM: 'vercel'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
setup:
name: Find Changes
@@ -29,8 +33,7 @@ jobs:
with:
go-version: '1.13.15'
- uses: actions/setup-node@v3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: 5 # https://github.com/actions/cache/issues/810
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
@@ -67,8 +70,7 @@ jobs:
with:
go-version: '1.13.15'
- uses: actions/setup-node@v3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: 5 # https://github.com/actions/cache/issues/810
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
@@ -80,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

@@ -380,8 +380,8 @@ This is a [class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refere
This is an abstract enumeration type that is implemented by one of the following possible `String` values:
- `nodejs16.x`
- `nodejs14.x`
- `nodejs12.x`
- `go1.x`
- `java11`
- `python3.9`

View File

@@ -19,11 +19,9 @@
## Vercel
Vercel is a platform for **static sites and frontend frameworks**, built to integrate with your headless content, commerce, or database.
Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.
We provide a **frictionless developer experience** to take care of the hard things: deploy instantly, scale automatically, and serve personalized content around the globe.
We make it easy for frontend teams to **develop, preview, and ship** delightful user experiences, where performance is the default.
We enable teams to iterate quickly and develop, preview, and ship delightful user experiences. Vercel has zero-configuration support for 35+ frontend frameworks and integrates with your headless content, commerce, or database of choice.
## Deploy
@@ -37,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.0",
"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

@@ -61,14 +61,14 @@ export function getPrettyError(obj: {
}
return new NowBuildError({
code: 'DEV_VALIDATE_CONFIG',
code: 'INVALID_VERCEL_CONFIG',
message: message,
link: prop ? `${docsUrl}#project/${prop.toLowerCase()}` : docsUrl,
action: 'View Documentation',
});
} catch (e) {
return new NowBuildError({
code: 'DEV_VALIDATE_CONFIG',
code: 'INVALID_VERCEL_CONFIG',
message: `Failed to validate configuration.`,
link: docsUrl,
action: 'View Documentation',

View File

@@ -5,7 +5,13 @@ import path from 'path';
import Sema from 'async-sema';
import { FileBase } from './types';
const semaToPreventEMFILE = new Sema(20);
const DEFAULT_SEMA = 20;
const semaToPreventEMFILE = new Sema(
parseInt(
process.env.VERCEL_INTERNAL_FILE_FS_REF_SEMA || String(DEFAULT_SEMA),
10
) || DEFAULT_SEMA
);
interface FileFsRefOptions {
mode?: number;

View File

@@ -12,7 +12,13 @@ interface FileRefOptions {
mutable?: boolean;
}
const semaToDownloadFromS3 = new Sema(5);
const DEFAULT_SEMA = 5;
const semaToDownloadFromS3 = new Sema(
parseInt(
process.env.VERCEL_INTERNAL_FILE_REF_SEMA || String(DEFAULT_SEMA),
10
) || DEFAULT_SEMA
);
class BailableError extends Error {
public bail: boolean;

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-01'),
},
{
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`
);
@@ -461,7 +452,8 @@ export async function runNpmInstall(
let commandArgs: string[];
const isPotentiallyBrokenNpm =
cliType === 'npm' &&
nodeVersion?.major === 16 &&
(nodeVersion?.major === 16 ||
opts.env.PATH?.includes('/node16/bin-npm7')) &&
!args.includes('--legacy-peer-deps') &&
spawnOpts?.env?.ENABLE_EXPERIMENTAL_COREPACK !== '1';

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

@@ -0,0 +1,2 @@
node_modules
.vercel

View File

@@ -0,0 +1,64 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"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"
}
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
},
"prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"react": {
"version": "16.8.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.0.tgz",
"integrity": "sha512-g+nikW2D48kqgWSPwNo0NH9tIGG3DsQFlrtrQ1kj6W77z5ahyIHG0w8kPpz4Sdj6gyLnz0lEd/xsjOoGge2MYQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.0"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"scheduler": {
"version": "0.13.6",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz",
"integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"swr": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/swr/-/swr-1.3.0.tgz",
"integrity": "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw=="
}
}
}

View File

@@ -0,0 +1,11 @@
{
"private": true,
"scripts": {
"build": "mkdir -p public && echo 'legacy peer deps' > public/index.txt"
},
"packageManager": "npm@6.14.17",
"dependencies": {
"swr": "1.3.0",
"react": "16.8.0"
}
}

View File

@@ -0,0 +1,3 @@
{
"probes": [{ "path": "/", "mustContain": "legacy peer deps" }]
}

View File

@@ -34,7 +34,7 @@ it('should not include peer dependencies when missing VERCEL_NPM_LEGACY_PEER_DEP
const fixture = path.join(__dirname, 'fixtures', '20-npm-7');
const meta: Meta = {};
const spawnOpts = getTestSpawnOpts({});
const nodeVersion = { major: 16 } as any;
const nodeVersion = getNodeVersion(16);
await runNpmInstall(fixture, [], spawnOpts, meta, nodeVersion);
expect(spawnMock.mock.calls.length).toBe(1);
const args = spawnMock.mock.calls[0];
@@ -71,10 +71,35 @@ it('should include peer dependencies when VERCEL_NPM_LEGACY_PEER_DEPS=1 on node1
});
});
it('should not include peer dependencies when VERCEL_NPM_LEGACY_PEER_DEPS=1 on node14', async () => {
it('should include peer dependencies when VERCEL_NPM_LEGACY_PEER_DEPS=1 on node14 and npm7+', async () => {
const fixture = path.join(__dirname, 'fixtures', '20-npm-7');
const meta: Meta = {};
const spawnOpts = getTestSpawnOpts({ VERCEL_NPM_LEGACY_PEER_DEPS: '1' });
const nodeVersion = getNodeVersion(14);
await runNpmInstall(fixture, [], spawnOpts, meta, nodeVersion);
expect(spawnMock.mock.calls.length).toBe(1);
const args = spawnMock.mock.calls[0];
expect(args[0]).toEqual('npm');
expect(args[1]).toEqual([
'install',
'--no-audit',
'--unsafe-perm',
'--legacy-peer-deps',
]);
expect(args[2]).toEqual({
cwd: fixture,
prettyCommand: 'npm install',
stdio: 'inherit',
env: expect.any(Object),
});
});
it('should not include peer dependencies when VERCEL_NPM_LEGACY_PEER_DEPS=1 on node14 and npm6', async () => {
const fixture = path.join(__dirname, 'fixtures', '14-npm-6-legacy-peer-deps');
const meta: Meta = {};
const spawnOpts = getTestSpawnOpts({ VERCEL_NPM_LEGACY_PEER_DEPS: '1' });
const nodeVersion = getNodeVersion(14);
await runNpmInstall(fixture, [], spawnOpts, meta, nodeVersion);
expect(spawnMock.mock.calls.length).toBe(1);

View File

@@ -216,10 +216,6 @@ it('should download symlinks even with incorrect file', async () => {
});
it('should only match supported node versions, otherwise throw an error', async () => {
expect(await getSupportedNodeVersion('12.x', false)).toHaveProperty(
'major',
12
);
expect(await getSupportedNodeVersion('14.x', false)).toHaveProperty(
'major',
14
@@ -240,10 +236,6 @@ it('should only match supported node versions, otherwise throw an error', async
await expectBuilderError(getSupportedNodeVersion('foo', true), autoMessage);
await expectBuilderError(getSupportedNodeVersion('=> 10', true), autoMessage);
expect(await getSupportedNodeVersion('12.x', true)).toHaveProperty(
'major',
12
);
expect(await getSupportedNodeVersion('14.x', true)).toHaveProperty(
'major',
14
@@ -273,24 +265,41 @@ it('should only match supported node versions, otherwise throw an error', async
it('should match all semver ranges', async () => {
// See https://docs.npmjs.com/files/package.json#engines
expect(await getSupportedNodeVersion('12.0.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('12.x')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('14.0.0')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('14.x')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 16);
expect(await getSupportedNodeVersion('>=10.3.0')).toHaveProperty('major', 16);
expect(await getSupportedNodeVersion('11.5.0 - 12.5.0')).toHaveProperty(
expect(await getSupportedNodeVersion('16.5.0 - 16.9.0')).toHaveProperty(
'major',
12
16
);
expect(await getSupportedNodeVersion('>=9.5.0 <=12.5.0')).toHaveProperty(
'major',
12
);
expect(await getSupportedNodeVersion('~12.5.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('^12.5.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('12.5.0 - 14.5.0')).toHaveProperty(
expect(await getSupportedNodeVersion('>=9.5.0 <=14.5.0')).toHaveProperty(
'major',
14
);
expect(await getSupportedNodeVersion('~14.5.0')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('^14.5.0')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('14.5.0 - 14.20.0')).toHaveProperty(
'major',
14
);
});
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 () => {
@@ -434,13 +443,45 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
expect(warningMessages).toStrictEqual([
'Error: Node.js version 10.x has reached End-of-Life. Deployments created on or after 2021-04-20 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.',
'Error: Node.js version 10.x has reached End-of-Life. Deployments created on or after 2021-04-20 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-01 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-01 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-03 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-03 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.',
]);
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

@@ -10,11 +10,9 @@
## Usage
Vercel is a platform for **static sites and frontend frameworks**, built to integrate with your headless content, commerce, or database.
Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.
We provide a **frictionless developer experience** to take care of the hard things: deploy instantly, scale automatically, and serve personalized content around the globe.
We make it easy for frontend teams to **develop, preview, and ship** delightful user experiences, where performance is the default.
We enable teams to iterate quickly and develop, preview, and ship delightful user experiences. Vercel has zero-configuration support for 35+ frontend frameworks and integrates with your headless content, commerce, or database of choice.
To install the latest version of Vercel CLI, run this command:

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.4.2",
"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.0",
"@vercel/go": "2.2.8",
"@vercel/hydrogen": "0.0.21",
"@vercel/next": "3.1.29",
"@vercel/node": "2.5.18",
"@vercel/python": "3.1.17",
"@vercel/redwood": "1.0.26",
"@vercel/remix": "1.0.27",
"@vercel/ruby": "1.3.34",
"@vercel/static-build": "1.0.26",
"@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.7",
"@vercel/frameworks": "1.1.6",
"@vercel/fs-detectors": "3.4.0",
"@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

@@ -2,7 +2,6 @@ import open from 'open';
import boxen from 'boxen';
import execa from 'execa';
import plural from 'pluralize';
import inquirer from 'inquirer';
import { resolve } from 'path';
import chalk, { Chalk } from 'chalk';
import { URLSearchParams, parse } from 'url';
@@ -150,7 +149,9 @@ export default async function main(client: Client): Promise<number> {
if (badDeployment) {
if (badDeployment instanceof Error) {
badDeployment.message += ` "${bad}"`;
badDeployment.message += ` when requesting bad deployment "${normalizeURL(
bad
)}"`;
output.prettyError(badDeployment);
return 1;
}
@@ -165,7 +166,9 @@ export default async function main(client: Client): Promise<number> {
if (goodDeployment) {
if (goodDeployment instanceof Error) {
goodDeployment.message += ` "${good}"`;
goodDeployment.message += ` when requesting good deployment "${normalizeURL(
good
)}"`;
output.prettyError(goodDeployment);
return 1;
}
@@ -226,7 +229,8 @@ export default async function main(client: Client): Promise<number> {
// If we have the "good" deployment in this chunk, then we're done
for (let i = 0; i < newDeployments.length; i++) {
if (newDeployments[i].url === good) {
newDeployments = newDeployments.slice(0, i + 1);
// grab all deployments up until the good one
newDeployments = newDeployments.slice(0, i);
next = undefined;
break;
}
@@ -316,7 +320,7 @@ export default async function main(client: Client): Promise<number> {
if (openEnabled) {
await open(testUrl);
}
const answer = await inquirer.prompt({
const answer = await client.prompt({
type: 'expand',
name: 'action',
message: 'Select an action:',

View File

@@ -3,6 +3,7 @@ import chalk from 'chalk';
import dotenv from 'dotenv';
import { join, normalize, relative, resolve } from 'path';
import {
getDiscontinuedNodeVersions,
normalizePath,
Files,
FileFsRef,
@@ -25,6 +26,7 @@ import {
MergeRoutesProps,
Route,
} from '@vercel/routing-utils';
import { fileNameSymbol } from '@vercel/client';
import type { VercelConfig } from '@vercel/client';
import pull from './pull';
@@ -54,6 +56,7 @@ import { importBuilders } from '../util/build/import-builders';
import { initCorepack, cleanupCorepack } from '../util/build/corepack';
import { sortBuilders } from '../util/build/sort-builders';
import { toEnumerableError } from '../util/error';
import { validateConfig } from '../util/validate-config';
type BuildResult = BuildResultV2 | BuildResultV3;
@@ -232,7 +235,8 @@ export default async function main(client: Client): Promise<number> {
process.env.VERCEL = '1';
process.env.NOW_BUILDER = '1';
return await doBuild(client, project, buildsJson, cwd, outputDir);
await doBuild(client, project, buildsJson, cwd, outputDir);
return 0;
} catch (err: any) {
output.prettyError(err);
@@ -265,23 +269,36 @@ async function doBuild(
buildsJson: BuildsManifest,
cwd: string,
outputDir: string
): Promise<number> {
): Promise<void> {
const { output } = client;
const workPath = join(cwd, project.settings.rootDirectory || '.');
// Load `package.json` and `vercel.json` files
const [pkg, vercelConfig] = await Promise.all([
const [pkg, vercelConfig, nowConfig] = await Promise.all([
readJSONFile<PackageJson>(join(workPath, 'package.json')),
readJSONFile<VercelConfig>(join(workPath, 'vercel.json')).then(
config => config || readJSONFile<VercelConfig>(join(workPath, 'now.json'))
),
readJSONFile<VercelConfig>(join(workPath, 'vercel.json')),
readJSONFile<VercelConfig>(join(workPath, 'now.json')),
]);
if (pkg instanceof CantParseJSONFile) throw pkg;
if (vercelConfig instanceof CantParseJSONFile) throw vercelConfig;
if (nowConfig instanceof CantParseJSONFile) throw nowConfig;
if (vercelConfig) {
vercelConfig[fileNameSymbol] = 'vercel.json';
} else if (nowConfig) {
nowConfig[fileNameSymbol] = 'now.json';
}
const localConfig = vercelConfig || nowConfig || {};
const validateError = validateConfig(localConfig);
if (validateError) {
throw validateError;
}
const projectSettings = {
...project.settings,
...pickOverrides(vercelConfig || {}),
...pickOverrides(localConfig),
};
// Get a list of source files
@@ -289,12 +306,12 @@ async function doBuild(
normalizePath(relative(workPath, f))
);
const routesResult = getTransformedRoutes(vercelConfig || {});
const routesResult = getTransformedRoutes(localConfig);
if (routesResult.error) {
throw routesResult.error;
}
if (vercelConfig?.builds && vercelConfig.functions) {
if (localConfig.builds && localConfig.functions) {
throw new NowBuildError({
code: 'bad_request',
message:
@@ -303,7 +320,7 @@ async function doBuild(
});
}
let builds = vercelConfig?.builds || [];
let builds = localConfig.builds || [];
let zeroConfigRoutes: Route[] = [];
let isZeroConfig = false;
@@ -318,7 +335,7 @@ async function doBuild(
// Detect the Vercel Builders that will need to be invoked
const detectedBuilders = await detectBuilders(files, pkg, {
...vercelConfig,
...localConfig,
projectSettings,
ignoreBuildScript: true,
featHandleMiss: true,
@@ -395,13 +412,10 @@ async function doBuild(
})
);
buildsJson.builds = Array.from(buildsJsonBuilds.values());
const buildsJsonPath = join(outputDir, 'builds.json');
const writeBuildsJsonPromise = fs.writeJSON(buildsJsonPath, buildsJson, {
await fs.writeJSON(join(outputDir, 'builds.json'), buildsJson, {
spaces: 2,
});
ops.push(writeBuildsJsonPromise);
// The `meta` config property is re-used for each Builder
// invocation so that Builders can share state between
// subsequent entrypoint builds.
@@ -454,6 +468,25 @@ async function doBuild(
);
const buildResult = await builder.build(buildOptions);
if (
buildResult &&
'output' in buildResult &&
'runtime' in buildResult.output &&
'type' in buildResult.output &&
buildResult.output.type === 'Lambda'
) {
const lambdaRuntime = buildResult.output.runtime;
if (
getDiscontinuedNodeVersions().some(o => o.runtime === lambdaRuntime)
) {
throw new NowBuildError({
code: 'NODEJS_DISCONTINUED_VERSION',
message: `The Runtime "${build.use}" is using "${lambdaRuntime}", which is discontinued. Please upgrade your Runtime to a more recent version or consult the author for more details.`,
link: 'https://github.com/vercel/vercel/blob/main/DEVELOPING_A_RUNTIME.md#lambdaruntime',
});
}
}
// Store the build result to generate the final `config.json` after
// all builds have completed
buildResults.set(build, buildResult);
@@ -466,7 +499,7 @@ async function doBuild(
build,
builder,
builderPkg,
vercelConfig
localConfig
).then(
override => {
if (override) overrides.push(override);
@@ -475,26 +508,11 @@ async function doBuild(
)
);
} catch (err: any) {
output.prettyError(err);
const writeConfigJsonPromise = fs.writeJSON(
join(outputDir, 'config.json'),
{ version: 3 },
{ spaces: 2 }
);
await Promise.all([writeBuildsJsonPromise, writeConfigJsonPromise]);
const buildJsonBuild = buildsJsonBuilds.get(build);
if (buildJsonBuild) {
buildJsonBuild.error = toEnumerableError(err);
await fs.writeJSON(buildsJsonPath, buildsJson, {
spaces: 2,
});
}
return 1;
throw err;
}
}
@@ -555,150 +573,7 @@ async function doBuild(
builds: builderRoutes,
});
const images = vercelConfig?.images
if (images) {
if (typeof images !== 'object') {
throw new Error(
`vercel.json "images" should be an object received ${typeof images}.`
);
}
if (!Array.isArray(images.domains)) {
throw new Error(
`vercel.json "images.domains" should be an Array received ${typeof images.domains}.`
);
}
if (images.domains.length > 50) {
throw new Error(
`vercel.json "images.domains" exceeds length of 50 received length (${images.domains.length}).`
);
}
const invalidImageDomains = images.domains.filter(
(d: unknown) => typeof d !== 'string'
);
if (invalidImageDomains.length > 0) {
throw new Error(
`vercel.json "images.domains" should be an Array of strings received invalid values (${invalidImageDomains.join(
', '
)}).`
);
}
if (images.remotePatterns) {
if (!Array.isArray(images.remotePatterns)) {
throw new Error(
`vercel.json "images.remotePatterns" should be an Array received ${typeof images.remotePatterns}.`
);
}
if (images.remotePatterns.length > 50) {
throw new Error(
`vercel.json "images.remotePatterns" exceeds length of 50, received length (${images.remotePatterns.length}).`
);
}
const validProps = new Set(['protocol', 'hostname', 'pathname', 'port']);
const requiredProps = ['hostname'];
const invalidPatterns = images.remotePatterns.filter(
(d: unknown) =>
!d ||
typeof d !== 'object' ||
Object.entries(d).some(
([k, v]) => !validProps.has(k) || typeof v !== 'string'
) ||
requiredProps.some(k => !(k in d))
);
if (invalidPatterns.length > 0) {
throw new Error(
`vercel.json "images.remotePatterns" received invalid values:\n${invalidPatterns
.map(item => JSON.stringify(item))
.join(
'\n'
)}\n\nremotePatterns value must follow format { protocol: 'https', hostname: 'example.com', port: '', pathname: '/imgs/**' }.`
);
}
}
if (!Array.isArray(images.sizes)) {
throw new Error(
`vercel.json "images.sizes" should be an Array received ${typeof images.sizes}.`
);
}
if (images.sizes.length < 1 || images.sizes.length > 50) {
throw new Error(
`vercel.json "images.sizes" should be an Array of length between 1 to 50 received length (${images.sizes.length}).`
);
}
const invalidImageSizes = images.sizes.filter((d: unknown) => {
return typeof d !== 'number' || d < 1 || d > 10000;
});
if (invalidImageSizes.length > 0) {
throw new Error(
`vercel.json "images.sizes" should be an Array of numbers that are between 1 and 10000, received invalid values (${invalidImageSizes.join(
', '
)}).`
);
}
if (images.minimumCacheTTL) {
if (
!Number.isInteger(images.minimumCacheTTL) ||
images.minimumCacheTTL < 0
) {
throw new Error(
`vercel.json "images.minimumCacheTTL" should be an integer 0 or more received (${images.minimumCacheTTL}).`
);
}
}
if (images.formats) {
if (!Array.isArray(images.formats)) {
throw new Error(
`vercel.json "images.formats" should be an Array received ${typeof images.formats}.`
);
}
if (images.formats.length < 1 || images.formats.length > 2) {
throw new Error(
`vercel.json "images.formats" must be length 1 or 2, received length (${images.formats.length}).`
);
}
const invalid = images.formats.filter(f => {
return f !== 'image/avif' && f !== 'image/webp';
});
if (invalid.length > 0) {
throw new Error(
`vercel.json "images.formats" should be an Array of mime type strings, received invalid values (${invalid.join(
', '
)}).`
);
}
}
if (
typeof images.dangerouslyAllowSVG !== 'undefined' &&
typeof images.dangerouslyAllowSVG !== 'boolean'
) {
throw new Error(
`vercel.json "images.dangerouslyAllowSVG" should be a boolean received (${images.dangerouslyAllowSVG}).`
);
}
if (
typeof images.contentSecurityPolicy !== 'undefined' &&
typeof images.contentSecurityPolicy !== 'string'
) {
throw new Error(
`vercel.json "images.contentSecurityPolicy" should be a string received ${images.contentSecurityPolicy}`
);
}
}
const mergedImages = mergeImages(images, buildResults.values());
const mergedImages = mergeImages(localConfig.images, buildResults.values());
const mergedWildcard = mergeWildcard(buildResults.values());
const mergedOverrides: Record<string, PathOverride> =
overrides.length > 0 ? Object.assign({}, ...overrides) : undefined;
@@ -724,8 +599,6 @@ async function doBuild(
emoji('success')
)}\n`
);
return 0;
}
function expandBuild(files: string[], build: Builder): Builder[] {

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,6 +23,7 @@ import type {
} from '../types';
import { sharedPromise } from './promise';
import { APIError } from './errors-ts';
import { normalizeError } from '@vercel/error-utils';
const isSAMLError = (v: any): v is SAMLError => {
return v && v.saml;
@@ -146,10 +147,15 @@ export default class Client extends EventEmitter implements Stdio {
const error = await responseError(res);
if (isSAMLError(error)) {
// A SAML error means the token is expired, or is not
// designated for the requested team, so the user needs
// to re-authenticate
await this.reauthenticate(error);
try {
// A SAML error means the token is expired, or is not
// designated for the requested team, so the user needs
// to re-authenticate
await this.reauthenticate(error);
} catch (reauthError) {
// there's no sense in retrying
return bail(normalizeError(reauthError));
}
} else if (res.status >= 400 && res.status < 500) {
// Any other 4xx should bail without retrying
return bail(error);
@@ -186,7 +192,7 @@ export default class Client extends EventEmitter implements Stdio {
`Failed to re-authenticate for ${bold(error.scope)} scope`
);
}
process.exit(1);
throw error;
}
this.authConfig.token = result.token;

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

@@ -115,29 +115,39 @@ export default async function processDeployment({
.reduce((a: number, b: number) => a + b, 0);
const totalSizeHuman = bytes.format(missingSize, { decimalPlaces: 1 });
uploads.forEach((e: any) =>
e.on('progress', () => {
const uploadedBytes = uploads.reduce((acc: number, e: any) => {
return acc + e.bytesUploaded;
}, 0);
// When stderr is not a TTY then we only want to
// print upload progress in 25% increments
let nextStep = 0;
const stepSize = now._client.stderr.isTTY ? 0 : 0.25;
const bar = progress(uploadedBytes, missingSize);
if (!bar || uploadedBytes === missingSize) {
output.spinner(deployingSpinnerVal, 0);
} else {
const uploadedHuman = bytes.format(uploadedBytes, {
decimalPlaces: 1,
fixedDecimals: true,
});
const updateProgress = () => {
const uploadedBytes = uploads.reduce((acc: number, e: any) => {
return acc + e.bytesUploaded;
}, 0);
const bar = progress(uploadedBytes, missingSize);
if (!bar) {
output.spinner(deployingSpinnerVal, 0);
} else {
const uploadedHuman = bytes.format(uploadedBytes, {
decimalPlaces: 1,
fixedDecimals: true,
});
const percent = uploadedBytes / missingSize;
if (percent >= nextStep) {
output.spinner(
`Uploading ${chalk.reset(
`[${bar}] (${uploadedHuman}/${totalSizeHuman})`
)}`,
0
);
nextStep += stepSize;
}
})
);
}
};
uploads.forEach((e: any) => e.on('progress', updateProgress));
updateProgress();
}
if (event.type === 'file-uploaded') {

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

@@ -57,7 +57,7 @@ import { MissingDotenvVarsError } from '../errors-ts';
import cliPkg from '../pkg';
import { getVercelDirectory } from '../projects/link';
import { staticFiles as getFiles } from '../get-files';
import { validateConfig } from './validate';
import { validateConfig } from '../validate-config';
import { devRouter, getRoutesTypes } from './router';
import getMimeType from './mime-type';
import { executeBuild, getBuildMatches, shutdownBuilder } from './builder';
@@ -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,9 @@ 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';
export interface OutputOptions {
debug?: boolean;
@@ -108,12 +110,15 @@ export class Output {
};
spinner = (message: string, delay: number = 300): void => {
this.spinnerMessage = message;
if (this.debugEnabled) {
this.debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
return;
}
if (this.stream.isTTY) {
if (IS_TEST || !this.stream.isTTY) {
this.print(`${message}\n`);
} else {
this.spinnerMessage = message;
if (this._spinner) {
this._spinner.text = message;
} else {
@@ -125,8 +130,6 @@ export class Output {
delay
);
}
} else {
this.print(`${message}\n`);
}
};

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

@@ -7,7 +7,7 @@ import {
rewritesSchema,
trailingSlashSchema,
} from '@vercel/routing-utils';
import { VercelConfig } from './types';
import { VercelConfig } from './dev/types';
import {
functionsSchema,
buildsSchema,
@@ -16,6 +16,83 @@ import {
} from '@vercel/build-utils';
import { fileNameSymbol } from '@vercel/client';
const imagesSchema = {
type: 'object',
additionalProperties: false,
required: ['sizes'],
properties: {
contentSecurityPolicy: {
type: 'string',
minLength: 1,
maxLength: 256,
},
dangerouslyAllowSVG: {
type: 'boolean',
},
domains: {
type: 'array',
minItems: 0,
maxItems: 50,
items: {
type: 'string',
minLength: 1,
maxLength: 256,
},
},
formats: {
type: 'array',
minItems: 1,
maxItems: 4,
items: {
enum: ['image/avif', 'image/webp', 'image/jpeg', 'image/png'],
},
},
minimumCacheTTL: {
type: 'integer',
minimum: 1,
maximum: 315360000,
},
remotePatterns: {
type: 'array',
minItems: 0,
maxItems: 50,
items: {
type: 'object',
additionalProperties: false,
required: ['hostname'],
properties: {
protocol: {
enum: ['http', 'https'],
},
hostname: {
type: 'string',
minLength: 1,
maxLength: 256,
},
port: {
type: 'string',
minLength: 1,
maxLength: 5,
},
pathname: {
type: 'string',
minLength: 1,
maxLength: 256,
},
},
},
},
sizes: {
type: 'array',
minItems: 1,
maxItems: 50,
items: {
type: 'number',
},
},
},
};
const vercelConfigSchema = {
type: 'object',
// These are not all possibilities because `vc dev`
@@ -30,6 +107,7 @@ const vercelConfigSchema = {
rewrites: rewritesSchema,
trailingSlash: trailingSlashSchema,
functions: functionsSchema,
images: imagesSchema,
},
};

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

@@ -0,0 +1,2 @@
.vercel/builders
.vercel/output

View File

@@ -0,0 +1,7 @@
{
"orgId": ".",
"projectId": ".",
"settings": {
"framework": null
}
}

View File

@@ -0,0 +1 @@
<?php echo 'This version of vercel-php uses the nodejs12.x Lambda Runtime'; ?>

View File

@@ -0,0 +1,8 @@
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"functions": {
"api/index.php": {
"runtime": "vercel-php@0.1.0"
}
}
}

View File

@@ -3,6 +3,6 @@
"sizes": [256, 384, 600, 1000],
"domains": [],
"minimumCacheTTL": 60,
"formats": ["image/webp", "image/avif"]
"formats": ["image/avif", "image/webp"]
}
}

View File

@@ -0,0 +1,7 @@
{
"orgId": ".",
"projectId": ".",
"settings": {
"framework": null
}
}

View File

@@ -0,0 +1 @@
<h1>Vercel</h1>

View File

@@ -0,0 +1,16 @@
{
"rewrites": [
{
"source": "/one",
"destination": "/api/one"
},
{
"source": "/two",
"destination": "/api/two"
},
{
"src": "/three",
"dest": "/api/three"
}
]
}

View File

@@ -326,7 +326,7 @@ module.exports = async function prepare(session, binaryPath, tmpFixturesDir) {
'vercel.json': JSON.stringify({
functions: {
'api/**/*.php': {
runtime: 'vercel-php@0.1.0',
runtime: 'vercel-php@0.5.2',
},
},
}),

View File

@@ -3,20 +3,26 @@ import chance from 'chance';
import { Deployment } from '@vercel/client';
import { client } from './client';
import { Build, User } from '../../src/types';
import type { Request, Response } from 'express';
let deployments = new Map<string, Deployment>();
let deploymentBuilds = new Map<Deployment, Build[]>();
let alreadySetupDeplomentEndpoints = false;
type State = Deployment['readyState'];
export function useDeployment({
creator,
state = 'READY',
createdAt,
}: {
creator: Pick<User, 'id' | 'email' | 'name' | 'username'>;
state?: State;
createdAt?: number;
}) {
const createdAt = Date.now();
setupDeploymentEndpoints();
createdAt = createdAt || Date.now();
const url = new URL(chance().url());
const name = chance().name();
const id = `dpl_${chance().guid()}`;
@@ -99,6 +105,15 @@ export function useDeploymentMissingProjectSettings() {
beforeEach(() => {
deployments = new Map();
deploymentBuilds = new Map();
alreadySetupDeplomentEndpoints = false;
});
function setupDeploymentEndpoints() {
if (alreadySetupDeplomentEndpoints) {
return;
}
alreadySetupDeplomentEndpoints = true;
client.scenario.get('/:version/deployments/:id', (req, res) => {
const { id } = req.params;
@@ -136,8 +151,21 @@ beforeEach(() => {
res.json({ builds });
});
client.scenario.get('/:version/now/deployments', (req, res) => {
const deploymentsList = Array.from(deployments.values());
res.json({ deployments: deploymentsList });
});
});
function handleGetDeployments(req: Request, res: Response) {
const currentDeployments = Array.from(deployments.values()).sort(
(a: Deployment, b: Deployment) => {
// sort in reverse chronological order
return b.createdAt - a.createdAt;
}
);
res.json({
pagination: {
count: currentDeployments.length,
},
deployments: currentDeployments,
});
}
client.scenario.get('/:version/now/deployments', handleGetDeployments);
client.scenario.get('/:version/deployments', handleGetDeployments);
}

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,55 @@
import { client } from '../../mocks/client';
import { useUser } from '../../mocks/user';
import bisect from '../../../src/commands/bisect';
import { useDeployment } from '../../mocks/deployment';
describe('bisect', () => {
it('should find the bad deployment', async () => {
const user = useUser();
const now = Date.now();
const deployment1 = useDeployment({ creator: user, createdAt: now });
const deployment2 = useDeployment({
creator: user,
createdAt: now + 10000,
});
const deployment3 = useDeployment({
creator: user,
createdAt: now + 20000,
});
// also create an extra deployment before the known good deployment
// to make sure the bisect pool doesn't include it
useDeployment({
creator: user,
createdAt: now - 30000,
});
const bisectPromise = bisect(client);
await expect(client.stderr).toOutput('Specify a URL where the bug occurs:');
client.stdin.write(`https://${deployment3.url}\n`);
await expect(client.stderr).toOutput(
'Specify a URL where the bug does not occur:'
);
client.stdin.write(`https://${deployment1.url}\n`);
await expect(client.stderr).toOutput(
'Specify the URL subpath where the bug occurs:'
);
client.stdin.write('/docs\n');
await expect(client.stderr).toOutput('Bisecting');
await expect(client.stderr).toOutput(
`Deployment URL: https://${deployment2.url}`
);
client.stdin.write('b\n');
await expect(client.stderr).toOutput(
`The first bad deployment is: https://${deployment2.url}`
);
await expect(bisectPromise).resolves.toEqual(0);
});
});

View File

@@ -750,11 +750,71 @@ describe('build', () => {
const errorBuilds = builds.builds.filter((b: any) => 'error' in b);
expect(errorBuilds).toHaveLength(1);
expect(errorBuilds[0].error.name).toEqual('Error');
expect(errorBuilds[0].error.message).toMatch(`TS1005`);
expect(errorBuilds[0].error.message).toMatch(`',' expected.`);
expect(errorBuilds[0].error.hideStackTrace).toEqual(true);
expect(errorBuilds[0].error.code).toEqual('NODE_TYPESCRIPT_ERROR');
expect(errorBuilds[0].error).toEqual({
name: 'Error',
message: expect.stringContaining('TS1005'),
stack: expect.stringContaining('api/typescript.ts'),
hideStackTrace: true,
code: 'NODE_TYPESCRIPT_ERROR',
});
// top level "error" also contains the same error
expect(builds.error).toEqual({
name: 'Error',
message: expect.stringContaining('TS1005'),
stack: expect.stringContaining('api/typescript.ts'),
hideStackTrace: true,
code: 'NODE_TYPESCRIPT_ERROR',
});
// `config.json` contains `version`
const configJson = await fs.readJSON(join(output, 'config.json'));
expect(configJson.version).toBe(3);
} finally {
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
it('should error when "functions" has runtime that emits discontinued "nodejs12.x"', async () => {
if (process.platform === 'win32') {
console.log('Skipping test on Windows');
return;
}
const cwd = fixture('discontinued-nodejs12.x');
const output = join(cwd, '.vercel/output');
try {
process.chdir(cwd);
const exitCode = await build(client);
expect(exitCode).toEqual(1);
// Error gets printed to the terminal
await expect(client.stderr).toOutput(
'The Runtime "vercel-php@0.1.0" is using "nodejs12.x", which is discontinued. Please upgrade your Runtime to a more recent version or consult the author for more details.'
);
// `builds.json` contains "error" build
const builds = await fs.readJSON(join(output, 'builds.json'));
const errorBuilds = builds.builds.filter((b: any) => 'error' in b);
expect(errorBuilds).toHaveLength(1);
expect(errorBuilds[0].error).toEqual({
name: 'Error',
message: expect.stringContaining('Please upgrade your Runtime'),
stack: expect.stringContaining('Please upgrade your Runtime'),
hideStackTrace: true,
code: 'NODEJS_DISCONTINUED_VERSION',
link: 'https://github.com/vercel/vercel/blob/main/DEVELOPING_A_RUNTIME.md#lambdaruntime',
});
// top level "error" also contains the same error
expect(builds.error).toEqual({
name: 'Error',
message: expect.stringContaining('Please upgrade your Runtime'),
stack: expect.stringContaining('Please upgrade your Runtime'),
hideStackTrace: true,
code: 'NODEJS_DISCONTINUED_VERSION',
link: 'https://github.com/vercel/vercel/blob/main/DEVELOPING_A_RUNTIME.md#lambdaruntime',
});
// `config.json` contains `version`
const configJson = await fs.readJSON(join(output, 'config.json'));
@@ -920,7 +980,7 @@ describe('build', () => {
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
it('should apply "images" configuration from `vercel.json`', async () => {
const cwd = fixture('images');
const output = join(cwd, '.vercel/output');
@@ -936,7 +996,7 @@ describe('build', () => {
sizes: [256, 384, 600, 1000],
domains: [],
minimumCacheTTL: 60,
formats: ['image/webp', 'image/avif'],
formats: ['image/avif', 'image/webp'],
},
});
} finally {
@@ -945,6 +1005,38 @@ describe('build', () => {
}
});
it('should fail with invalid "rewrites" configuration from `vercel.json`', async () => {
const cwd = fixture('invalid-rewrites');
const output = join(cwd, '.vercel/output');
try {
process.chdir(cwd);
const exitCode = await build(client);
expect(exitCode).toEqual(1);
await expect(client.stderr).toOutput(
'Error: Invalid vercel.json - `rewrites[2]` should NOT have additional property `src`. Did you mean `source`?' +
'\n' +
'View Documentation: https://vercel.com/docs/configuration#project/rewrites'
);
const builds = await fs.readJSON(join(output, 'builds.json'));
expect(builds.builds).toBeUndefined();
expect(builds.error).toEqual({
name: 'Error',
message:
'Invalid vercel.json - `rewrites[2]` should NOT have additional property `src`. Did you mean `source`?',
stack: expect.stringContaining('at validateConfig'),
hideStackTrace: true,
code: 'INVALID_VERCEL_CONFIG',
link: 'https://vercel.com/docs/configuration#project/rewrites',
action: 'View Documentation',
});
const configJson = await fs.readJSON(join(output, 'config.json'));
expect(configJson.version).toBe(3);
} finally {
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
describe('should find packages with different main/module/browser keys', function () {
let output: string;

View File

@@ -1,4 +1,7 @@
import bytes from 'bytes';
import fs from 'fs-extra';
import { join } from 'path';
import { randomBytes } from 'crypto';
import { fileNameSymbol } from '@vercel/client';
import { client } from '../../mocks/client';
import deploy from '../../../src/commands/deploy';
@@ -199,4 +202,119 @@ describe('deploy', () => {
process.chdir(originalCwd);
}
});
it('should upload missing files', async () => {
const cwd = setupFixture('commands/deploy/archive');
const originalCwd = process.cwd();
// Add random 1mb file
await fs.writeFile(join(cwd, 'data'), randomBytes(bytes('1mb')));
try {
process.chdir(cwd);
const user = useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
name: 'archive',
id: 'archive',
});
let body: any;
let fileUploaded = false;
client.scenario.post(`/v13/deployments`, (req, res) => {
if (fileUploaded) {
body = req.body;
res.json({
creator: {
uid: user.id,
username: user.username,
},
id: 'dpl_archive_test',
});
} else {
const sha = req.body.files[0].sha;
res.status(400).json({
error: {
code: 'missing_files',
message: 'Missing files',
missing: [sha],
},
});
}
});
client.scenario.post('/v2/files', (req, res) => {
// Wait for file to be finished uploading
req.on('data', () => {
// Noop
});
req.on('end', () => {
fileUploaded = true;
res.end();
});
});
client.scenario.get(`/v13/deployments/dpl_archive_test`, (req, res) => {
res.json({
creator: {
uid: user.id,
username: user.username,
},
id: 'dpl_archive_test',
readyState: 'READY',
aliasAssigned: true,
alias: [],
});
});
client.scenario.get(
`/v10/now/deployments/dpl_archive_test`,
(req, res) => {
res.json({
creator: {
uid: user.id,
username: user.username,
},
id: 'dpl_archive_test',
readyState: 'READY',
aliasAssigned: true,
alias: [],
});
}
);
// When stderr is not a TTY we expect 5 progress lines to be printed
client.stderr.isTTY = false;
client.setArgv('deploy', '--archive=tgz');
const uploadingLines: string[] = [];
client.stderr.on('data', data => {
if (data.startsWith('Uploading [')) {
uploadingLines.push(data);
}
});
client.stderr.resume();
const exitCode = await deploy(client);
expect(exitCode).toEqual(0);
expect(body?.files?.length).toEqual(1);
expect(body?.files?.[0].file).toEqual('.vercel/source.tgz');
expect(uploadingLines.length).toEqual(5);
expect(
uploadingLines[0].startsWith('Uploading [--------------------]')
).toEqual(true);
expect(
uploadingLines[1].startsWith('Uploading [=====---------------]')
).toEqual(true);
expect(
uploadingLines[2].startsWith('Uploading [==========----------]')
).toEqual(true);
expect(
uploadingLines[3].startsWith('Uploading [===============-----]')
).toEqual(true);
expect(
uploadingLines[4].startsWith('Uploading [====================]')
).toEqual(true);
} finally {
process.chdir(originalCwd);
}
});
});

View File

@@ -38,11 +38,11 @@ describe('list', () => {
await list(client);
const output = await readOutputStream(client, 4);
const output = await readOutputStream(client, 6);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[2]);
const header: string[] = parseSpacedTableRow(output.split('\n')[5]);
const data: string[] = parseSpacedTableRow(output.split('\n')[6]);
data.shift();
expect(org).toEqual(team[0].slug);
@@ -81,11 +81,11 @@ describe('list', () => {
client.setArgv('-S', user.username);
await list(client);
const output = await readOutputStream(client, 4);
const output = await readOutputStream(client, 6);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[2]);
const header: string[] = parseSpacedTableRow(output.split('\n')[5]);
const data: string[] = parseSpacedTableRow(output.split('\n')[6]);
data.shift();
expect(org).toEqual(user.username);
@@ -116,11 +116,11 @@ describe('list', () => {
client.setArgv(deployment.name);
await list(client);
const output = await readOutputStream(client, 4);
const output = await readOutputStream(client, 6);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[2]);
const header: string[] = parseSpacedTableRow(output.split('\n')[5]);
const data: string[] = parseSpacedTableRow(output.split('\n')[6]);
data.shift();
expect(org).toEqual(teamSlug || team[0].slug);

View File

@@ -22,10 +22,10 @@ describe('project', () => {
client.setArgv('project', 'ls');
await projects(client);
const output = await readOutputStream(client, 2);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[2]);
const data: string[] = parseSpacedTableRow(output.split('\n')[3]);
const output = await readOutputStream(client, 3);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[1]);
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
data.pop();
expect(org).toEqual(user.username);
@@ -47,10 +47,10 @@ describe('project', () => {
client.setArgv('project', 'ls');
await projects(client);
const output = await readOutputStream(client, 2);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[2]);
const data: string[] = parseSpacedTableRow(output.split('\n')[3]);
const output = await readOutputStream(client, 3);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[1]);
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
data.pop();
expect(org).toEqual(user.username);

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' });
});
});

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