mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 12:57:46 +00:00
Compare commits
70 Commits
@vercel/ru
...
@vercel/py
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a630e19896 | ||
|
|
00430eeabf | ||
|
|
49f453742b | ||
|
|
bcb8d4f812 | ||
|
|
d42a8a6588 | ||
|
|
301bcf58fb | ||
|
|
11d0091393 | ||
|
|
6405fb51a1 | ||
|
|
edd477e602 | ||
|
|
4f8f8a5b98 | ||
|
|
a8e66eef41 | ||
|
|
23dd29e269 | ||
|
|
4eb4d2b355 | ||
|
|
3590ea06a4 | ||
|
|
314a97b318 | ||
|
|
d41d9e7374 | ||
|
|
80b211fb4a | ||
|
|
ffaf5c9143 | ||
|
|
ba1c2a7e54 | ||
|
|
30a9183836 | ||
|
|
df9accfd6c | ||
|
|
b388357c0b | ||
|
|
90291525c2 | ||
|
|
812dd43b6a | ||
|
|
9e97e0fd58 | ||
|
|
74528c2160 | ||
|
|
82fd2b8068 | ||
|
|
dd94dcab32 | ||
|
|
300e6c6ebb | ||
|
|
cfe6550ac8 | ||
|
|
dfe009ffe2 | ||
|
|
40f38948a0 | ||
|
|
87eba56063 | ||
|
|
d0a5676c26 | ||
|
|
da9fa997ed | ||
|
|
3d79a9d4d4 | ||
|
|
ae13c5ee92 | ||
|
|
ad654139df | ||
|
|
74f8414e12 | ||
|
|
65c2860e14 | ||
|
|
3b5b397b35 | ||
|
|
051e061176 | ||
|
|
30d46321cc | ||
|
|
c2563535ea | ||
|
|
5f2bed4f24 | ||
|
|
cfb7946f4b | ||
|
|
05c5b3a80d | ||
|
|
45bd855250 | ||
|
|
49de8ad9a0 | ||
|
|
a1ea56fd67 | ||
|
|
e88addc9ed | ||
|
|
5d50013f93 | ||
|
|
44e1eb3983 | ||
|
|
f8af013349 | ||
|
|
972cc495ec | ||
|
|
1c580da3d8 | ||
|
|
244554ab1b | ||
|
|
053c185481 | ||
|
|
8805b586ea | ||
|
|
681070ffa0 | ||
|
|
362b17d60a | ||
|
|
c7c9b1a791 | ||
|
|
c42f309463 | ||
|
|
a0ead28369 | ||
|
|
8814fc1515 | ||
|
|
0d044b4eac | ||
|
|
f6bd1aa8c0 | ||
|
|
8cd84ec066 | ||
|
|
a8df231e4c | ||
|
|
f674842bed |
1
.github/CONTRIBUTING.md
vendored
1
.github/CONTRIBUTING.md
vendored
@@ -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
|
||||
|
||||
17
.github/workflows/cancel.yml
vendored
17
.github/workflows/cancel.yml
vendored
@@ -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 }}
|
||||
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
@@ -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: |
|
||||
|
||||
12
.github/workflows/required-pr-label.yml
vendored
12
.github/workflows/required-pr-label.yml
vendored
@@ -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!');
|
||||
|
||||
4
.github/workflows/test-integration-cli.yml
vendored
4
.github/workflows/test-integration-cli.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/test-unit.yml
vendored
4
.github/workflows/test-unit.yml
vendored
@@ -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
|
||||
|
||||
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
@@ -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
|
||||
@@ -78,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
1
.gitignore
vendored
@@ -28,3 +28,4 @@ test/lib/deployment/failed-page.txt
|
||||
__pycache__
|
||||
.vercel
|
||||
.turbo
|
||||
turbo-cache-key.json
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
@@ -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
637
examples/nextjs/package-lock.json
generated
Normal 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": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 →</h2>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -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"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "5.5.1",
|
||||
"version": "5.5.6",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,28 +3,37 @@ import { NodeVersion } from '../types';
|
||||
import { NowBuildError } from '../errors';
|
||||
import debug from '../debug';
|
||||
|
||||
const allOptions = [
|
||||
{ major: 16, range: '16.x', runtime: 'nodejs16.x' },
|
||||
{ major: 14, range: '14.x', runtime: 'nodejs14.x' },
|
||||
{
|
||||
major: 12,
|
||||
range: '12.x',
|
||||
runtime: 'nodejs12.x',
|
||||
discontinueDate: new Date('2022-10-03'),
|
||||
},
|
||||
{
|
||||
major: 10,
|
||||
range: '10.x',
|
||||
runtime: 'nodejs10.x',
|
||||
discontinueDate: new Date('2021-04-20'),
|
||||
},
|
||||
{
|
||||
major: 8,
|
||||
range: '8.10.x',
|
||||
runtime: 'nodejs8.10',
|
||||
discontinueDate: new Date('2020-01-06'),
|
||||
},
|
||||
] as const;
|
||||
function getOptions() {
|
||||
const options = [
|
||||
{ major: 16, range: '16.x', runtime: 'nodejs16.x' },
|
||||
{ major: 14, range: '14.x', runtime: 'nodejs14.x' },
|
||||
{
|
||||
major: 12,
|
||||
range: '12.x',
|
||||
runtime: 'nodejs12.x',
|
||||
discontinueDate: new Date('2022-10-03'),
|
||||
},
|
||||
{
|
||||
major: 10,
|
||||
range: '10.x',
|
||||
runtime: 'nodejs10.x',
|
||||
discontinueDate: new Date('2021-04-20'),
|
||||
},
|
||||
{
|
||||
major: 8,
|
||||
range: '8.10.x',
|
||||
runtime: 'nodejs8.10',
|
||||
discontinueDate: new Date('2020-01-06'),
|
||||
},
|
||||
] as const;
|
||||
if (process.env.VERCEL_ALLOW_NODEJS18 === '1') {
|
||||
return [
|
||||
{ major: 18, range: '18.x', runtime: 'nodejs18.x' },
|
||||
...options,
|
||||
] as const;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
function getHint(isAuto = false) {
|
||||
const { major, range } = getLatestNodeVersion();
|
||||
@@ -34,11 +43,11 @@ function getHint(isAuto = false) {
|
||||
}
|
||||
|
||||
export function getLatestNodeVersion() {
|
||||
return allOptions[0];
|
||||
return getOptions()[0];
|
||||
}
|
||||
|
||||
export function getDiscontinuedNodeVersions(): NodeVersion[] {
|
||||
return allOptions.filter(isDiscontinued);
|
||||
return getOptions().filter(isDiscontinued);
|
||||
}
|
||||
|
||||
export async function getSupportedNodeVersion(
|
||||
@@ -50,7 +59,7 @@ export async function getSupportedNodeVersion(
|
||||
if (engineRange) {
|
||||
const found =
|
||||
validRange(engineRange) &&
|
||||
allOptions.some(o => {
|
||||
getOptions().some(o => {
|
||||
// the array is already in order so return the first
|
||||
// match which will be the newest version of node
|
||||
selection = o;
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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(
|
||||
|
||||
2
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/.gitignore
vendored
Normal file
2
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
.vercel
|
||||
64
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/package-lock.json
generated
vendored
Normal file
64
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/package-lock.json
generated
vendored
Normal 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=="
|
||||
}
|
||||
}
|
||||
}
|
||||
11
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/package.json
vendored
Normal file
11
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/package.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
3
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/probes.json
vendored
Normal file
3
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/probes.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"probes": [{ "path": "/", "mustContain": "legacy peer deps" }]
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
79
packages/build-utils/test/unit.test.ts
vendored
79
packages/build-utils/test/unit.test.ts
vendored
@@ -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 () => {
|
||||
@@ -441,6 +450,38 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
|
||||
global.Date.now = realDateNow;
|
||||
});
|
||||
|
||||
it('should support initialHeaders and initialStatus correctly', async () => {
|
||||
const { Prerender } = require('@vercel/build-utils/dist/prerender.js');
|
||||
new Prerender({
|
||||
expiration: 1,
|
||||
fallback: null,
|
||||
group: 1,
|
||||
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
||||
initialHeaders: {
|
||||
'content-type': 'application/json',
|
||||
'x-initial': 'true',
|
||||
},
|
||||
initialStatus: 308,
|
||||
});
|
||||
new Prerender({
|
||||
expiration: 1,
|
||||
fallback: null,
|
||||
group: 1,
|
||||
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
||||
initialStatus: 308,
|
||||
});
|
||||
new Prerender({
|
||||
expiration: 1,
|
||||
fallback: null,
|
||||
group: 1,
|
||||
bypassToken: 'some-long-bypass-token-to-make-it-work',
|
||||
initialHeaders: {
|
||||
'content-type': 'application/json',
|
||||
'x-initial': 'true',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should support require by path for legacy builders', () => {
|
||||
const index = require('@vercel/build-utils');
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "28.4.3",
|
||||
"version": "28.4.13",
|
||||
"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.1",
|
||||
"@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",
|
||||
"@vercel/build-utils": "5.5.6",
|
||||
"@vercel/go": "2.2.14",
|
||||
"@vercel/hydrogen": "0.0.27",
|
||||
"@vercel/next": "3.2.7",
|
||||
"@vercel/node": "2.6.0",
|
||||
"@vercel/python": "3.1.23",
|
||||
"@vercel/redwood": "1.0.32",
|
||||
"@vercel/remix": "1.0.33",
|
||||
"@vercel/ruby": "1.3.40",
|
||||
"@vercel/static-build": "1.0.33",
|
||||
"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.8",
|
||||
"@vercel/frameworks": "1.1.6",
|
||||
"@vercel/fs-detectors": "3.4.0",
|
||||
"@vercel/client": "12.2.14",
|
||||
"@vercel/error-utils": "1.0.1",
|
||||
"@vercel/frameworks": "1.1.9",
|
||||
"@vercel/fs-detectors": "3.4.6",
|
||||
"@vercel/fun": "1.0.4",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
|
||||
@@ -3,6 +3,7 @@ import chalk from 'chalk';
|
||||
import dotenv from 'dotenv';
|
||||
import { join, normalize, relative, resolve } from 'path';
|
||||
import {
|
||||
getDiscontinuedNodeVersions,
|
||||
normalizePath,
|
||||
Files,
|
||||
FileFsRef,
|
||||
@@ -467,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);
|
||||
|
||||
@@ -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> => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
|
||||
29
packages/cli/src/commands/env/pull.ts
vendored
29
packages/cli/src/commands/env/pull.ts
vendored
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(`
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -23,7 +23,7 @@ import type {
|
||||
} from '../types';
|
||||
import { sharedPromise } from './promise';
|
||||
import { APIError } from './errors-ts';
|
||||
import { normalizeError } from './is-error';
|
||||
import { normalizeError } from '@vercel/error-utils';
|
||||
|
||||
const isSAMLError = (v: any): v is SAMLError => {
|
||||
return v && v.saml;
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
38
packages/cli/src/util/env/get-env-records.ts
vendored
38
packages/cli/src/util/env/get-env-records.ts
vendored
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
2
packages/cli/src/util/env/known-error.ts
vendored
2
packages/cli/src/util/env/known-error.ts
vendored
@@ -1,4 +1,4 @@
|
||||
import { isErrnoException } from '../is-error';
|
||||
import { isErrnoException } from '@vercel/error-utils';
|
||||
|
||||
const knownErrorsCodes = new Set([
|
||||
'PAYMENT_REQUIRED',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
58
packages/cli/src/util/link/ensure-link.ts
Normal file
58
packages/cli/src/util/link/ensure-link.ts
Normal 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 };
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default () => new Response(null, { status: 500 });
|
||||
export default () => new Response('Example Error', { status: 500 });
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export default (req, res) => {
|
||||
res.json(req.headers);
|
||||
};
|
||||
@@ -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',
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -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 }
|
||||
)
|
||||
);
|
||||
|
||||
2
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/.gitignore
vendored
Normal file
2
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.vercel/builders
|
||||
.vercel/output
|
||||
7
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/.vercel/project.json
vendored
Normal file
7
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/.vercel/project.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"orgId": ".",
|
||||
"projectId": ".",
|
||||
"settings": {
|
||||
"framework": null
|
||||
}
|
||||
}
|
||||
1
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/api/index.php
vendored
Normal file
1
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/api/index.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<?php echo 'This version of vercel-php uses the nodejs12.x Lambda Runtime'; ?>
|
||||
8
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/vercel.json
vendored
Normal file
8
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/vercel.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||
"functions": {
|
||||
"api/index.php": {
|
||||
"runtime": "vercel-php@0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -776,6 +776,55 @@ describe('build', () => {
|
||||
}
|
||||
});
|
||||
|
||||
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'));
|
||||
expect(configJson.version).toBe(3);
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
delete process.env.__VERCEL_BUILD_RUNNING;
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow for missing "build" script', async () => {
|
||||
const cwd = fixture('static-with-pkg');
|
||||
const output = join(cwd, '.vercel/output');
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
77
packages/cli/test/unit/util/dev/headers.test.ts
Normal file
77
packages/cli/test/unit/util/dev/headers.test.ts
Normal 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' });
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "12.2.8",
|
||||
"version": "12.2.14",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -43,8 +43,8 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "5.5.1",
|
||||
"@vercel/routing-utils": "2.0.2",
|
||||
"@vercel/build-utils": "5.5.6",
|
||||
"@vercel/routing-utils": "2.1.0",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Agent } from 'https';
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
import { Readable } from 'stream';
|
||||
import { EventEmitter } from 'events';
|
||||
import retry from 'async-retry';
|
||||
@@ -78,7 +79,9 @@ export async function* upload(
|
||||
debug('Building an upload list...');
|
||||
|
||||
const semaphore = new Sema(50, { capacity: 50 });
|
||||
const agent = new Agent({ keepAlive: true });
|
||||
const agent = apiUrl?.startsWith('https://')
|
||||
? new https.Agent({ keepAlive: true })
|
||||
: new http.Agent({ keepAlive: true });
|
||||
|
||||
shas.forEach((sha, index) => {
|
||||
const uploadProgress = uploads[index];
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
- [ExtraResponseInit](interfaces/ExtraResponseInit.md)
|
||||
- [Geo](interfaces/Geo.md)
|
||||
- [ModifiedRequest](interfaces/ModifiedRequest.md)
|
||||
|
||||
### Variables
|
||||
|
||||
@@ -15,6 +16,7 @@
|
||||
- [LATITUDE_HEADER_NAME](README.md#latitude_header_name)
|
||||
- [LONGITUDE_HEADER_NAME](README.md#longitude_header_name)
|
||||
- [REGION_HEADER_NAME](README.md#region_header_name)
|
||||
- [REQUEST_ID_HEADER_NAME](README.md#request_id_header_name)
|
||||
|
||||
### Functions
|
||||
|
||||
@@ -89,11 +91,25 @@ Longitude of the original client IP as calculated by Vercel Proxy.
|
||||
|
||||
• `Const` **REGION_HEADER_NAME**: `"x-vercel-ip-country-region"`
|
||||
|
||||
Region of the original client IP as calculated by Vercel Proxy.
|
||||
Country region of the original client IP calculated by Vercel Proxy.
|
||||
|
||||
See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region).
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/edge-headers.ts:24](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L24)
|
||||
[src/edge-headers.ts:26](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L26)
|
||||
|
||||
---
|
||||
|
||||
### REQUEST_ID_HEADER_NAME
|
||||
|
||||
• `Const` **REQUEST_ID_HEADER_NAME**: `"x-vercel-id"`
|
||||
|
||||
The request ID for each request generated by Vercel Proxy.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/edge-headers.ts:30](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L30)
|
||||
|
||||
## Functions
|
||||
|
||||
@@ -123,7 +139,7 @@ Returns the location information for the incoming request.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/edge-headers.ts:80](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L80)
|
||||
[src/edge-headers.ts:106](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L106)
|
||||
|
||||
---
|
||||
|
||||
@@ -149,7 +165,7 @@ Returns the IP address of the request from the headers.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/edge-headers.ts:66](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L66)
|
||||
[src/edge-headers.ts:77](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L77)
|
||||
|
||||
---
|
||||
|
||||
@@ -197,7 +213,7 @@ export default function middleware(_req: Request) {
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/middleware-helpers.ts:94](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L94)
|
||||
[src/middleware-helpers.ts:145](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L145)
|
||||
|
||||
---
|
||||
|
||||
@@ -259,4 +275,4 @@ export const config = { matcher: '/api/users/:path*' };
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/middleware-helpers.ts:53](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L53)
|
||||
[src/middleware-helpers.ts:101](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L101)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
### Properties
|
||||
|
||||
- [headers](ExtraResponseInit.md#headers)
|
||||
- [request](ExtraResponseInit.md#request)
|
||||
- [status](ExtraResponseInit.md#status)
|
||||
- [statusText](ExtraResponseInit.md#statustext)
|
||||
|
||||
@@ -25,7 +26,19 @@ along with the response headers from the origin.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/middleware-helpers.ts:6](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L6)
|
||||
[src/middleware-helpers.ts:31](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L31)
|
||||
|
||||
---
|
||||
|
||||
### request
|
||||
|
||||
• `Optional` **request**: [`ModifiedRequest`](ModifiedRequest.md)
|
||||
|
||||
Fields to rewrite for the upstream request.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/middleware-helpers.ts:35](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L35)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ The location information of a given request.
|
||||
|
||||
- [city](Geo.md#city)
|
||||
- [country](Geo.md#country)
|
||||
- [countryRegion](Geo.md#countryregion)
|
||||
- [latitude](Geo.md#latitude)
|
||||
- [longitude](Geo.md#longitude)
|
||||
- [region](Geo.md#region)
|
||||
@@ -22,7 +23,7 @@ The city that the request originated from.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/edge-headers.ts:41](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L41)
|
||||
[src/edge-headers.ts:47](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L47)
|
||||
|
||||
---
|
||||
|
||||
@@ -34,7 +35,20 @@ The country that the request originated from.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/edge-headers.ts:44](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L44)
|
||||
[src/edge-headers.ts:50](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L50)
|
||||
|
||||
---
|
||||
|
||||
### countryRegion
|
||||
|
||||
• `Optional` **countryRegion**: `string`
|
||||
|
||||
The region part of the ISO 3166-2 code of the client IP.
|
||||
See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region).
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/edge-headers.ts:58](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L58)
|
||||
|
||||
---
|
||||
|
||||
@@ -46,7 +60,7 @@ The latitude of the client.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/edge-headers.ts:50](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L50)
|
||||
[src/edge-headers.ts:61](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L61)
|
||||
|
||||
---
|
||||
|
||||
@@ -58,7 +72,7 @@ The longitude of the client.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/edge-headers.ts:53](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L53)
|
||||
[src/edge-headers.ts:64](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L64)
|
||||
|
||||
---
|
||||
|
||||
@@ -70,4 +84,4 @@ The [Vercel Edge Network region](https://vercel.com/docs/concepts/edge-network/r
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/edge-headers.ts:47](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L47)
|
||||
[src/edge-headers.ts:53](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L53)
|
||||
|
||||
38
packages/edge/docs/interfaces/ModifiedRequest.md
Normal file
38
packages/edge/docs/interfaces/ModifiedRequest.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Interface: ModifiedRequest
|
||||
|
||||
## Table of contents
|
||||
|
||||
### Properties
|
||||
|
||||
- [headers](ModifiedRequest.md#headers)
|
||||
|
||||
## Properties
|
||||
|
||||
### headers
|
||||
|
||||
• `Optional` **headers**: `Headers`
|
||||
|
||||
If set, overwrites the incoming headers to the origin request.
|
||||
|
||||
This is useful when you want to pass data between a Middleware and a
|
||||
Serverless or Edge Function.
|
||||
|
||||
**`Example`**
|
||||
|
||||
<caption>Add a `x-user-id` header and remove the `Authorization` header</caption>
|
||||
|
||||
```ts
|
||||
import { rewrite } from '@vercel/edge';
|
||||
export default async function middleware(request: Request): Promise<Response> {
|
||||
const newHeaders = new Headers(request.headers);
|
||||
newHeaders.set('x-user-id', 'user_123');
|
||||
newHeaders.delete('authorization');
|
||||
return rewrite(request.url, {
|
||||
request: { headers: newHeaders },
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/middleware-helpers.ts:23](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L23)
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/edge",
|
||||
"version": "0.0.4",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.mjs",
|
||||
@@ -12,7 +12,7 @@
|
||||
"build:docs": "typedoc && prettier --write docs/**/*.md docs/*.md"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edge-runtime/jest-environment": "1.1.0-beta.7",
|
||||
"@edge-runtime/jest-environment": "2.0.0",
|
||||
"@types/jest": "27.4.1",
|
||||
"ts-node": "8.9.1",
|
||||
"tsup": "6.1.2",
|
||||
|
||||
@@ -19,9 +19,15 @@ export const LATITUDE_HEADER_NAME = 'x-vercel-ip-latitude';
|
||||
*/
|
||||
export const LONGITUDE_HEADER_NAME = 'x-vercel-ip-longitude';
|
||||
/**
|
||||
* Region of the original client IP as calculated by Vercel Proxy.
|
||||
* Country region of the original client IP calculated by Vercel Proxy.
|
||||
*
|
||||
* See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region).
|
||||
*/
|
||||
export const REGION_HEADER_NAME = 'x-vercel-ip-country-region';
|
||||
/**
|
||||
* The request ID for each request generated by Vercel Proxy.
|
||||
*/
|
||||
export const REQUEST_ID_HEADER_NAME = 'x-vercel-id';
|
||||
|
||||
/**
|
||||
* We define a new type so this function can be reused with
|
||||
@@ -46,6 +52,11 @@ export interface Geo {
|
||||
/** The [Vercel Edge Network region](https://vercel.com/docs/concepts/edge-network/regions) that received the request. */
|
||||
region?: string;
|
||||
|
||||
/** The region part of the ISO 3166-2 code of the client IP.
|
||||
* See [docs](https://vercel.com/docs/concepts/edge-network/headers#x-vercel-ip-country-region).
|
||||
*/
|
||||
countryRegion?: string;
|
||||
|
||||
/** The latitude of the client. */
|
||||
latitude?: string;
|
||||
|
||||
@@ -67,6 +78,21 @@ export function ipAddress(request: Request): string | undefined {
|
||||
return getHeader(request, IP_HEADER_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Vercel Edge Network region name from the request ID.
|
||||
*
|
||||
* @param requestId The request ID (`x-vercel-id`).
|
||||
* @returns The first region received the client request.
|
||||
*/
|
||||
function getRegionFromRequestId(requestId?: string): string | undefined {
|
||||
if (!requestId) {
|
||||
return 'dev1';
|
||||
}
|
||||
|
||||
// The request ID is in the format of `region::id` or `region1:region2:...::id`.
|
||||
return requestId.split(':')[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location information for the incoming request.
|
||||
*
|
||||
@@ -81,7 +107,8 @@ export function geolocation(request: Request): Geo {
|
||||
return {
|
||||
city: getHeader(request, CITY_HEADER_NAME),
|
||||
country: getHeader(request, COUNTRY_HEADER_NAME),
|
||||
region: getHeader(request, REGION_HEADER_NAME),
|
||||
countryRegion: getHeader(request, REGION_HEADER_NAME),
|
||||
region: getRegionFromRequestId(getHeader(request, REQUEST_ID_HEADER_NAME)),
|
||||
latitude: getHeader(request, LATITUDE_HEADER_NAME),
|
||||
longitude: getHeader(request, LONGITUDE_HEADER_NAME),
|
||||
};
|
||||
|
||||
@@ -1,9 +1,57 @@
|
||||
export interface ModifiedRequest {
|
||||
/**
|
||||
* If set, overwrites the incoming headers to the origin request.
|
||||
*
|
||||
* This is useful when you want to pass data between a Middleware and a
|
||||
* Serverless or Edge Function.
|
||||
*
|
||||
* @example
|
||||
* <caption>Add a `x-user-id` header and remove the `Authorization` header</caption>
|
||||
*
|
||||
* ```ts
|
||||
* import { rewrite } from '@vercel/edge';
|
||||
* export default async function middleware(request: Request): Promise<Response> {
|
||||
* const newHeaders = new Headers(request.headers);
|
||||
* newHeaders.set('x-user-id', 'user_123');
|
||||
* newHeaders.delete('authorization');
|
||||
* return rewrite(request.url, {
|
||||
* request: { headers: newHeaders }
|
||||
* })
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
headers?: Headers;
|
||||
}
|
||||
|
||||
export interface ExtraResponseInit extends Omit<ResponseInit, 'headers'> {
|
||||
/**
|
||||
* These headers will be sent to the user response
|
||||
* along with the response headers from the origin.
|
||||
*/
|
||||
headers?: HeadersInit;
|
||||
/**
|
||||
* Fields to rewrite for the upstream request.
|
||||
*/
|
||||
request?: ModifiedRequest;
|
||||
}
|
||||
|
||||
function handleMiddlewareField(
|
||||
init: ExtraResponseInit | undefined,
|
||||
headers: Headers
|
||||
) {
|
||||
if (init?.request?.headers) {
|
||||
if (!(init.request.headers instanceof Headers)) {
|
||||
throw new Error('request.headers must be an instance of Headers');
|
||||
}
|
||||
|
||||
const keys = [];
|
||||
for (const [key, value] of init.request.headers) {
|
||||
headers.set('x-middleware-request-' + key, value);
|
||||
keys.push(key);
|
||||
}
|
||||
|
||||
headers.set('x-middleware-override-headers', keys.join(','));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,6 +104,9 @@ export function rewrite(
|
||||
): Response {
|
||||
const headers = new Headers(init?.headers ?? {});
|
||||
headers.set('x-middleware-rewrite', String(destination));
|
||||
|
||||
handleMiddlewareField(init, headers);
|
||||
|
||||
return new Response(null, {
|
||||
...init,
|
||||
headers,
|
||||
@@ -94,6 +145,9 @@ export function rewrite(
|
||||
export function next(init?: ExtraResponseInit): Response {
|
||||
const headers = new Headers(init?.headers ?? {});
|
||||
headers.set('x-middleware-next', '1');
|
||||
|
||||
handleMiddlewareField(init, headers);
|
||||
|
||||
return new Response(null, {
|
||||
...init,
|
||||
headers,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user