Compare commits

...

23 Commits

Author SHA1 Message Date
Steven
d5537500d8 Publish Stable
- @vercel/build-utils@5.4.2
 - vercel@28.2.3
 - @vercel/client@12.2.4
 - @vercel/frameworks@1.1.4
 - @vercel/fs-detectors@3.0.0
 - @vercel/go@2.2.5
 - @vercel/hydrogen@0.0.18
 - @vercel/next@3.1.25
 - @vercel/node@2.5.14
 - @vercel/python@3.1.14
 - @vercel/redwood@1.0.23
 - @vercel/remix@1.0.24
 - @vercel/ruby@1.3.31
 - @vercel/static-build@1.0.22
2022-09-07 15:39:47 -04:00
Andrew Gadzik
4b1736b2f2 [fs-detectors] optimize readdir for framework detection (#8497)
Adding the ability to explicitly set the `hasPath` and `isFile` caches via `readdir`

**Why is this needed?**

For framework detection, this allows us to fetch the contents of a directory (the root directory) of a git repo, and make assumptions about the existence of files,

```ts
import frameworks from "@vercel/frameworks";

// we can make the set of all file paths needed for framework detection like this
const setOfPaths = new Set(
  frameworks.frameworks
    .flatMap((f) => [
      ...(f.detectors?.every ?? []),
      ...(f.detectors?.some ?? []),
    ])
    .map((d) => d.path)
);

// then we can read the contents of the root directory of a git repo via the `DetectorFilesystem`
fs.readdir('./', { potentialFiles: [...setOfPaths] });

// The filesystem has been fully primed - any network calls from here will only happen if needed.
// This works because the logic of `detectFramework` calls `hasPath` and then `isFile` before 
// fetching file contents.  Since the filesystem knows this information, it doesn't have to make 
// any additional network calls 🙌 
const framework = detectFramework(fs);
```

### Related Issues

Related to [HIT-57](https://linear.app/vercel/issue/HIT-57/monorepo-detection-api-prevent-rate-limits)

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2022-09-07 15:35:56 -04:00
Sean Massa
9da1c6fa66 [tests] set retry count to 0 when running tests locally (#8529)
When running tests locally that fail to make a fetch request, the retries add a lot of noise to debugging. This PR sets those retry counts to `0` locally, but keeps them at their current value for CI.
2022-09-07 18:47:09 +00:00
Jiachi Liu
a6c320846e [@vercel/next] Handle nextjs edge routes from app dir (#8524)
### Related Issues

Add handling for edge SSR in app dir, normalize the edge page routes
from app dir, like what we did for ones in `pages/`.

### 📋 Checklist

#### Tests

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

#### Code Review

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

Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-09-06 16:19:28 -07:00
Shu Uesugi
4973814978 [frameworks] Update Next.js Logos (#8505)
Update them to match the new logos. See: https://vercel.com/design/brands#next-js

<img width="869" alt="Screen Shot 2022-09-02 at 10 43 52 AM" src="https://user-images.githubusercontent.com/992008/188208956-aae10437-77ef-43c8-9bc1-c2499e509a7c.png">
2022-09-02 18:06:31 +00:00
Sean Massa
f1289ff263 [node] update middleware-no-response behavior to match production changes (#8500)
Production was updated to allow middleware without responses to pass through. This PR makes `vc dev` behave the same way. Tests were updated.
2022-09-01 22:19:10 +00:00
Steven
3ff93279cd Publish Stable
- vercel@28.2.2
 - @vercel/next@3.1.24
 - @vercel/node@2.5.13
 - @vercel/redwood@1.0.22
 - @vercel/remix@1.0.23
2022-09-01 16:42:27 -04:00
Steven
58f9d649a8 [next][node][remix][redwood] Bump @vercel/nft@0.22.1 (#8502)
Bump `@vercel/nft` to version [0.22.1](https://github.com/vercel/nft/releases/tag/0.22.1)
2022-09-01 16:41:30 -04:00
JJ Kasper
cadc082ad1 [next] Fix index app dir case (#8501)
### Related Issues

This ensures we properly normalize the `/` case from the `app-path-routes-manifest` as the outputs expect this to be normalized to `index`. 

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-01 12:06:18 -07:00
Steven
2906d83eae Publish Stable
- @vercel/build-utils@5.4.1
 - vercel@28.2.1
 - @vercel/client@12.2.3
 - @vercel/edge@0.0.4
 - @vercel/fs-detectors@2.1.0
 - @vercel/go@2.2.4
 - @vercel/hydrogen@0.0.17
 - @vercel/next@3.1.23
 - @vercel/node@2.5.12
 - @vercel/python@3.1.13
 - @vercel/redwood@1.0.21
 - @vercel/remix@1.0.22
 - @vercel/ruby@1.3.30
 - @vercel/static-build@1.0.21
2022-09-01 13:16:42 -04:00
Steven
675c3e2915 [cli] Change error message from Error! to Error: (#8498)
We have code that tries to detect and highlight errors in the build logs, however it doesn't look for `Error!`, only `Error:`.

We could update that highlight code or we could update Vercel CLI to make it consistent.

This PR is the latter.
2022-09-01 13:12:41 -04:00
Steven
13a8a7dbf6 [next] Revert nft to 0.21.0 (#8499)
In Next.js 12, we introduce `outputFileTracing` as the default behavior. This moved `@vercel/nft` from `@vercel/next` into `next` itself so users can upgrade or downgrade Next.js if there is a bug in `@vercel/nft`.

Unfortunately, some users are setting `outputFileTracing: false` which deopts tracing back from `next` to `@vercel/next`. So we should pin `@vercel/nft` here and never upgrade in case of any bugs.
2022-09-01 09:13:35 -07:00
Andrew Gadzik
b480e632a3 Add writeFile to DetectorFilesystem (#8493)
Adds a `writeFile` function to `DetectorFilesystem` that will be used to update the various file cache maps.

When detecting npm7+ monorepos, we identified a performance improvement where the service can inspect the `package-lock.json` file for workspaces, and reuse the package information for each workspace in framework-detection.

The pseudo code in `vercel/api` will look something like this

For a given lockfile
```json
{
  ...,
  "packages": {
    "": {
      "name": "npm-workspaces",
      "version": "1.0.0",
      "license": "ISC",
      "workspaces": {
        "packages": [
          "apps/*"
        ]
      }
    },
    "apps/admin": {
      "version": "0.1.0",
      "dependencies": {
        "next": "12.2.5",
        "react": "18.2.0",
        "react-dom": "18.2.0"
      },
      "devDependencies": {
        "eslint": "8.23.0",
        "eslint-config-next": "12.2.5"
      }
    },
    ...,
}
```

```ts
// for each projectPath we detect in package-lock.json
// switch the cwd of the fs to the project directory
const projectFs = fs.chdir(projectPath);
// gets the package info from the lockfile
const projectPackageInfo = lockFile.packages[projectPath];
// insert this content into fs cache
projectFs.writeFile('package.json', projectPackageInfo)
// call detectFramework, which should now have a cached "package.json" file 
const projectFramework = await detectFramework(projectFs);
```

### Related Issues

Related to [HIT-57](https://linear.app/vercel/issue/HIT-57/monorepo-detection-api-prevent-rate-limits)

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-31 18:51:03 +00:00
Chris Barber
bc6c364888 [node] Added 'cause' to error message (#8467)
When running `vc dev` for a project using `experimental-edge` runtime and a fetch-related error occurs, the message is a little vague:

```
Unhandled rejection: fetch failed
```

In this case, the fetch promise (defined in @edge-runtime/primitives) is being rejected with a `TypeError: fetch failed` that has a `cause` property containing the actual `response.error` message. The `response.error` is the actual error and is never reported to the user.

This PR simply appends the `cause` message, if exists, to the fetch failure message.

```
Unhandled rejection: fetch failed: Error: getaddrinfo ENOTFOUND undefined
```

### 📋 Checklist

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-30 02:32:38 +00:00
Steven
6cad10c899 [build-utils] Replace npm bin with custom implementation (#8483)
This PR removes `npm bin` in favor of a custom implementation since the command will be removed in npm 9.

- Related to https://github.com/npm/statusboard/issues/537
2022-08-30 01:23:34 +00:00
Gal Schlezinger
0d78ec4666 [edge] Add markdown documentation using TypeDoc (#8446) 2022-08-29 12:36:44 +03:00
Nathan Rajlich
b05d653cf1 [cli] Use unique "name" in package.json test fixtures (#8475)
This fixes a few warnings when running Jest that look something like:

```
jest-haste-map: Haste module naming collision: nextjs
  The following files share their name; please adjust your hasteImpl:
    * <rootDir>/test/dev/fixtures/10-nextjs-node/package.json
    * <rootDir>/test/dev/fixtures/25-nextjs-src-dir/package.json
```
2022-08-26 21:43:25 +00:00
Nathan Rajlich
5a38b94de2 [cli] Replace "progress" module with custom implementation (#8397)
The `progress` module is overkill for what we are using it for and it's inconsistent with our styling for the rest of the deployment process.

So drop the npm module in favor of an in-house progress bar implementation that can be used inside of the Ora `output.spinner()` that we already use for the other steps of the deployment process.

**Before:**

<img width="307" alt="Screen Shot 2022-08-25 at 5 07 28 PM" src="https://user-images.githubusercontent.com/71256/186790052-18431ac0-b05b-434e-b885-9eb76d880b9d.png">

**After:**

<img width="347" alt="Screen Shot 2022-08-25 at 5 09 35 PM" src="https://user-images.githubusercontent.com/71256/186790168-8dd3e9a2-863a-45cc-bd21-d98308d34e22.png">
2022-08-26 21:03:38 +00:00
Nathan Rajlich
6668d6cf21 [client] Add "tar-fs" dependency (#8473)
Fixes #8471.
2022-08-26 18:02:01 +00:00
Andrew Gadzik
b63f444abd [fs-detectors] Add a default workspace manager when there's no lock file (#8469)
This fixes an issue with this example,

https://vercel.com/api/v1/integrations/detect-eligible-projects?url=https%3A%2F%2Fgithub.com%2Fvercel%2Fturborepo%2Ftree%2Fmain%2Fexamples%2Fkitchen-sink

Where the repo has no lockfile, and hence our workspace detection fails.  This fix is to add a fallback of "npm" in this scenario

### Related Issues

Fixes https://github.com/vercel/turbo-internal/issues/232

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-26 16:24:13 +00:00
Steven
9d71d4332f [tests] Update codeowners for fs-detectors (#8470)
Update codeowners for `@vercel/fs-detectors` to add @agadzik and @chloetedder
2022-08-26 15:43:14 +00:00
Steven
151db21a2f [tests] Add required labels (#8464)
This action will fail if the required labels are not applied to a PR.

We allow multiple `area` labels and only a single `semver` label.

- Example failure: https://github.com/vercel/vercel/runs/8024949804?check_suite_focus=true#step:2:19
- Example success: https://github.com/vercel/vercel/runs/8026212031?check_suite_focus=true#step:2:19
2022-08-25 22:20:37 +00:00
Steven
aba9c95ea2 [tests] Change PR automerge label (#8465)
We decided to change this to an explicit label and rely on a separate action to check for correct labels (see #8464).

This action likely won't work for PRs from forks, but that might be okay because our other actions don't work for forks either.
2022-08-25 22:05:55 +00:00
78 changed files with 1137 additions and 290 deletions

4
.github/CODEOWNERS vendored
View File

@@ -6,10 +6,12 @@
/packages/cli/src/commands/domains @mglagola @anatrajkovska
/packages/cli/src/commands/certs @mglagola @anatrajkovska
/packages/cli/src/commands/env @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood
/packages/middleware @gdborton @javivelasco @Schniz
/packages/fs-detectors @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @agadzik @chloetedder
/packages/middleware @gdborton @vercel/edge-function
/packages/node-bridge @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
/packages/next @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
/packages/routing-utils @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
/packages/edge @vercel/edge-function
/examples @leerob
/examples/create-react-app @Timer
/examples/nextjs @timneutkens @ijjk @styfle

22
.github/workflows/required-pr-label.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Required PR Label
on:
pull_request:
types: [opened, labeled, unlabeled, synchronize]
jobs:
label:
runs-on: ubuntu-latest
steps:
- name: Check PR Labels
uses: actions/github-script@v6
with:
script: |
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);
}
if (labels.filter(l => l.startsWith('semver:')).length !== 1) {
console.error('\u001b[31mMissing label: Please add exactly one "semver" label.');
process.exit(1);
}
console.log('\u001b[32mSuccess: This pull request has correct labels, thanks!');

View File

@@ -1,7 +1,7 @@
version = 1
[merge]
automerge_label = ["semver-major","semver-minor","semver-patch"]
automerge_label = ["pr: automerge"]
blacklist_title_regex = "^WIP.*"
blacklist_labels = ["work in progress"]
method = "squash"

View File

@@ -8,7 +8,10 @@
"packages/*"
],
"nohoist": [
"**/@types/**"
"**/@types/**",
"**/typedoc",
"**/typedoc-plugin-markdown",
"**/typedoc-plugin-mdn-links"
]
},
"dependencies": {

View File

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

View File

@@ -23,8 +23,7 @@ export interface ScanParentDirsResult {
*/
cliType: CliType;
/**
* The file path of found `package.json` file, or `undefined` if none was
* found.
* The file path of found `package.json` file, or `undefined` if not found.
*/
packageJsonPath?: string;
/**
@@ -33,8 +32,13 @@ export interface ScanParentDirsResult {
*/
packageJson?: PackageJson;
/**
* The `lockfileVersion` number from the `package-lock.json` file,
* when present.
* The file path of the lockfile (`yarn.lock`, `package-lock.json`, or `pnpm-lock.yaml`)
* or `undefined` if not found.
*/
lockfilePath?: string;
/**
* The `lockfileVersion` number from lockfile (`package-lock.json` or `pnpm-lock.yaml`),
* or `undefined` if not found.
*/
lockfileVersion?: number;
}
@@ -178,25 +182,9 @@ export async function getNodeBinPath({
}: {
cwd: string;
}): Promise<string> {
const { code, stdout, stderr } = await execAsync('npm', ['bin'], {
cwd,
prettyCommand: 'npm bin',
// in some rare cases, we saw `npm bin` exit with a non-0 code, but still
// output the right bin path, so we ignore the exit code
ignoreNon0Exit: true,
});
const nodeBinPath = stdout.trim();
if (path.isAbsolute(nodeBinPath)) {
return nodeBinPath;
}
throw new NowBuildError({
code: `BUILD_UTILS_GET_NODE_BIN_PATH`,
message: `Running \`npm bin\` failed to return a valid bin path (code=${code}, stdout=${stdout}, stderr=${stderr})`,
});
const { lockfilePath } = await scanParentDirs(cwd);
const dir = path.dirname(lockfilePath || cwd);
return path.join(dir, 'node_modules', '.bin');
}
async function chmodPlusX(fsPath: string) {
@@ -319,6 +307,7 @@ export async function scanParentDirs(
start: destPath,
filenames: ['yarn.lock', 'package-lock.json', 'pnpm-lock.yaml'],
});
let lockfilePath: string | undefined;
let lockfileVersion: number | undefined;
let cliType: CliType = 'yarn';
@@ -335,17 +324,25 @@ export async function scanParentDirs(
// Priority order is Yarn > pnpm > npm
if (hasYarnLock) {
cliType = 'yarn';
lockfilePath = yarnLockPath;
} else if (pnpmLockYaml) {
cliType = 'pnpm';
// just ensure that it is read as a number and not a string
lockfilePath = pnpmLockPath;
lockfileVersion = Number(pnpmLockYaml.lockfileVersion);
} else if (packageLockJson) {
cliType = 'npm';
lockfilePath = npmLockPath;
lockfileVersion = packageLockJson.lockfileVersion;
}
const packageJsonPath = pkgJsonPath || undefined;
return { cliType, packageJson, lockfileVersion, packageJsonPath };
return {
cliType,
packageJson,
lockfilePath,
lockfileVersion,
packageJsonPath,
};
}
export async function walkParentDirs({

View File

@@ -41,6 +41,7 @@ export interface Config {
devCommand?: string;
framework?: string | null;
nodeVersion?: string;
middleware?: boolean;
[key: string]: unknown;
}

View File

@@ -0,0 +1,46 @@
import { join, parse } from 'path';
import { getNodeBinPath } from '../src';
describe('Test `getNodeBinPath()`', () => {
it('should work with npm7', async () => {
const cwd = join(__dirname, 'fixtures', '20-npm-7');
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, 'node_modules', '.bin'));
});
it('should work with yarn', async () => {
const cwd = join(__dirname, 'fixtures', '19-yarn-v2');
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, 'node_modules', '.bin'));
});
it('should work with npm 6', async () => {
const cwd = join(__dirname, 'fixtures', '08-yarn-npm/with-npm');
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, 'node_modules', '.bin'));
});
it('should work with npm workspaces', async () => {
const cwd = join(__dirname, 'fixtures', '21-npm-workspaces/a');
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, '..', 'node_modules', '.bin'));
});
it('should work with pnpm', async () => {
const cwd = join(__dirname, 'fixtures', '22-pnpm');
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, 'node_modules', '.bin'));
});
it('should work with pnpm workspaces', async () => {
const cwd = join(__dirname, 'fixtures', '23-pnpm-workspaces/c');
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, '..', 'node_modules', '.bin'));
});
it('should fallback to cwd if no lockfile found', async () => {
const cwd = parse(process.cwd()).root;
const result = await getNodeBinPath({ cwd });
expect(result).toBe(join(cwd, 'node_modules', '.bin'));
});
});

View File

@@ -501,6 +501,7 @@ it('should return lockfileVersion 2 with npm7', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('npm');
expect(result.lockfileVersion).toEqual(2);
expect(result.lockfilePath).toEqual(path.join(fixture, 'package-lock.json'));
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -509,6 +510,7 @@ it('should not return lockfileVersion with yarn', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('yarn');
expect(result.lockfileVersion).toEqual(undefined);
expect(result.lockfilePath).toEqual(path.join(fixture, 'yarn.lock'));
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -517,6 +519,7 @@ it('should return lockfileVersion 1 with older versions of npm', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('npm');
expect(result.lockfileVersion).toEqual(1);
expect(result.lockfilePath).toEqual(path.join(fixture, 'package-lock.json'));
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -525,6 +528,9 @@ it('should detect npm Workspaces', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('npm');
expect(result.lockfileVersion).toEqual(2);
expect(result.lockfilePath).toEqual(
path.join(fixture, '..', 'package-lock.json')
);
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -533,6 +539,7 @@ it('should detect pnpm without workspace', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('pnpm');
expect(result.lockfileVersion).toEqual(5.3);
expect(result.lockfilePath).toEqual(path.join(fixture, 'pnpm-lock.yaml'));
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -541,6 +548,9 @@ it('should detect pnpm with workspaces', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('pnpm');
expect(result.lockfileVersion).toEqual(5.3);
expect(result.lockfilePath).toEqual(
path.join(fixture, '..', 'pnpm-lock.yaml')
);
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -552,6 +562,7 @@ it('should detect package.json in nested backend', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('yarn');
expect(result.lockfileVersion).toEqual(undefined);
// There is no lockfile but this test will pick up vercel/vercel/yarn.lock
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
@@ -563,6 +574,7 @@ it('should detect package.json in nested frontend', async () => {
const result = await scanParentDirs(fixture);
expect(result.cliType).toEqual('yarn');
expect(result.lockfileVersion).toEqual(undefined);
// There is no lockfile but this test will pick up vercel/vercel/yarn.lock
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.2.0",
"version": "28.2.3",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -41,16 +41,16 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "5.4.0",
"@vercel/go": "2.2.3",
"@vercel/hydrogen": "0.0.16",
"@vercel/next": "3.1.22",
"@vercel/node": "2.5.11",
"@vercel/python": "3.1.12",
"@vercel/redwood": "1.0.20",
"@vercel/remix": "1.0.21",
"@vercel/ruby": "1.3.29",
"@vercel/static-build": "1.0.20",
"@vercel/build-utils": "5.4.2",
"@vercel/go": "2.2.5",
"@vercel/hydrogen": "0.0.18",
"@vercel/next": "3.1.25",
"@vercel/node": "2.5.14",
"@vercel/python": "3.1.14",
"@vercel/redwood": "1.0.23",
"@vercel/remix": "1.0.24",
"@vercel/ruby": "1.3.31",
"@vercel/static-build": "1.0.22",
"update-notifier": "5.1.0"
},
"devDependencies": {
@@ -85,7 +85,6 @@
"@types/node-fetch": "2.5.10",
"@types/npm-package-arg": "6.1.0",
"@types/pluralize": "0.0.29",
"@types/progress": "2.0.3",
"@types/psl": "1.1.0",
"@types/semver": "6.0.1",
"@types/tar-fs": "1.16.1",
@@ -96,9 +95,9 @@
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.2.2",
"@vercel/frameworks": "1.1.3",
"@vercel/fs-detectors": "2.0.5",
"@vercel/client": "12.2.4",
"@vercel/frameworks": "1.1.4",
"@vercel/fs-detectors": "3.0.0",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@zeit/source-map-support": "0.6.2",
@@ -153,7 +152,6 @@
"ora": "3.4.0",
"pcre-to-regexp": "1.0.0",
"pluralize": "7.0.0",
"progress": "2.0.3",
"promisepipe": "3.0.0",
"psl": "1.1.31",
"qr-image": "3.2.0",

View File

@@ -4,7 +4,7 @@ const { statSync } = require('fs');
const pkg = require('../package');
function error(command) {
console.error('> Error!', command);
console.error('> Error:', command);
}
function debug(str) {

View File

@@ -7,7 +7,7 @@ try {
process.cwd();
} catch (err: unknown) {
if (isError(err) && err.message.includes('uv_cwd')) {
console.error('Error! The current working directory does not exist.');
console.error('Error: The current working directory does not exist.');
process.exit(1);
}
}

View File

@@ -1,5 +1,4 @@
import bytes from 'bytes';
import Progress from 'progress';
import chalk from 'chalk';
import {
ArchiveFormat,
@@ -8,6 +7,7 @@ import {
VercelClientOptions,
} from '@vercel/client';
import { Output } from '../output';
import { progress } from '../output/progress';
import Now from '../../util';
import { Org } from '../../types';
import ua from '../ua';
@@ -68,7 +68,6 @@ export default async function processDeployment({
} = args;
const { debug } = output;
let bar: Progress | null = null;
const { env = {} } = requestBody;
@@ -114,32 +113,28 @@ export default async function processDeployment({
const missingSize = missing
.map((sha: string) => total.get(sha).data.length)
.reduce((a: number, b: number) => a + b, 0);
output.stopSpinner();
bar = new Progress(`${chalk.gray('>')} Upload [:bar] :percent :etas`, {
width: 20,
complete: '=',
incomplete: '',
total: missingSize,
clear: true,
});
bar.tick(0);
const totalSizeHuman = bytes.format(missingSize, { decimalPlaces: 1 });
uploads.forEach((e: any) =>
e.on('progress', () => {
if (!bar) return;
const totalBytesUploaded = uploads.reduce((acc: number, e: any) => {
const uploadedBytes = uploads.reduce((acc: number, e: any) => {
return acc + e.bytesUploaded;
}, 0);
// set the current progress bar value
bar.curr = totalBytesUploaded;
// trigger rendering
bar.tick(0);
if (bar.complete) {
const bar = progress(uploadedBytes, missingSize);
if (!bar || uploadedBytes === missingSize) {
output.spinner(deployingSpinnerVal, 0);
} else {
const uploadedHuman = bytes.format(uploadedBytes, {
decimalPlaces: 1,
fixedDecimals: true,
});
output.spinner(
`Uploading ${chalk.reset(
`[${bar}] (${uploadedHuman}/${totalSizeHuman})`
)}`,
0
);
}
})
);
@@ -154,10 +149,6 @@ export default async function processDeployment({
}
if (event.type === 'created') {
if (bar && !bar.complete) {
bar.tick(bar.total + 1);
}
await linkFolderToProject(
output,
cwd || paths[0],

View File

@@ -73,7 +73,7 @@ export class Output {
link?: string,
action = 'Learn More'
) => {
this.print(`${chalk.red(`Error!`)} ${str}\n`);
this.print(`${chalk.red(`Error:`)} ${str}\n`);
const details = slug ? `https://err.sh/vercel/${slug}` : link;
if (details) {
this.print(`${chalk.bold(action)}: ${renderLink(details)}\n`);

View File

@@ -23,5 +23,5 @@ export default function error(
metric.exception(messages.join('\n')).send();
}
return `${chalk.red('Error!')} ${messages.join('\n')}`;
return `${chalk.red('Error:')} ${messages.join('\n')}`;
}

View File

@@ -0,0 +1,23 @@
export interface ProgressOptions {
width?: number;
complete?: string;
incomplete?: string;
}
/**
* Returns a raw progress bar string.
*/
export function progress(
current: number,
total: number,
opts: ProgressOptions = {}
): string | null {
const { width = 20, complete = '=', incomplete = '-' } = opts;
if (total <= 0 || current < 0 || current > total) {
// Let the caller decide how to handle out-of-range values
return null;
}
const unit = total / width;
const pos = Math.floor(current / unit);
return `${complete.repeat(pos)}${incomplete.repeat(width - pos)}`;
}

View File

@@ -22,28 +22,28 @@ export async function validateRootDirectory(
const suffix = errorSuffix ? ` ${errorSuffix}` : '';
if (!pathStat) {
output.print(
`${chalk.red('Error!')} The provided path ${chalk.cyan(
output.error(
`The provided path ${chalk.cyan(
`${toHumanPath(path)}`
)} does not exist.${suffix}\n`
)} does not exist.${suffix}`
);
return false;
}
if (!pathStat.isDirectory()) {
output.print(
`${chalk.red('Error!')} The provided path ${chalk.cyan(
output.error(
`The provided path ${chalk.cyan(
`${toHumanPath(path)}`
)} is a file, but expected a directory.${suffix}\n`
)} is a file, but expected a directory.${suffix}`
);
return false;
}
if (!path.startsWith(cwd)) {
output.print(
`${chalk.red('Error!')} The provided path ${chalk.cyan(
output.error(
`The provided path ${chalk.cyan(
`${toHumanPath(path)}`
)} is outside of the project.${suffix}\n`
)} is outside of the project.${suffix}`
);
return false;
}
@@ -59,7 +59,7 @@ export default async function validatePaths(
// can't deploy more than 1 path
if (paths.length > 1) {
output.print(`${chalk.red('Error!')} Can't deploy more than one path.\n`);
output.error(`Can't deploy more than one path.`);
return { valid: false, exitCode: 1 };
}
@@ -69,11 +69,7 @@ export default async function validatePaths(
const pathStat = await stat(path).catch(() => null);
if (!pathStat) {
output.print(
`${chalk.red('Error!')} Could not find ${chalk.cyan(
`${toHumanPath(path)}`
)}\n`
);
output.error(`Could not find ${chalk.cyan(`${toHumanPath(path)}`)}`);
return { valid: false, exitCode: 1 };
}

View File

@@ -1,5 +1,5 @@
{
"name": "nextjs",
"name": "nextjs-node",
"license": "MIT",
"scripts": {
"dev": "next",

View File

@@ -1,5 +1,5 @@
{
"name": "nextjs",
"name": "nextjs-src-dir",
"license": "MIT",
"scripts": {
"dev": "next",

View File

@@ -445,11 +445,7 @@ test(
test(
'[vercel dev] Middleware that has no response',
testFixtureStdio('middleware-no-response', async (testPath: any) => {
await testPath(
500,
'/api/hello',
'A server error has occurred\n\nEDGE_FUNCTION_INVOCATION_FAILED'
);
await testPath(200, '/api/hello', 'hello from a serverless function');
})
);

View File

@@ -13,6 +13,7 @@ const {
jest.setTimeout(6 * 60 * 1000);
const isCI = !!process.env.CI;
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const isCanary = () => getDistTag(cliVersion) === 'canary';
@@ -52,7 +53,7 @@ function fetchWithRetry(url, opts = {}) {
return res;
},
{
retries: opts.retries || 3,
retries: opts.retries ?? 3,
factor: 1,
}
);
@@ -150,9 +151,9 @@ async function testPath(
fetchOpts = {}
) {
const opts = {
retries: isCI ? 5 : 0,
...fetchOpts,
redirect: 'manual-dont-change',
retries: 5,
status,
};
const url = `${origin}${path}`;
@@ -330,7 +331,7 @@ function testFixtureStdio(
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(projectSettings),
retries: 3,
retries: isCI ? 3 : 0,
status: 200,
}
);

View File

@@ -1,5 +1,5 @@
{
"name": "only-module",
"name": "prefer-browser",
"private": true,
"main": "dist-main.js",
"module": "dist-module.js",

View File

@@ -360,7 +360,7 @@ test('default command should prompt login with empty auth.json', async t => {
} catch (err) {
t.true(
err.stderr.includes(
'Error! No existing credentials found. Please run `vercel login` or pass "--token"'
'Error: No existing credentials found. Please run `vercel login` or pass "--token"'
)
);
}
@@ -749,7 +749,7 @@ test('deploy fails using --local-config flag with non-existent path', async t =>
t.is(exitCode, 1, formatOutput({ stderr, stdout }));
t.regex(stderr, /Error! Couldn't find a project configuration file at/);
t.regex(stderr, /Error: Couldn't find a project configuration file at/);
t.regex(stderr, /does-not-exist\.json/);
});
@@ -1451,7 +1451,7 @@ test('login with unregistered user', async t => {
console.log(stdout);
console.log(exitCode);
const goal = `Error! Please sign up: https://vercel.com/signup`;
const goal = `Error: Please sign up: https://vercel.com/signup`;
const lines = stderr.trim().split('\n');
const last = lines[lines.length - 1];
@@ -1629,7 +1629,7 @@ test('try to purchase a domain', async t => {
t.is(exitCode, 1);
t.regex(
stderr,
/Error! Could not purchase domain\. Please add a payment method using/
/Error: Could not purchase domain\. Please add a payment method using/
);
});
@@ -1655,7 +1655,7 @@ test('try to transfer-in a domain with "--code" option', async t => {
t.true(
stderr.includes(
`Error! The domain "${session}-test.com" is not transferable.`
`Error: The domain "${session}-test.com" is not transferable.`
)
);
t.is(exitCode, 1);
@@ -1680,7 +1680,7 @@ test('try to move an invalid domain', async t => {
console.log(stdout);
console.log(exitCode);
t.true(stderr.includes(`Error! Domain not found under `));
t.true(stderr.includes(`Error: Domain not found under `));
t.is(exitCode, 1);
});
@@ -1964,7 +1964,7 @@ test('try to create a builds deployments with wrong now.json', async t => {
t.is(exitCode, 1);
t.true(
stderr.includes(
'Error! Invalid now.json - should NOT have additional property `builder`. Did you mean `builds`?'
'Error: Invalid now.json - should NOT have additional property `builder`. Did you mean `builds`?'
)
);
t.true(stderr.includes('https://vercel.com/docs/configuration'));
@@ -1988,7 +1988,7 @@ test('try to create a builds deployments with wrong vercel.json', async t => {
t.is(exitCode, 1);
t.true(
stderr.includes(
'Error! Invalid vercel.json - should NOT have additional property `fake`. Please remove it.'
'Error: Invalid vercel.json - should NOT have additional property `fake`. Please remove it.'
)
);
t.true(stderr.includes('https://vercel.com/docs/configuration'));
@@ -2009,7 +2009,7 @@ test('try to create a builds deployments with wrong `build.env` property', async
t.is(exitCode, 1, formatOutput({ stdout, stderr }));
t.true(
stderr.includes(
'Error! Invalid vercel.json - should NOT have additional property `build.env`. Did you mean `{ "build": { "env": {"name": "value"} } }`?'
'Error: Invalid vercel.json - should NOT have additional property `build.env`. Did you mean `{ "build": { "env": {"name": "value"} } }`?'
),
formatOutput({ stdout, stderr })
);
@@ -2171,7 +2171,7 @@ test('use build-env', async t => {
});
test('try to deploy non-existing path', async t => {
const goal = `Error! The specified file or directory "${session}" does not exist.`;
const goal = `Error: The specified file or directory "${session}" does not exist.`;
const { stderr, stdout, exitCode } = await execa(
binaryPath,
@@ -2191,7 +2191,7 @@ test('try to deploy non-existing path', async t => {
test('try to deploy with non-existing team', async t => {
const target = fixture('static-deployment');
const goal = `Error! The specified scope does not exist`;
const goal = `Error: The specified scope does not exist`;
const { stderr, stdout, exitCode } = await execa(
binaryPath,
@@ -2274,7 +2274,7 @@ test('try to initialize example to existing directory', async t => {
tmpDir = tmp.dirSync({ unsafeCleanup: true });
const cwd = tmpDir.name;
const goal =
'Error! Destination path "angular" already exists and is not an empty directory. You may use `--force` or `-f` to override it.';
'Error: Destination path "angular" already exists and is not an empty directory. You may use `--force` or `-f` to override it.';
await ensureDir(path.join(cwd, 'angular'));
createFile(path.join(cwd, 'angular', '.gitignore'));
@@ -2291,7 +2291,7 @@ test('try to initialize misspelled example (noce) in non-tty', async t => {
tmpDir = tmp.dirSync({ unsafeCleanup: true });
const cwd = tmpDir.name;
const goal =
'Error! No example found for noce, run `vercel init` to see the list of available examples.';
'Error: No example found for noce, run `vercel init` to see the list of available examples.';
const { stdout, stderr, exitCode } = await execute(['init', 'noce'], { cwd });
@@ -2307,7 +2307,7 @@ test('try to initialize example "example-404"', async t => {
tmpDir = tmp.dirSync({ unsafeCleanup: true });
const cwd = tmpDir.name;
const goal =
'Error! No example found for example-404, run `vercel init` to see the list of available examples.';
'Error: No example found for example-404, run `vercel init` to see the list of available examples.';
const { stdout, stderr, exitCode } = await execute(['init', 'example-404'], {
cwd,
@@ -2483,7 +2483,7 @@ test('`vercel rm` should fail with unexpected option', async t => {
t.is(output.exitCode, 1, formatOutput(output));
t.regex(
output.stderr,
/Error! unknown or unexpected option: --fake/gm,
/Error: unknown or unexpected option: --fake/gm,
formatOutput(output)
);
});
@@ -2777,7 +2777,7 @@ test('invalid `--token`', async t => {
t.is(output.exitCode, 1, formatOutput(output));
t.true(
output.stderr.includes(
'Error! You defined "--token", but its contents are invalid. Must not contain: "\\n", ",", "."'
'Error: You defined "--token", but its contents are invalid. Must not contain: "\\n", ",", "."'
)
);
});
@@ -3500,7 +3500,7 @@ test('reject deploying with invalid token', async t => {
t.is(exitCode, 1, formatOutput({ stderr, stdout }));
t.regex(
stderr,
/Error! Could not retrieve Project Settings\. To link your Project, remove the `\.vercel` directory and deploy again\./g
/Error: Could not retrieve Project Settings\. To link your Project, remove the `\.vercel` directory and deploy again\./g
);
});

View File

@@ -711,7 +711,7 @@ describe('build', () => {
// Error gets printed to the terminal
await expect(client.stderr).toOutput(
'Error! Function must contain at least one property.'
'Error: Function must contain at least one property.'
);
// `builds.json` contains top-level "error" property

View File

@@ -12,7 +12,7 @@ describe('deploy', () => {
client.setArgv('deploy', __filename);
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
`Error! Support for single file deployments has been removed.\nLearn More: https://vercel.link/no-single-file-deployments\n`
`Error: Support for single file deployments has been removed.\nLearn More: https://vercel.link/no-single-file-deployments\n`
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -21,7 +21,7 @@ describe('deploy', () => {
client.setArgv('deploy', __filename, join(__dirname, 'inspect.test.ts'));
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
`Error! Can't deploy more than one path.\n`
`Error: Can't deploy more than one path.\n`
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -30,7 +30,7 @@ describe('deploy', () => {
client.setArgv('deploy', 'does-not-exists');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
`Error! The specified file or directory "does-not-exists" does not exist.\n`
`Error: The specified file or directory "does-not-exists" does not exist.\n`
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -41,7 +41,7 @@ describe('deploy', () => {
client.setArgv('deploy', cwd, '--prebuilt');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'> Prebuilt deployment cannot be created because `vercel build` failed with error:\n\nError! The build failed (top-level)\n'
'> Prebuilt deployment cannot be created because `vercel build` failed with error:\n\nError: The build failed (top-level)\n'
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -52,7 +52,7 @@ describe('deploy', () => {
client.setArgv('deploy', cwd, '--prebuilt');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'> Prebuilt deployment cannot be created because `vercel build` failed with error:\n\nError! The build failed within a Builder\n'
'> Prebuilt deployment cannot be created because `vercel build` failed with error:\n\nError: The build failed within a Builder\n'
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -61,7 +61,7 @@ describe('deploy', () => {
client.setArgv('deploy', __dirname, '--prebuilt');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'Error! The "--prebuilt" option was used, but no prebuilt output found in ".vercel/output". Run `vercel build` to generate a local build.\n'
'Error: The "--prebuilt" option was used, but no prebuilt output found in ".vercel/output". Run `vercel build` to generate a local build.\n'
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -80,7 +80,7 @@ describe('deploy', () => {
client.setArgv('deploy', cwd, '--prebuilt', '--prod');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'Error! The "--prebuilt" option was used with the target environment "production",' +
'Error: The "--prebuilt" option was used with the target environment "production",' +
' but the prebuilt output found in ".vercel/output" was built with target environment "preview".' +
' Please run `vercel --prebuilt`.\n' +
'Learn More: https://vercel.link/prebuilt-environment-mismatch\n'
@@ -102,7 +102,7 @@ describe('deploy', () => {
client.setArgv('deploy', cwd, '--prebuilt');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'Error! The "--prebuilt" option was used with the target environment "preview",' +
'Error: The "--prebuilt" option was used with the target environment "preview",' +
' but the prebuilt output found in ".vercel/output" was built with target environment "production".' +
' Please run `vercel --prebuilt --prod`.\n' +
'Learn More: https://vercel.link/prebuilt-environment-mismatch\n'
@@ -118,7 +118,7 @@ describe('deploy', () => {
};
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'Error! The value of the `version` property within vercel.json can only be `2`.\n'
'Error: The value of the `version` property within vercel.json can only be `2`.\n'
);
await expect(exitCodePromise).resolves.toEqual(1);
});
@@ -132,7 +132,7 @@ describe('deploy', () => {
};
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'Error! The `version` property inside your vercel.json file must be a number.\n'
'Error: The `version` property inside your vercel.json file must be a number.\n'
);
await expect(exitCodePromise).resolves.toEqual(1);
});

View File

@@ -80,7 +80,7 @@ describe('git', () => {
const exitCode = await git(client);
expect(exitCode).toEqual(1);
await expect(client.stderr).toOutput(
`Error! No local Git repository found. Run \`git clone <url>\` to clone a remote Git repository first.\n`
`Error: No local Git repository found. Run \`git clone <url>\` to clone a remote Git repository first.\n`
);
} finally {
process.chdir(originalCwd);
@@ -102,7 +102,7 @@ describe('git', () => {
const exitCode = await git(client);
expect(exitCode).toEqual(1);
await expect(client.stderr).toOutput(
`Error! No remote URLs found in your Git config. Make sure you've configured a remote repo in your local Git config. Run \`git remote --help\` for more details.`
`Error: No remote URLs found in your Git config. Make sure you've configured a remote repo in your local Git config. Run \`git remote --help\` for more details.`
);
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
@@ -129,7 +129,7 @@ describe('git', () => {
`Connecting Git remote: bababooey`
);
await expect(client.stderr).toOutput(
`Error! Failed to parse Git repo data from the following remote URL: bababooey\n`
`Error: Failed to parse Git repo data from the following remote URL: bababooey\n`
);
} finally {
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));

View File

@@ -33,7 +33,7 @@ describe('inspect', () => {
const exitCode = await inspect(client);
expect(exitCode).toEqual(1);
await expect(client.stderr).toOutput(
`Error! Failed to find deployment "bad.com" in ${user.username}\n`
`Error: Failed to find deployment "bad.com" in ${user.username}\n`
);
});
});

View File

@@ -7,7 +7,7 @@ describe('login', () => {
client.setArgv('login', '--token', 'foo');
const exitCodePromise = login(client);
await expect(client.stderr).toOutput(
'Error! `--token` may not be used with the "login" command\n'
'Error: `--token` may not be used with the "login" command\n'
);
await expect(exitCodePromise).resolves.toEqual(2);
});

View File

@@ -0,0 +1,31 @@
import { progress } from '../../../../src/util/output/progress';
describe('progress()', () => {
test.each([
{ current: 0, total: 5, opts: { width: 5 }, expected: '-----' },
{ current: 1, total: 5, opts: { width: 5 }, expected: '=----' },
{ current: 2, total: 5, opts: { width: 5 }, expected: '==---' },
{ current: 3, total: 5, opts: { width: 5 }, expected: '===--' },
{ current: 4, total: 5, opts: { width: 5 }, expected: '====-' },
{ current: 5, total: 5, opts: { width: 5 }, expected: '=====' },
{ current: 0, total: 12, expected: '--------------------' },
{ current: 1, total: 12, expected: '=-------------------' },
{ current: 2, total: 12, expected: '===-----------------' },
{ current: 600, total: 1200, expected: '==========----------' },
{
current: 9,
total: 10,
opts: { complete: '.', incomplete: ' ', width: 10 },
expected: '......... ',
},
{ current: 10, total: 10, expected: '====================' },
{ current: 11, total: 10, expected: null },
{ current: -1, total: 10, expected: null },
{ current: 1, total: 0, expected: null },
])(
'$current / $total -> "$expected"',
({ current, total, opts, expected }) => {
expect(progress(current, total, opts)).toEqual(expected);
}
);
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "12.2.2",
"version": "12.2.4",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -31,7 +31,7 @@
"@types/node": "12.0.4",
"@types/node-fetch": "2.5.4",
"@types/recursive-readdir": "2.2.0",
"@types/tar-fs": "^2.0.1",
"@types/tar-fs": "1.16.1",
"typescript": "4.3.4"
},
"jest": {
@@ -43,7 +43,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "5.4.0",
"@vercel/build-utils": "5.4.2",
"@vercel/routing-utils": "2.0.2",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
@@ -54,6 +54,7 @@
"ms": "2.1.2",
"node-fetch": "2.6.7",
"querystring": "^0.2.0",
"sleep-promise": "8.0.1"
"sleep-promise": "8.0.1",
"tar-fs": "1.16.3"
}
}

View File

@@ -1,3 +1,4 @@
# `@vercel/edge`
A set of utilities to help you deploy your app on the Edge using Vercel.
A set of utilities to help you deploy any framework on the Edge using Vercel.
Please [follow the documentation](./docs) for examples and usage.

View File

@@ -0,0 +1,262 @@
# @vercel/edge
## Table of contents
### Interfaces
- [ExtraResponseInit](interfaces/ExtraResponseInit.md)
- [Geo](interfaces/Geo.md)
### Variables
- [CITY_HEADER_NAME](README.md#city_header_name)
- [COUNTRY_HEADER_NAME](README.md#country_header_name)
- [IP_HEADER_NAME](README.md#ip_header_name)
- [LATITUDE_HEADER_NAME](README.md#latitude_header_name)
- [LONGITUDE_HEADER_NAME](README.md#longitude_header_name)
- [REGION_HEADER_NAME](README.md#region_header_name)
### Functions
- [geolocation](README.md#geolocation)
- [ipAddress](README.md#ipaddress)
- [next](README.md#next)
- [rewrite](README.md#rewrite)
## Variables
### CITY_HEADER_NAME
`Const` **CITY_HEADER_NAME**: `"x-vercel-ip-city"`
City of the original client IP as calculated by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:4](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L4)
---
### COUNTRY_HEADER_NAME
`Const` **COUNTRY_HEADER_NAME**: `"x-vercel-ip-country"`
Country of the original client IP as calculated by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:8](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L8)
---
### IP_HEADER_NAME
`Const` **IP_HEADER_NAME**: `"x-real-ip"`
Client IP as calcualted by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:12](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L12)
---
### LATITUDE_HEADER_NAME
`Const` **LATITUDE_HEADER_NAME**: `"x-vercel-ip-latitude"`
Latitude of the original client IP as calculated by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:16](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L16)
---
### LONGITUDE_HEADER_NAME
`Const` **LONGITUDE_HEADER_NAME**: `"x-vercel-ip-longitude"`
Longitude of the original client IP as calculated by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:20](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L20)
---
### REGION_HEADER_NAME
`Const` **REGION_HEADER_NAME**: `"x-vercel-ip-country-region"`
Region of the original client IP as calculated by Vercel Proxy.
#### Defined in
[src/edge-headers.ts:24](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L24)
## Functions
### geolocation
**geolocation**(`request`): [`Geo`](interfaces/Geo.md)
Returns the location information for the incoming request.
**`See`**
- [CITY_HEADER_NAME](README.md#city_header_name)
- [COUNTRY_HEADER_NAME](README.md#country_header_name)
- [REGION_HEADER_NAME](README.md#region_header_name)
- [LATITUDE_HEADER_NAME](README.md#latitude_header_name)
- [LONGITUDE_HEADER_NAME](README.md#longitude_header_name)
#### Parameters
| Name | Type | Description |
| :-------- | :-------- | :-------------------------------------------------------------- |
| `request` | `Request` | The incoming request object which provides the geolocation data |
#### Returns
[`Geo`](interfaces/Geo.md)
#### Defined in
[src/edge-headers.ts:80](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L80)
---
### ipAddress
**ipAddress**(`request`): `string` \| `undefined`
Returns the IP address of the request from the headers.
**`See`**
[IP_HEADER_NAME](README.md#ip_header_name)
#### Parameters
| Name | Type | Description |
| :-------- | :-------- | :------------------------------------------------ |
| `request` | `Request` | The incoming request object which provides the IP |
#### Returns
`string` \| `undefined`
#### Defined in
[src/edge-headers.ts:66](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L66)
---
### next
**next**(`init?`): `Response`
Returns a Response that instructs the system to continue processing the request.
**`Example`**
<caption>No-op middleware</caption>
```ts
import { next } from '@vercel/edge';
export default function middleware(_req: Request) {
return next();
}
```
**`Example`**
<caption>Add response headers to all requests</caption>
```ts
import { next } from '@vercel/edge';
export default function middleware(_req: Request) {
return next({
headers: { 'x-from-middleware': 'true' },
});
}
```
#### Parameters
| Name | Type | Description |
| :------ | :----------------------------------------------------- | :---------------------------------- |
| `init?` | [`ExtraResponseInit`](interfaces/ExtraResponseInit.md) | Additional options for the response |
#### Returns
`Response`
#### Defined in
[src/middleware-helpers.ts:94](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L94)
---
### rewrite
**rewrite**(`destination`, `init?`): `Response`
Returns a response that rewrites the request to a different URL.
**`Example`**
<caption>Rewrite all feature-flagged requests from `/:path*` to `/experimental/:path*`</caption>
```ts
import { rewrite, next } from '@vercel/edge';
export default async function middleware(req: Request) {
const flagged = await getFlag(req, 'isExperimental');
if (flagged) {
const url = new URL(req.url);
url.pathname = `/experimental{url.pathname}`;
return rewrite(url);
}
return next();
}
```
**`Example`**
<caption>JWT authentication for `/api/:path*` requests</caption>
```ts
import { rewrite, next } from '@vercel/edge';
export default function middleware(req: Request) {
const auth = checkJwt(req.headers.get('Authorization'));
if (!checkJwt) {
return rewrite(new URL('/api/error-unauthorized', req.url));
}
const url = new URL(req.url);
url.searchParams.set('_userId', auth.userId);
return rewrite(url);
}
export const config = { matcher: '/api/users/:path*' };
```
#### Parameters
| Name | Type | Description |
| :------------ | :------------------------------------------------------------------------ | :---------------------------------- |
| `destination` | `string` \| [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) | new URL to rewrite the request to |
| `init?` | [`ExtraResponseInit`](interfaces/ExtraResponseInit.md) | Additional options for the response |
#### Returns
`Response`
#### Defined in
[src/middleware-helpers.ts:53](https://github.com/vercel/vercel/blob/main/packages/edge/src/middleware-helpers.ts#L53)

View File

@@ -0,0 +1,56 @@
# Interface: ExtraResponseInit
## Hierarchy
- `Omit`<`ResponseInit`, `"headers"`\>
**`ExtraResponseInit`**
## Table of contents
### Properties
- [headers](ExtraResponseInit.md#headers)
- [status](ExtraResponseInit.md#status)
- [statusText](ExtraResponseInit.md#statustext)
## Properties
### headers
`Optional` **headers**: `HeadersInit`
These headers will be sent to the user response
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)
---
### status
`Optional` **status**: `number`
#### Inherited from
Omit.status
#### Defined in
node_modules/typescript/lib/lib.dom.d.ts:1578
---
### statusText
`Optional` **statusText**: `string`
#### Inherited from
Omit.statusText
#### Defined in
node_modules/typescript/lib/lib.dom.d.ts:1579

View File

@@ -0,0 +1,73 @@
# Interface: Geo
The location information of a given request.
## Table of contents
### Properties
- [city](Geo.md#city)
- [country](Geo.md#country)
- [latitude](Geo.md#latitude)
- [longitude](Geo.md#longitude)
- [region](Geo.md#region)
## Properties
### city
`Optional` **city**: `string`
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)
---
### country
`Optional` **country**: `string`
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)
---
### latitude
`Optional` **latitude**: `string`
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)
---
### longitude
`Optional` **longitude**: `string`
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)
---
### region
`Optional` **region**: `string`
The [Vercel Edge Network region](https://vercel.com/docs/concepts/edge-network/regions) that received the request.
#### Defined in
[src/edge-headers.ts:47](https://github.com/vercel/vercel/blob/main/packages/edge/src/edge-headers.ts#L47)

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/edge",
"version": "0.0.3",
"version": "0.0.4",
"license": "MIT",
"main": "dist/index.js",
"module": "dist/index.mjs",
@@ -8,13 +8,17 @@
"scripts": {
"build": "tsup src/index.ts --dts --format esm,cjs",
"test": "jest --env node --verbose --runInBand --bail",
"test-unit": "yarn test"
"test-unit": "yarn test",
"build:docs": "typedoc && prettier --write docs/**/*.md docs/*.md"
},
"devDependencies": {
"@edge-runtime/jest-environment": "1.1.0-beta.7",
"@types/jest": "27.4.1",
"ts-node": "8.9.1",
"tsup": "6.1.2",
"typedoc": "0.23.10",
"typedoc-plugin-markdown": "3.13.4",
"typedoc-plugin-mdn-links": "2.0.0",
"typescript": "4.7.4"
},
"jest": {

View File

@@ -1,25 +1,25 @@
/**
* City of the original client IP calculated by Vercel Proxy.
* City of the original client IP as calculated by Vercel Proxy.
*/
export const CITY_HEADER_NAME = 'x-vercel-ip-city';
/**
* Country of the original client IP calculated by Vercel Proxy.
* Country of the original client IP as calculated by Vercel Proxy.
*/
export const COUNTRY_HEADER_NAME = 'x-vercel-ip-country';
/**
* Ip from Vercel Proxy. Do not confuse it with the client Ip.
* Client IP as calcualted by Vercel Proxy.
*/
export const IP_HEADER_NAME = 'x-real-ip';
/**
* Latitude of the original client IP calculated by Vercel Proxy.
* Latitude of the original client IP as calculated by Vercel Proxy.
*/
export const LATITUDE_HEADER_NAME = 'x-vercel-ip-latitude';
/**
* Longitude of the original client IP calculated by Vercel Proxy.
* Longitude of the original client IP as calculated by Vercel Proxy.
*/
export const LONGITUDE_HEADER_NAME = 'x-vercel-ip-longitude';
/**
* Region of the original client IP calculated by Vercel Proxy.
* Region of the original client IP as calculated by Vercel Proxy.
*/
export const REGION_HEADER_NAME = 'x-vercel-ip-country-region';
@@ -34,18 +34,22 @@ interface Request {
}
/**
* The location information of a given request
* The location information of a given request.
*/
export interface Geo {
/** The city that the request originated from */
/** The city that the request originated from. */
city?: string;
/** The country that the request originated from */
/** The country that the request originated from. */
country?: string;
/** The Vercel Edge Network region that received the request */
/** The [Vercel Edge Network region](https://vercel.com/docs/concepts/edge-network/regions) that received the request. */
region?: string;
/** The latitude of the client */
/** The latitude of the client. */
latitude?: string;
/** The longitude of the client */
/** The longitude of the client. */
longitude?: string;
}
@@ -57,19 +61,21 @@ function getHeader(request: Request, key: string): string | undefined {
* Returns the IP address of the request from the headers.
*
* @see {@link IP_HEADER_NAME}
* @param request The incoming request object which provides the IP
*/
export function ipAddress(request: Request): string | undefined {
return getHeader(request, IP_HEADER_NAME);
}
/**
* Returns the location information from for the incoming request
* Returns the location information for the incoming request.
*
* @see {@link CITY_HEADER_NAME}
* @see {@link COUNTRY_HEADER_NAME}
* @see {@link REGION_HEADER_NAME}
* @see {@link LATITUDE_HEADER_NAME}
* @see {@link LONGITUDE_HEADER_NAME}
* @param request The incoming request object which provides the geolocation data
*/
export function geolocation(request: Request): Geo {
return {

View File

@@ -1,13 +1,54 @@
export type ExtraResponseInit = Omit<ResponseInit, '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
* along with the response headers from the origin.
*/
headers?: HeadersInit;
};
}
/**
* Rewrite the request into a different URL.
* Returns a response that rewrites the request to a different URL.
*
* @param destination new URL to rewrite the request to
* @param init Additional options for the response
*
*
* @example
* <caption>Rewrite all feature-flagged requests from `/:path*` to `/experimental/:path*`</caption>
*
* ```ts
* import { rewrite, next } from '@vercel/edge';
*
* export default async function middleware(req: Request) {
* const flagged = await getFlag(req, 'isExperimental');
* if (flagged) {
* const url = new URL(req.url);
* url.pathname = `/experimental{url.pathname}`;
* return rewrite(url);
* }
*
* return next();
* }
* ```
*
* @example
* <caption>JWT authentication for `/api/:path*` requests</caption>
*
* ```ts
* import { rewrite, next } from '@vercel/edge';
*
* export default function middleware(req: Request) {
* const auth = checkJwt(req.headers.get('Authorization'));
* if (!checkJwt) {
* return rewrite(new URL('/api/error-unauthorized', req.url));
* }
* const url = new URL(req.url);
* url.searchParams.set('_userId', auth.userId);
* return rewrite(url);
* }
*
* export const config = { matcher: '/api/users/:path*' };
* ```
*/
export function rewrite(
destination: string | URL,
@@ -22,7 +63,33 @@ export function rewrite(
}
/**
* This tells the Middleware to continue with the request.
* Returns a Response that instructs the system to continue processing the request.
*
* @param init Additional options for the response
*
* @example
* <caption>No-op middleware</caption>
*
* ```ts
* import { next } from '@vercel/edge';
*
* export default function middleware(_req: Request) {
* return next();
* }
* ```
*
* @example
* <caption>Add response headers to all requests</caption>
*
* ```ts
* import { next } from '@vercel/edge';
*
* export default function middleware(_req: Request) {
* return next({
* headers: { 'x-from-middleware': 'true' },
* })
* }
* ```
*/
export function next(init?: ExtraResponseInit): Response {
const headers = new Headers(init?.headers ?? {});

38
packages/edge/test/docs.test.ts vendored Normal file
View File

@@ -0,0 +1,38 @@
/// <reference types="@types/node" />
import { exec } from 'child_process';
import { promisify } from 'util';
import path from 'path';
const execAsync = promisify(exec);
const test = process.platform === 'win32' ? it.skip : it;
test('docs are up to date', async () => {
const cwd = path.resolve(__dirname, '../');
await execAsync(`yarn build:docs`, { cwd });
const result = await execAsync(`git status --short docs`, {
cwd,
encoding: 'utf-8',
});
const lines = result.stdout
.trim()
.split(/(?:\r?\n)+/)
.map(x => x.trim().split(/\s+/).slice(1).join(' '))
.filter(x => x.startsWith('docs/'))
.map(x => `* ${x}`)
.join('\n')
.trim();
if (lines !== '') {
const diff = await execAsync(`git diff docs`, { cwd, encoding: 'utf8' });
throw new Error(
'Docs are not up to date. Please re-run `yarn build:docs` to re-generate them.\nChanges:\n' +
lines +
'\n\n' +
diff.stdout
);
}
expect(result.stdout.trim()).toEqual('');
}, 120000);

View File

@@ -0,0 +1,10 @@
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": ["src/index.ts"],
"plugin": ["typedoc-plugin-markdown", "typedoc-plugin-mdn-links"],
"out": "docs",
"githubPages": false,
"gitRevision": "main",
"readme": "none",
"hideBreadcrumbs": true
}

View File

@@ -1 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M22.428.013c-.103.01-.431.042-.727.066C14.883.693 8.497 4.37 4.453 10.024A23.754 23.754 0 0 0 .216 20.51C.023 21.828 0 22.217 0 24.005c0 1.787.023 2.177.216 3.495 1.304 9.012 7.718 16.584 16.417 19.39 1.558.501 3.2.844 5.068 1.05.727.08 3.87.08 4.598 0 3.224-.356 5.954-1.154 8.648-2.529.412-.21.492-.267.436-.314-.038-.028-1.797-2.388-3.909-5.24l-3.838-5.184-4.809-7.117c-2.646-3.913-4.824-7.112-4.842-7.112-.019-.005-.038 3.157-.047 7.018-.014 6.76-.019 7.033-.103 7.192-.122.23-.216.324-.413.427-.15.075-.282.09-.99.09h-.812l-.216-.137a.878.878 0 0 1-.314-.342l-.099-.211.01-9.407.014-9.41.145-.184c.075-.098.235-.225.347-.286.193-.094.268-.103 1.08-.103.957 0 1.116.038 1.365.31.07.075 2.674 3.997 5.79 8.721s7.376 11.175 9.469 14.342l3.8 5.756.192-.127c1.704-1.107 3.505-2.683 4.932-4.325a23.888 23.888 0 0 0 5.65-12.268c.192-1.319.215-1.708.215-3.495 0-1.788-.023-2.177-.216-3.495-1.304-9.013-7.718-16.584-16.417-19.39C29.832.623 28.199.28 26.369.074c-.45-.047-3.551-.099-3.94-.061zm9.825 14.515a.947.947 0 0 1 .474.554c.038.122.047 2.73.038 8.608l-.014 8.436-1.488-2.28-1.492-2.28v-6.132c0-3.964.019-6.193.047-6.3a.957.957 0 0 1 .465-.592c.192-.098.262-.108 1-.108.694 0 .816.01.97.094z" fill="#fff"/></svg>
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="mask" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="48" height="48"><circle cx="24" cy="24" r="24" fill="#000"/></mask><g mask="url(#mask)"><circle cx="24" cy="24" r="23.2" fill="#000" stroke="#fff" stroke-width="1.6"/><path d="M39.8687 42.0055L18.4378 14.4H14.3999V33.592H17.6302V18.5023L37.333 43.9587C38.222 43.3637 39.069 42.7108 39.8687 42.0055Z" fill="url(#gradient0)"/><rect x="30.6667" y="14.4" width="3.2" height="19.2" fill="url(#gradient1)"/></g><defs><linearGradient id="gradient0" x1="29.0666" y1="31.0667" x2="38.5332" y2="42.8" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient><linearGradient id="gradient1" x1="32.2667" y1="14.4" x2="32.2132" y2="28.5001" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient></defs></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 997 B

View File

@@ -1 +1 @@
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M22.428.013c-.103.01-.431.042-.727.066C14.883.693 8.497 4.37 4.453 10.024A23.754 23.754 0 0 0 .216 20.51C.023 21.828 0 22.217 0 24.005c0 1.787.023 2.177.216 3.495 1.304 9.012 7.718 16.584 16.417 19.39 1.558.501 3.2.844 5.068 1.05.727.08 3.87.08 4.598 0 3.224-.356 5.954-1.154 8.648-2.529.412-.21.492-.267.436-.314-.038-.028-1.797-2.388-3.909-5.24l-3.838-5.184-4.809-7.117c-2.646-3.913-4.824-7.112-4.842-7.112-.019-.005-.038 3.157-.047 7.018-.014 6.76-.019 7.033-.103 7.192-.122.23-.216.324-.413.427-.15.075-.282.09-.99.09h-.812l-.216-.137a.878.878 0 0 1-.314-.342l-.099-.211.01-9.407.014-9.41.145-.184c.075-.098.235-.225.347-.286.193-.094.268-.103 1.08-.103.957 0 1.116.038 1.365.31.07.075 2.674 3.997 5.79 8.721s7.376 11.175 9.469 14.342l3.8 5.756.192-.127c1.704-1.107 3.505-2.683 4.932-4.325a23.888 23.888 0 0 0 5.65-12.268c.192-1.319.215-1.708.215-3.495 0-1.788-.023-2.177-.216-3.495-1.304-9.013-7.718-16.584-16.417-19.39C29.832.623 28.199.28 26.369.074c-.45-.047-3.551-.099-3.94-.061zm9.825 14.515a.947.947 0 0 1 .474.554c.038.122.047 2.73.038 8.608l-.014 8.436-1.488-2.28-1.492-2.28v-6.132c0-3.964.019-6.193.047-6.3a.957.957 0 0 1 .465-.592c.192-.098.262-.108 1-.108.694 0 .816.01.97.094z" fill="#000"/></svg>
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="mask" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="48" height="48"><circle cx="24" cy="24" r="24" fill="#000"/></mask><g mask="url(#mask)"><circle cx="24" cy="24" r="23.2" fill="#000" stroke="#000" stroke-width="1.6"/><path d="M39.8687 42.0055L18.4378 14.4H14.3999V33.592H17.6302V18.5023L37.333 43.9587C38.222 43.3637 39.069 42.7108 39.8687 42.0055Z" fill="url(#gradient0)"/><rect x="30.6667" y="14.4" width="3.2" height="19.2" fill="url(#gradient1)"/></g><defs><linearGradient id="gradient0" x1="29.0666" y1="31.0667" x2="38.5332" y2="42.8" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient><linearGradient id="gradient1" x1="32.2667" y1="14.4" x2="32.2132" y2="28.5001" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient></defs></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 997 B

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "1.1.3",
"version": "1.1.4",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "2.0.5",
"version": "3.0.0",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -19,7 +19,7 @@
"test-unit": "yarn test"
},
"dependencies": {
"@vercel/frameworks": "1.1.3",
"@vercel/frameworks": "1.1.4",
"@vercel/routing-utils": "2.0.2",
"glob": "8.0.3",
"js-yaml": "4.1.0",

View File

@@ -1,3 +1,5 @@
import { posix as posixPath } from 'path';
export interface Stat {
name: string;
path: string;
@@ -76,12 +78,43 @@ export abstract class DetectorFilesystem {
/**
* Returns a list of Stat objects from the current working directory.
* @param dirPath The path of the directory to read.
* @param options.potentialFiles optional. Array of potential file names (relative to the path). If provided, these will be used to mark the filesystem caches as existing or not existing.
*/
public readdir = async (name: string): Promise<Stat[]> => {
let p = this.readdirCache.get(name);
public readdir = async (
dirPath: string,
options?: { potentialFiles?: string[] }
): Promise<Stat[]> => {
let p = this.readdirCache.get(dirPath);
if (!p) {
p = this._readdir(name);
this.readdirCache.set(name, p);
p = this._readdir(dirPath);
this.readdirCache.set(dirPath, p);
const directoryContent = await p;
const directoryFiles = new Set<string>();
for (const file of directoryContent) {
if (file.type === 'file') {
// we know this file exists, mark it as so on the filesystem
this.fileCache.set(file.path, Promise.resolve(true));
this.pathCache.set(file.path, Promise.resolve(true));
directoryFiles.add(file.name);
}
}
if (options?.potentialFiles) {
// calculate the set of paths that truly do not exist
const filesThatDoNotExist = options.potentialFiles.filter(
path => !directoryFiles.has(path)
);
for (const filePath of filesThatDoNotExist) {
const fullFilePath =
dirPath === '/' ? filePath : posixPath.join(dirPath, filePath);
// we know this file does not exist, mark it as so on the filesystem
this.fileCache.set(fullFilePath, Promise.resolve(false));
this.pathCache.set(fullFilePath, Promise.resolve(false));
}
}
}
return p;
};

View File

@@ -59,6 +59,19 @@ export const workspaceManagers: Array<
],
},
},
{
name: 'default',
slug: 'yarn',
detectors: {
every: [
{
path: 'package.json',
matchContent:
'"workspaces":\\s*(?:\\[[^\\]]*]|{[^}]*"packages":[^}]*})',
},
],
},
},
];
export default workspaceManagers;

View File

@@ -0,0 +1,15 @@
{
"name": "a",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"debug": "^4.3.2"
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "b",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cowsay": "^1.5.0"
}
}

View File

@@ -0,0 +1,8 @@
{
"name": "21-npm-workspaces",
"version": "1.0.0",
"workspaces": [
"a",
"b"
]
}

View File

@@ -11,6 +11,7 @@ describe('workspace-managers', () => {
['25-multiple-lock-files-yarn', 'yarn'],
['26-multiple-lock-files-pnpm', 'pnpm'],
['22-pnpm', null],
['38-workspaces-no-lock-file', 'yarn'],
])('with detectFramework', (fixturePath, frameworkSlug) => {
const testName = frameworkSlug
? `should detect a ${frameworkSlug} workspace for ${fixturePath}`

View File

@@ -135,11 +135,18 @@ describe('DetectorFilesystem', () => {
};
const fs = new VirtualFilesystem(files);
const hasPathSpy = jest.spyOn(fs, '_hasPath');
expect(await fs.readdir('/')).toEqual([
expect(await fs.readdir('/', { potentialFiles: ['config.rb'] })).toEqual([
{ name: 'package.json', path: 'package.json', type: 'file' },
{ name: 'packages', path: 'packages', type: 'dir' },
]);
expect(await fs.hasPath('package.json')).toBe(true);
expect(hasPathSpy).not.toHaveBeenCalled();
expect(await fs.hasPath('config.rb')).toBe(false);
expect(hasPathSpy).not.toHaveBeenCalled();
expect(await fs.hasPath('tsconfig.json')).toBe(false);
expect(hasPathSpy).toHaveBeenCalled();
expect(await fs.readdir('packages')).toEqual([
{ name: 'app1', path: 'packages/app1', type: 'dir' },
@@ -151,13 +158,19 @@ describe('DetectorFilesystem', () => {
{ name: 'app2', path: 'packages/app2', type: 'dir' },
]);
expect(await fs.readdir('packages/app1')).toEqual([
expect(
await fs.readdir('packages/app1', { potentialFiles: ['package.json'] })
).toEqual([
{
name: 'package.json',
path: 'packages/app1/package.json',
type: 'file',
},
]);
hasPathSpy.mock.calls.length = 0;
expect(await fs.hasPath('packages/app1/package.json')).toBe(true);
expect(hasPathSpy).not.toHaveBeenCalled();
});
it('should be able to change directories', async () => {

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/hydrogen",
"version": "0.0.16",
"version": "0.0.18",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -21,7 +21,7 @@
"devDependencies": {
"@types/jest": "27.5.1",
"@types/node": "*",
"@vercel/build-utils": "5.4.0",
"@vercel/build-utils": "5.4.2",
"typescript": "4.6.4"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/next",
"version": "3.1.22",
"version": "3.1.25",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
@@ -44,8 +44,8 @@
"@types/semver": "6.0.0",
"@types/text-table": "0.2.1",
"@types/webpack-sources": "3.2.0",
"@vercel/build-utils": "5.4.0",
"@vercel/nft": "0.22.0",
"@vercel/build-utils": "5.4.2",
"@vercel/nft": "0.22.1",
"@vercel/routing-utils": "2.0.2",
"async-sema": "3.0.1",
"buffer-crc32": "0.2.13",

View File

@@ -2636,7 +2636,10 @@ async function getServerlessPages(params: {
for (const [entry, normalizedEntry] of Object.entries(
params.appPathRoutesManifest
)) {
const normalizedPath = `${path.join('.', normalizedEntry)}.js`;
const normalizedPath = `${path.join(
'.',
normalizedEntry === '/' ? '/index' : normalizedEntry
)}.js`;
const globPath = `${path.join('.', entry)}.js`;
if (appPaths[globPath]) {

View File

@@ -2352,7 +2352,22 @@ export async function getMiddlewareBundle({
for (const worker of workerConfigs.values()) {
const edgeFile = worker.edgeFunction.name;
const shortPath = edgeFile.replace(/^pages\//, '');
let shortPath = edgeFile;
// Replacing the folder prefix for the page
//
// For `pages/`, use file base name directly:
// pages/index -> index
// For `app/`, use folder name, handle the root page as index:
// app/route/page -> route
// app/page -> index
// app/index/page -> index/index
if (shortPath.startsWith('pages/')) {
shortPath = shortPath.replace(/^pages\//, '');
} else if (shortPath.startsWith('app/') && shortPath.endsWith('/page')) {
shortPath =
shortPath.replace(/^app\//, '').replace(/(^|\/)page$/, '') || 'index';
}
worker.edgeFunction.name = shortPath;
source.edgeFunctions[shortPath] = worker.edgeFunction;

View File

@@ -0,0 +1,7 @@
export default function Page() {
return <p>edge</p>;
}
export const config = {
runtime: 'experimental-edge',
};

View File

@@ -0,0 +1,7 @@
export default function page() {
return 'index/page';
}
export const config = {
runtime: 'experimental-edge',
};

View File

@@ -0,0 +1,7 @@
export default function page() {
return 'page';
}
export const config = {
runtime: 'experimental-edge',
};

View File

@@ -0,0 +1,9 @@
module.exports = {
experimental: {
appDir: true,
runtime: 'experimental-edge',
serverComponents: true,
legacyBrowsers: false,
browsersListForSwc: true,
},
};

View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
}
}

View File

@@ -0,0 +1,15 @@
{
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
}
],
"probes": [
{
"path": "/edge",
"status": 200,
"mustContain": "edge"
}
]
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>index app page</p>;
}

View File

@@ -1,6 +1,6 @@
{
"dependencies": {
"next": "https://files-26yo0dy1b-ijjk-testing.vercel.app",
"next": "canary",
"react": "experimental",
"react-dom": "experimental"
}

View File

@@ -1,9 +0,0 @@
import Link from 'next/link';
export default function Page(props) {
return (
<>
<p>hello from pages/index</p>
<Link href="/dashboard">Dashboard</Link>
</>
);
}

View File

@@ -24,7 +24,7 @@
{
"path": "/",
"status": 200,
"mustContain": "hello from pages/index"
"mustContain": "index app page"
},
{
"path": "/blog/123",

View File

@@ -17,7 +17,6 @@ it('should build with app-dir correctly', async () => {
for (const key of Object.keys(buildResult.output)) {
if (buildResult.output[key].type === 'Lambda') {
lambdas.add(buildResult.output[key]);
console.log('found lambda', key);
}
}
expect(lambdas.size).toBe(1);
@@ -27,6 +26,26 @@ it('should build with app-dir correctly', async () => {
expect(buildResult.output['dashboard/deployments/[id]']).toBeDefined();
});
it('should build with app-dir in edg runtime correctly', async () => {
const { buildResult } = await runBuildLambda(
path.join(__dirname, '../fixtures/00-app-dir-edge')
);
console.log('buildResult', buildResult);
const edgeFunctions = new Set();
for (const key of Object.keys(buildResult.output)) {
if (buildResult.output[key].type === 'EdgeFunction') {
edgeFunctions.add(buildResult.output[key]);
}
}
expect(edgeFunctions.size).toBe(3);
expect(buildResult.output['edge']).toBeDefined();
expect(buildResult.output['index']).toBeDefined();
expect(buildResult.output['index/index']).toBeDefined();
});
it('should show error from basePath with legacy monorepo build', async () => {
let error;

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node",
"version": "2.5.11",
"version": "2.5.14",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -29,12 +29,12 @@
}
},
"dependencies": {
"@edge-runtime/vm": "1.1.0-beta.23",
"@edge-runtime/vm": "1.1.0-beta.32",
"@types/node": "*",
"@vercel/build-utils": "5.4.0",
"@vercel/build-utils": "5.4.2",
"@vercel/node-bridge": "3.0.0",
"@vercel/static-config": "2.0.3",
"edge-runtime": "1.1.0-beta.23",
"edge-runtime": "1.1.0-beta.32",
"esbuild": "0.14.47",
"exit-hook": "2.2.1",
"node-fetch": "2.6.7",
@@ -53,7 +53,7 @@
"@types/node-fetch": "^2.6.1",
"@types/test-listen": "1.1.0",
"@vercel/ncc": "0.24.0",
"@vercel/nft": "0.22.0",
"@vercel/nft": "0.22.1",
"content-type": "1.0.4",
"cookie": "0.4.0",
"etag": "1.8.1",

View File

@@ -151,7 +151,8 @@ async function serializeRequest(message: IncomingMessage) {
async function compileUserCode(
entrypointPath: string,
entrypointLabel: string
entrypointLabel: string,
isMiddleware: boolean
): Promise<undefined | { userCode: string; wasmAssets: WasmAssets }> {
const { wasmAssets, plugin: edgeWasmPlugin } = createEdgeWasmPlugin();
try {
@@ -176,6 +177,8 @@ async function compileUserCode(
const userCode = `
${compiledFile.text};
const isMiddleware = ${isMiddleware};
addEventListener('fetch', async (event) => {
try {
let serializedRequest = await event.request.text();
@@ -205,14 +208,26 @@ async function compileUserCode(
let response = await edgeHandler(event.request, event);
if (!response) {
if (isMiddleware) {
// allow empty responses to pass through
response = new Response(null, {
headers: {
'x-middleware-next': '1',
},
});
} else {
throw new Error('Edge Function "${entrypointLabel}" did not return a response.');
}
}
return event.respondWith(response);
} catch (error) {
// we can't easily show a meaningful stack trace
// so, stick to just the error message for now
event.respondWith(new Response(error.message, {
const msg = error.cause
? (error.message + ': ' + (error.cause.message || error.cause))
: error.message;
event.respondWith(new Response(msg, {
status: 500,
headers: {
'x-vercel-failed': 'edge-wrapper'
@@ -277,9 +292,14 @@ async function createEdgeRuntime(params?: {
async function createEdgeEventHandler(
entrypointPath: string,
entrypointLabel: string
entrypointLabel: string,
isMiddleware: boolean
): Promise<(request: IncomingMessage) => Promise<VercelProxyResponse>> {
const userCode = await compileUserCode(entrypointPath, entrypointLabel);
const userCode = await compileUserCode(
entrypointPath,
entrypointLabel,
isMiddleware
);
const server = await createEdgeRuntime(userCode);
return async function (request: IncomingMessage) {
@@ -349,7 +369,11 @@ async function createEventHandler(
// an Edge Function, otherwise needs to be opted-in via
// `export const config = { runtime: 'experimental-edge' }`
if (config.middleware === true || runtime === 'experimental-edge') {
return createEdgeEventHandler(entrypointPath, entrypoint);
return createEdgeEventHandler(
entrypointPath,
entrypoint,
config.middleware || false
);
}
return createServerlessEventHandler(entrypointPath, options);

View File

@@ -1,12 +0,0 @@
import { readFileSync } from 'fs';
import { join } from 'path';
export default function handler(_req, res) {
// This build.js asset should be included but not the dep.js asset
// because this is readFile(), not require(). It also shouldn't transpile
// with babel because it should be considered an asset.
const file = join(process.cwd(), 'assets', 'build.js');
const content = readFileSync(file, 'utf8');
res.setHeader('Content-Type', 'application/javascript');
return res.end(content);
}

View File

@@ -1 +0,0 @@
import dep from './dep.js';

View File

@@ -1,3 +0,0 @@
// This file should not be included because the
// api/index.js is performing a readFile(), not require().
export const dep = 'dep1';

View File

@@ -1,8 +0,0 @@
{
"probes": [
{
"path": "/api",
"mustContain": "import dep from './dep.js';"
}
]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/python",
"version": "3.1.12",
"version": "3.1.14",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -22,7 +22,7 @@
"devDependencies": {
"@types/execa": "^0.9.0",
"@types/jest": "27.4.1",
"@vercel/build-utils": "5.4.0",
"@vercel/build-utils": "5.4.2",
"@vercel/ncc": "0.24.0",
"execa": "^1.0.0",
"typescript": "4.3.4"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/redwood",
"version": "1.0.20",
"version": "1.0.23",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://vercel.com/docs",
@@ -19,7 +19,7 @@
"test-unit": "yarn test test/prepare-cache.test.js"
},
"dependencies": {
"@vercel/nft": "0.22.0",
"@vercel/nft": "0.22.1",
"@vercel/routing-utils": "2.0.2",
"semver": "6.1.1"
},
@@ -27,6 +27,6 @@
"@types/aws-lambda": "8.10.19",
"@types/node": "*",
"@types/semver": "6.0.0",
"@vercel/build-utils": "5.4.0"
"@vercel/build-utils": "5.4.2"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/remix",
"version": "1.0.21",
"version": "1.0.24",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -20,12 +20,12 @@
"default-server.js"
],
"dependencies": {
"@vercel/nft": "0.22.0"
"@vercel/nft": "0.22.1"
},
"devDependencies": {
"@types/jest": "27.5.1",
"@types/node": "*",
"@vercel/build-utils": "5.4.0",
"@vercel/build-utils": "5.4.2",
"typescript": "4.6.4"
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "@vercel/ruby",
"author": "Nathan Cahill <nathan@nathancahill.com>",
"version": "1.3.29",
"version": "1.3.31",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
@@ -22,7 +22,7 @@
"devDependencies": {
"@types/fs-extra": "8.0.0",
"@types/semver": "6.0.0",
"@vercel/build-utils": "5.4.0",
"@vercel/build-utils": "5.4.2",
"@vercel/ncc": "0.24.0",
"execa": "2.0.4",
"fs-extra": "^7.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/static-build",
"version": "1.0.20",
"version": "1.0.22",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/build-step",
@@ -36,8 +36,8 @@
"@types/ms": "0.7.31",
"@types/node-fetch": "2.5.4",
"@types/promise-timeout": "1.3.0",
"@vercel/build-utils": "5.4.0",
"@vercel/frameworks": "1.1.3",
"@vercel/build-utils": "5.4.2",
"@vercel/frameworks": "1.1.4",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.0.2",
"fs-extra": "10.0.0",

133
yarn.lock
View File

@@ -719,10 +719,10 @@
dependencies:
"@jridgewell/trace-mapping" "0.3.9"
"@edge-runtime/format@^1.1.0-beta.23":
version "1.1.0-beta.23"
resolved "https://registry.yarnpkg.com/@edge-runtime/format/-/format-1.1.0-beta.23.tgz#c28fddee0c45a62be63f691a01306c611c99e739"
integrity sha512-TuY7ywLzp2XQQZpM8cX1dzm2QMK2juvtpMVR8K61utL2qvokzJ4gBYLcPSKhH0EWAt4WwgymNVRX0cpdSLAqAg==
"@edge-runtime/format@^1.1.0-beta.32":
version "1.1.0-beta.32"
resolved "https://registry.yarnpkg.com/@edge-runtime/format/-/format-1.1.0-beta.32.tgz#6f0d5a8726cc54ebb2b1dcb86fe25c0ff093731d"
integrity sha512-wpQtbgHJuSF1fvDV6Gxg2L7uNpzhQnRl91DREgOdRfa3S8y9AANjPi1/g3GPBGPiGZoX2Fv+MKV6RgY5/jLfJA==
"@edge-runtime/jest-environment@1.1.0-beta.7":
version "1.1.0-beta.7"
@@ -736,22 +736,22 @@
jest-mock "28.1.1"
jest-util "28.1.1"
"@edge-runtime/primitives@^1.1.0-beta.23":
version "1.1.0-beta.23"
resolved "https://registry.yarnpkg.com/@edge-runtime/primitives/-/primitives-1.1.0-beta.23.tgz#b9b8d83e3dce63286cf80b8d5ece43f32420c4ab"
integrity sha512-0vHcZZwyxjmw/so9irYtA82/+nAlJRs+1WpRYBx7iae1FOGCPM4BIKEmboWmwTuj7c6avz9kIbptokdMUPgV9A==
"@edge-runtime/primitives@^1.1.0-beta.32":
version "1.1.0-beta.33"
resolved "https://registry.yarnpkg.com/@edge-runtime/primitives/-/primitives-1.1.0-beta.33.tgz#9bd0d866addfdc98ec32ff3ca0f6d24f412a5c03"
integrity sha512-mAZw/YRhwkaPVYwSwOTJTMMzZxfuLze6VEepsrVO/4yjnxriOf2GREgLal6OBtTcEEC44q4lqS+OSd0QaSFZEQ==
"@edge-runtime/primitives@^1.1.0-beta.7":
version "1.1.0-beta.7"
resolved "https://registry.yarnpkg.com/@edge-runtime/primitives/-/primitives-1.1.0-beta.7.tgz#0450ee3e5e03a8898ee072c0d0ee01fd2c1ed8f1"
integrity sha512-ZwuSMpmrf2mAj/O7EWxKOXrC03YMkU64N+CgvVFOtJGfhydk4Db/392Zama3BjNYAMOr/oY9L7HxfPutAFesKw==
"@edge-runtime/vm@1.1.0-beta.23", "@edge-runtime/vm@^1.1.0-beta.23":
version "1.1.0-beta.23"
resolved "https://registry.yarnpkg.com/@edge-runtime/vm/-/vm-1.1.0-beta.23.tgz#b55d9add18cb7bb57acf184f6cd7b6edec782a25"
integrity sha512-XBp3rCuX4scJVOo2KconAotL5XGX3zdd8IkfDNr5VVSQ/B6HkiTNuf+EvzSQTpplF+fiyLTpfcP9EbNLibwLTA==
"@edge-runtime/vm@1.1.0-beta.32", "@edge-runtime/vm@^1.1.0-beta.32":
version "1.1.0-beta.32"
resolved "https://registry.yarnpkg.com/@edge-runtime/vm/-/vm-1.1.0-beta.32.tgz#1bc9c77a88343478d50009f30813b9fbf8a0f4ad"
integrity sha512-G0SH80am2XjlK6oFI3RoKlg1SBS5ZAeqakYasfNhJEXqM7g7tsoh5jURMQcNxpLvo48XBTgHgAVEMzhAGgDPZg==
dependencies:
"@edge-runtime/primitives" "^1.1.0-beta.23"
"@edge-runtime/primitives" "^1.1.0-beta.32"
"@edge-runtime/vm@^1.1.0-beta.7":
version "1.1.0-beta.7"
@@ -2855,13 +2855,6 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3"
integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog==
"@types/progress@2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/progress/-/progress-2.0.3.tgz#7ccbd9c6d4d601319126c469e73b5bb90dfc8ccc"
integrity sha512-bPOsfCZ4tsTlKiBjBhKnM8jpY5nmIll166IPD58D92hR7G7kZDfx5iB9wGF4NfZrdKolebjeAr3GouYkSGoJ/A==
dependencies:
"@types/node" "*"
"@types/promise-timeout@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@types/promise-timeout/-/promise-timeout-1.3.0.tgz#90649ff6f48c1ead9de142e6dd9f62f8c5a54022"
@@ -2973,21 +2966,6 @@
dependencies:
"@types/node" "*"
"@types/tar-fs@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/tar-fs/-/tar-fs-2.0.1.tgz#6391dcad1b03dea2d79fac07371585ab54472bb1"
integrity sha512-qlsQyIY9sN7p221xHuXKNoMfUenOcvEBN4zI8dGsYbYCqHtTarXOEXSIgUnK+GcR0fZDse6pAIc5pIrCh9NefQ==
dependencies:
"@types/node" "*"
"@types/tar-stream" "*"
"@types/tar-stream@*":
version "2.2.2"
resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-2.2.2.tgz#be9d0be9404166e4b114151f93e8442e6ab6fb1d"
integrity sha512-1AX+Yt3icFuU6kxwmPakaiGrJUwG44MpuiqPg4dSolRFk6jmvs4b3IbUol9wKDLIgU76gevn3EwE8y/DkSJCZQ==
dependencies:
"@types/node" "*"
"@types/tar@^4.0.0":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@types/tar/-/tar-4.0.3.tgz#e2cce0b8ff4f285293243f5971bd7199176ac489"
@@ -3234,10 +3212,10 @@
resolved "https://registry.yarnpkg.com/@vercel/ncc/-/ncc-0.24.0.tgz#a2e8783a185caa99b5d8961a57dfc9665de16296"
integrity sha512-crqItMcIwCkvdXY/V3/TzrHJQx6nbIaRqE1cOopJhgGX6izvNov40SmD//nS5flfEvdK54YGjwVVq+zG6crjOg==
"@vercel/nft@0.22.0":
version "0.22.0"
resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.22.0.tgz#586ed4edfd0dabc9bf07525044782198a0b31199"
integrity sha512-hB80/093PPiCefN2gVbqv6J93MH+63Zr7uDCwkiS/U4W07DXkLoftbnkBmZoS0Q84LiTSl9DRVSHU4XYCX+sJA==
"@vercel/nft@0.22.1":
version "0.22.1"
resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.22.1.tgz#0d91d2a21e3a7f0b23ce1550da9870eac4942828"
integrity sha512-lYYZIoxRurqDOSoVIdBicGnpUIpfyaS5qVjdPq+EfI285WqtZK3NK/dyCkiyBul+X2U2OEhRyeMdXPCHGJbohw==
dependencies:
"@mapbox/node-pre-gyp" "^1.0.5"
acorn "^8.6.0"
@@ -5495,13 +5473,13 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
edge-runtime@1.1.0-beta.23:
version "1.1.0-beta.23"
resolved "https://registry.yarnpkg.com/edge-runtime/-/edge-runtime-1.1.0-beta.23.tgz#fd4d93f021c622e9b188399fa83e6bd5e445cb5e"
integrity sha512-A7dO/Y+4UJnaxFcdz6pepL+0GcvvViWvf201oFQXepgdSxPDKiqxaayCag0eiirQ6OfF+cSTmPD3xrfEoAIjiQ==
edge-runtime@1.1.0-beta.32:
version "1.1.0-beta.32"
resolved "https://registry.yarnpkg.com/edge-runtime/-/edge-runtime-1.1.0-beta.32.tgz#e43fd53c57fdba3c567b3fef50743cba00ff5e49"
integrity sha512-fbqqUF3OKynqtWgExhjyxXX2SwbkWuwmjUYhml3Sv8Y/vkrTxyTKrxS0MoxUy5sGPB3BBEtpopn36cQgwlOpAg==
dependencies:
"@edge-runtime/format" "^1.1.0-beta.23"
"@edge-runtime/vm" "^1.1.0-beta.23"
"@edge-runtime/format" "^1.1.0-beta.32"
"@edge-runtime/vm" "^1.1.0-beta.32"
exit-hook "2.2.1"
http-status "1.5.2"
mri "1.2.0"
@@ -6990,7 +6968,7 @@ graceful-fs@^4.2.9:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96"
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
handlebars@^4.7.6:
handlebars@^4.7.6, handlebars@^4.7.7:
version "4.7.7"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
@@ -8514,6 +8492,11 @@ json5@^2.2.1:
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
jsonc-parser@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.1.0.tgz#73b8f0e5c940b83d03476bc2e51a20ef0932615d"
integrity sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -8955,6 +8938,11 @@ lru_map@0.3.3:
resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd"
integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=
lunr@^2.3.9:
version "2.3.9"
resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1"
integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==
macos-release@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f"
@@ -9045,6 +9033,11 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
marked@^4.0.18:
version "4.0.19"
resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.19.tgz#d36198d1ac1255525153c351c68c75bc1d7aee46"
integrity sha512-rgQF/OxOiLcvgUAj1Q1tAf4Bgxn5h5JZTp04Fx4XUkVhs7B+7YA9JEWJhJpoO8eJt8MkZMwqLCNeNqj1bCREZQ==
matcher@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/matcher/-/matcher-2.1.0.tgz#64e1041c15b993e23b786f93320a7474bf833c28"
@@ -9301,7 +9294,7 @@ minimatch@5.0.1:
dependencies:
brace-expansion "^2.0.1"
minimatch@^5.0.1:
minimatch@^5.0.1, minimatch@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7"
integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==
@@ -10521,11 +10514,6 @@ process-nextick-args@~2.0.0:
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
progress@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
promise-inflight@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
@@ -11368,6 +11356,15 @@ shebang-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
shiki@^0.10.1:
version "0.10.1"
resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.10.1.tgz#6f9a16205a823b56c072d0f1a0bcd0f2646bef14"
integrity sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==
dependencies:
jsonc-parser "^3.0.0"
vscode-oniguruma "^1.6.1"
vscode-textmate "5.2.0"
signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
@@ -12615,6 +12612,28 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typedoc-plugin-markdown@3.13.4:
version "3.13.4"
resolved "https://registry.yarnpkg.com/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.13.4.tgz#4c2f02b254c47d25b91579177900c1da5c5579b8"
integrity sha512-E/EBBmu6ARtnbswZGtBVBB/BfukZiGMOlqPc0RXCI/NFitONBahFqbCAF5fKQlijlcfipJj5pw5AMFH3NytrAw==
dependencies:
handlebars "^4.7.7"
typedoc-plugin-mdn-links@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/typedoc-plugin-mdn-links/-/typedoc-plugin-mdn-links-2.0.0.tgz#b43893bc7bdbb94927091f66d465f31e8440ad6c"
integrity sha512-IGLuelXPOenGdmklr5DHgPPf/MfZj7aEYCxCMtPN8C1D0lA7w0YLahd0jhDDcOMU7zL1EPcM5pPnhZHltDhqGQ==
typedoc@0.23.10:
version "0.23.10"
resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.23.10.tgz#285d595a5f2e35ccdf6f38eba4dfe951d5bff461"
integrity sha512-03EUiu/ZuScUBMnY6p0lY+HTH8SwhzvRE3gImoemdPDWXPXlks83UGTx++lyquWeB1MTwm9D9Ca8RIjkK3AFfQ==
dependencies:
lunr "^2.3.9"
marked "^4.0.18"
minimatch "^5.1.0"
shiki "^0.10.1"
typescript@4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.4.tgz#3f85b986945bcf31071decdd96cf8bfa65f9dcbc"
@@ -12928,6 +12947,16 @@ verror@1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
vscode-oniguruma@^1.6.1:
version "1.6.2"
resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz#aeb9771a2f1dbfc9083c8a7fdd9cccaa3f386607"
integrity sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==
vscode-textmate@5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e"
integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==
walker@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"