Compare commits

..

8 Commits

Author SHA1 Message Date
Steven
b5db13c970 Publish Stable
- vercel@29.0.1
 - @vercel/fs-detectors@3.8.13
 - @vercel/gatsby-plugin-vercel-builder@1.3.1
 - @vercel/next@3.8.0
 - @vercel/node@2.14.0
 - @vercel/static-build@1.3.27
2023-04-29 18:02:35 -04:00
Tim Neutkens
473f62d141 Add support for home and global as edge function regions (#9869)
Adds `home` and `global` as supported values for `regions` when coming
from Next.js. We'll port these to the build output API at a later time.

Related Next.js changes: https://github.com/vercel/next.js/pull/48959

---------

Co-authored-by: JJ Kasper <jj@jjsweb.site>
2023-04-29 19:25:25 +02:00
JJ Kasper
cc140d6e28 [node] Fix exposing WebSocket to edge runtime and legacy next tests (#9871)
This ensures we expose WebSocket correctly for edge runtime for Node.js
v16 and v18 properly. This also fixes failing legacy Next.js tests due
to a bad `@babel/compat-data` package publish from 8 hours ago.

x-ref: [slack
thread](https://vercel.slack.com/archives/C04R82GSDBN/p1682709850008189?thread_ts=1682707749.424459&cid=C04R82GSDBN)
x-ref: https://github.com/vercel/next.js/pull/48924
x-ref: https://github.com/vercel/vercel/pull/9860
Closes: https://github.com/vercel/vercel/pull/9870
2023-04-29 10:43:40 +02:00
Kiko Beats
09ac96ae21 [node] expose WebSocket constructor in Edge functions (#9860)
This PR exposes `WebSocket` to be used for Edge Runtime for `vc dev`.

Ideally, that's a thing we should to handle inside `edge-runtime`
dependency.

Unfortunately, that is not as easy as it sounds, since Edge Runtime is
doing a heavy use of vm module and `undici` is using some Node.js APIs
that are not available, or they are hard to make them working inside vm.

Related PR: https://github.com/vercel/edge-runtime/pull/309

While we're finishing that work, in order to don't delay users to start
using `WebSocket`, we can simply expose it directly from `undici` as
dependency.

---------

Co-authored-by: Chris Barber <chris.barber@vercel.com>
2023-04-28 23:03:35 +02:00
Nathan Rajlich
ebd1bf41b6 [node] Don't compile "edge" functions with Babel (#9868) 2023-04-28 20:37:26 +00:00
Steven
e1a16b0333 [node] Add log message about compiling ESM to CJS (#9866)
When `@vercel/node` was created 5 or so years ago, Node.js didn't support ESM out of the box. So to create a zero configuration experience, babel was introduce to compile the authored ESM into the runtime CJS.

Today, Node.js supports ESM but you have to opt in using `type: module` in package.json or the `.mjs` file extension.

Indeed `@vercel/node` detects ESM and skips babel compilation. However, there is a case where the source code is ESM and a dependency supports both ESM and CJS via `imports` in package.json. This case will break because `@vercel/nft` traces the source file as ESM and assumes the dependency will be resolved as ESM too. But after `@vercel/nft` traces the source file, `@vercel/node` will compile to CJS and thus the trace will be incorrect and fail at runtime.

So this PR adds a warning when babel compiles ESM to CJS because it is no longer expected in 2023.
2023-04-28 19:58:43 +00:00
Shohei Maeda
ed56822675 [next] support functions property in appDir (#9811)
Currently, `functions` property (i.e., `memory` and `maxDuration`) are
not supported in appDir routes. This PR attempts to add support.

---------

Co-authored-by: Chris Barber <chris.barber@vercel.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2023-04-28 13:59:23 -05:00
Sean Massa
ce597ce0ec [node] validate function runtime and throw when "nodejs" (#9851)
We want to keep the currently unused `runtime: "nodejs"` value clear for future use. This PR effectively makes any unrecognized value fail the build. If the value is "nodejs" specifically, a direct error is thrown suggesting they remove the value.

Only a handful of projects set this value currently. We're reaching out to them to let them know that this value is not currently supported and that they should remove it. Then, we should deploy this PR so that anyone else using this value will know to remove it.
2023-04-27 19:22:47 +00:00
39 changed files with 741 additions and 414 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "29.0.0",
"version": "29.0.1",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -35,13 +35,13 @@
"@vercel/build-utils": "6.7.2",
"@vercel/go": "2.5.1",
"@vercel/hydrogen": "0.0.64",
"@vercel/next": "3.7.6",
"@vercel/node": "2.13.0",
"@vercel/next": "3.8.0",
"@vercel/node": "2.14.0",
"@vercel/python": "3.1.60",
"@vercel/redwood": "1.1.15",
"@vercel/remix-builder": "1.8.6",
"@vercel/ruby": "1.3.76",
"@vercel/static-build": "1.3.26"
"@vercel/static-build": "1.3.27"
},
"devDependencies": {
"@alex_neo/jest-expect-message": "1.0.5",
@@ -90,7 +90,7 @@
"@vercel/client": "12.4.12",
"@vercel/error-utils": "1.0.10",
"@vercel/frameworks": "1.3.5",
"@vercel/fs-detectors": "3.8.12",
"@vercel/fs-detectors": "3.8.13",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.2.1",

View File

@@ -207,8 +207,8 @@ test('[vercel dev] should handle config errors thrown in edge functions', async
expect(await res.text()).toMatch(
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
);
expect(stderr).toMatch(
/Invalid function runtime "invalid-runtime-value" for "api\/edge-error-config.js". Valid runtimes are: \["edge","experimental-edge"\]/g
expect(stderr).toContain(
'api/edge-error-config.js: unsupported "runtime" value in `config`: "invalid-runtime-value" (must be one of: ["edge","experimental-edge"]). Learn more: https://vercel.link/creating-edge-functions'
);
} finally {
await dev.kill();

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "3.8.12",
"version": "3.8.13",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View File

@@ -667,7 +667,12 @@ function checkUnusedFunctions(
// Next.js can use functions only for `src/pages` or `pages`
if (frontendBuilder && isOfficialRuntime('next', frontendBuilder.use)) {
for (const fnKey of unusedFunctions.values()) {
if (fnKey.startsWith('pages/') || fnKey.startsWith('src/pages')) {
if (
fnKey.startsWith('pages/') ||
fnKey.startsWith('src/pages') ||
fnKey.startsWith('app/') ||
fnKey.startsWith('src/app/')
) {
unusedFunctions.delete(fnKey);
} else {
return {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/gatsby-plugin-vercel-builder",
"version": "1.3.0",
"version": "1.3.1",
"main": "dist/index.js",
"files": [
"dist",
@@ -16,7 +16,7 @@
"dependencies": {
"@sinclair/typebox": "0.25.24",
"@vercel/build-utils": "6.7.2",
"@vercel/node": "2.13.0",
"@vercel/node": "2.14.0",
"@vercel/routing-utils": "2.2.1",
"esbuild": "0.14.47",
"etag": "1.8.1",

View File

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

View File

@@ -1242,30 +1242,36 @@ async function getSourceFilePathFromPage({
// value used during next build
const extensionsToTry = pageExtensions || ['js', 'jsx', 'ts', 'tsx'];
let fsPath = path.join(workPath, 'pages', page);
if (await usesSrcDirectory(workPath)) {
fsPath = path.join(workPath, 'src', 'pages', page);
}
for (const pageType of ['pages', 'app']) {
let fsPath = path.join(workPath, pageType, page);
if (await usesSrcDirectory(workPath)) {
fsPath = path.join(workPath, 'src', pageType, page);
}
if (fs.existsSync(fsPath)) {
return path.relative(workPath, fsPath);
}
const extensionless = fsPath.slice(0, -3); // remove ".js"
for (const ext of extensionsToTry) {
fsPath = `${extensionless}.${ext}`;
if (fs.existsSync(fsPath)) {
return path.relative(workPath, fsPath);
}
}
const extensionless = fsPath.replace(path.extname(fsPath), '');
if (isDirectory(extensionless)) {
for (const ext of extensionsToTry) {
fsPath = path.join(extensionless, `index.${ext}`);
fsPath = `${extensionless}.${ext}`;
if (fs.existsSync(fsPath)) {
return path.relative(workPath, fsPath);
}
}
if (isDirectory(extensionless)) {
for (const ext of extensionsToTry) {
fsPath = path.join(extensionless, `index.${ext}`);
if (fs.existsSync(fsPath)) {
return path.relative(workPath, fsPath);
}
fsPath = path.join(extensionless, `route.${ext}`);
if (fs.existsSync(fsPath)) {
return path.relative(workPath, fsPath);
}
}
}
}
console.log(
@@ -2346,6 +2352,8 @@ interface MiddlewareManifestV2 {
functions?: { [page: string]: EdgeFunctionInfoV2 };
}
type Regions = 'home' | 'global' | 'auto' | string[] | 'all' | 'default';
interface BaseEdgeFunctionInfo {
env: string[];
files: string[];
@@ -2353,7 +2361,7 @@ interface BaseEdgeFunctionInfo {
page: string;
wasm?: { filePath: string; name: string }[];
assets?: { filePath: string; name: string }[];
regions?: 'auto' | string[] | 'all' | 'default';
regions?: Regions;
}
interface EdgeFunctionInfoV1 extends BaseEdgeFunctionInfo {
@@ -2371,6 +2379,53 @@ interface EdgeFunctionMatcher {
originalSource?: string;
}
const vercelFunctionRegionsVar = process.env.VERCEL_FUNCTION_REGIONS;
let vercelFunctionRegions: string[] | undefined;
if (vercelFunctionRegionsVar) {
vercelFunctionRegions = vercelFunctionRegionsVar.split(',');
}
/**
* Normalizes the regions config that comes from the Next.js edge functions manifest.
* Ensures that config like `home` and `global` are converted to the corresponding Vercel region config.
* In the future we'll want to make `home` and `global` part of the Build Output API.
* - `home` refers to the regions set in vercel.json or on the Vercel dashboard project config.
* - `global` refers to all regions.
*/
function normalizeRegions(regions: Regions): string[] {
if (typeof regions === 'string') {
regions = [regions];
}
const newRegions: string[] = [];
for (const region of regions) {
// Explicitly mentioned as `home` is one of the explicit values for preferredRegion in Next.js.
if (region === 'home') {
if (vercelFunctionRegions) {
// Includes the regions from the VERCEL_FUNCTION_REGIONS env var.
newRegions.push(...vercelFunctionRegions);
}
continue;
}
// Explicitly mentioned as `global` is one of the explicit values for preferredRegion in Next.js.
if (region === 'global') {
// Uses `all` instead as that's how it's implemented on Vercel.
newRegions.push('all');
continue;
}
// Explicitly mentioned as `auto` is one of the explicit values for preferredRegion in Next.js.
if (region === 'auto') {
newRegions.push('auto');
continue;
}
newRegions.push(region);
}
return [];
}
export async function getMiddlewareBundle({
entryPath,
outputDirectory,
@@ -2500,7 +2555,9 @@ export async function getMiddlewareBundle({
...wasmFiles,
...assetFiles,
},
regions: edgeFunction.regions,
regions: edgeFunction.regions
? normalizeRegions(edgeFunction.regions)
: undefined,
entrypoint: 'index.js',
envVarsInUse: edgeFunction.env,
assets: (edgeFunction.assets ?? []).map(({ name }) => {

View File

@@ -2,7 +2,19 @@
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
"use": "@vercel/next",
"config": {
"functions": {
"app/api/**/*": {
"maxDuration": 5,
"memory": 512
},
"pages/api/**/*": {
"maxDuration": 5,
"memory": 512
}
}
}
}
],
"probes": [

View File

@@ -14,5 +14,8 @@
"next": "10.1.3",
"react": "17.0.2",
"react-dom": "17.0.2"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -15,5 +15,8 @@
"next": "11.1.0",
"react": "17.0.2",
"react-dom": "17.0.2"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -6,5 +6,8 @@
"next": "9.5.5",
"react": "17.0.2",
"react-dom": "17.0.2"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -6,5 +6,8 @@
"next": "9.5.3",
"react": "16.13.1",
"react-dom": "16.13.1"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -9,5 +9,8 @@
"next": "8.0.3",
"react": "latest",
"react-dom": "latest"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -6,5 +6,8 @@
"next": "10.0.5",
"react": "latest",
"react-dom": "latest"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -10,5 +10,8 @@
"next": "10.0.8",
"react": "latest",
"react-dom": "latest"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -6,5 +6,8 @@
"next": "10.0.8",
"react": "latest",
"react-dom": "latest"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -6,5 +6,8 @@
"next": "10.0.8",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -12,5 +12,8 @@
"next": "10.0.8",
"react": "17.0.1",
"react-dom": "17.0.1"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -6,5 +6,8 @@
"next": "8.x.x",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -6,5 +6,8 @@
"next": "9.1.6-canary.1",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -16,6 +16,9 @@
"react": "^16.12.0",
"react-dom": "^16.12.0"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
},
"devDependencies": {
"@babel/plugin-syntax-class-properties": "^7.7.4",
"@types/next": "^9.0.0",

View File

@@ -6,5 +6,8 @@
"next": "9.2.3-canary.13",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -6,5 +6,8 @@
"next": "9.2.3-canary.4",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -9,5 +9,8 @@
"next": "9.3.4",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -8,5 +8,8 @@
"next": "9.3.4",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -6,5 +6,8 @@
"next": "9.4.4",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -10,5 +10,8 @@
"next": "9.5.1",
"react": "16.13.1",
"react-dom": "16.13.1"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -65,14 +65,21 @@ if (parseInt(process.versions.node.split('.')[0], 10) >= 16) {
}
}
expect(lambdas.size).toBe(2);
expect(lambdas.size).toBe(3);
expect(buildResult.output['dashboard']).toBeDefined();
expect(buildResult.output['dashboard/another']).toBeDefined();
expect(buildResult.output['dashboard/changelog']).toBeDefined();
expect(buildResult.output['dashboard/deployments/[id]']).toBeDefined();
expect(buildResult.output['api/hello']).toBeDefined();
expect(buildResult.output['api/hello'].type).toBe('Lambda');
expect(buildResult.output['api/hello'].memory).toBe(512);
expect(buildResult.output['api/hello'].maxDuration).toBe(5);
expect(buildResult.output['api/hello-again']).toBeDefined();
expect(buildResult.output['api/hello-again'].type).toBe('Lambda');
expect(buildResult.output['api/hello-again'].memory).toBe(512);
expect(buildResult.output['api/hello-again'].maxDuration).toBe(5);
expect(
buildResult.output['api/hello-again'].supportsResponseStreaming
).toBe(true);

View File

@@ -6,5 +6,8 @@
"next": "9",
"react": "16",
"react-dom": "16"
},
"resolutions": {
"@babel/compat-data": "7.21.4"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node",
"version": "2.13.0",
"version": "2.14.0",
"license": "Apache-2.0",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -33,7 +33,9 @@
"path-to-regexp": "6.2.1",
"ts-morph": "12.0.0",
"ts-node": "10.9.1",
"typescript": "4.9.5"
"typescript": "4.9.5",
"undici": "5.22.0",
"ws": "8.13.0"
},
"devDependencies": {
"@babel/core": "7.5.0",

View File

@@ -12,7 +12,7 @@ import { Config } from '@vercel/build-utils';
import { createEdgeEventHandler } from './edge-functions/edge-handler.mjs';
import { createServer, IncomingMessage, ServerResponse } from 'http';
import { createServerlessEventHandler } from './serverless-functions/serverless-handler.mjs';
import { EdgeRuntimes, isEdgeRuntime, logError } from './utils.js';
import { isEdgeRuntime, logError, validateConfiguredRuntime } from './utils.js';
import { getConfig } from '@vercel/static-config';
import { Project } from 'ts-morph';
import asyncListen from 'async-listen';
@@ -22,17 +22,6 @@ const { default: listen } = asyncListen;
const parseConfig = (entryPointPath: string) =>
getConfig(new Project(), entryPointPath);
function getRuntime(runtime: string | undefined, entrypoint: string) {
if (runtime && !isEdgeRuntime(runtime)) {
throw new Error(
`Invalid function runtime "${runtime}" for "${entrypoint}". Valid runtimes are: ${JSON.stringify(
Object.values(EdgeRuntimes)
)}. Learn more: https://vercel.link/creating-edge-functions`
);
}
return runtime;
}
async function createEventHandler(
entrypoint: string,
config: Config,
@@ -40,7 +29,9 @@ async function createEventHandler(
): Promise<(request: IncomingMessage) => Promise<VercelProxyResponse>> {
const entrypointPath = join(process.cwd(), entrypoint!);
const staticConfig = parseConfig(entrypointPath);
const runtime = getRuntime(staticConfig?.runtime, entrypoint);
const runtime = staticConfig?.runtime;
validateConfiguredRuntime(runtime, entrypoint);
// `middleware.js`/`middleware.ts` file is always run as
// an Edge Function, otherwise needs to be opted-in via

View File

@@ -134,6 +134,17 @@ async function createEdgeRuntimeServer(params?: {
const wasmBindings = await params.wasmAssets.getContext();
const nodeCompatBindings = params.nodeCompatBindings.getContext();
let WebSocket: any;
// undici's WebSocket handling is only available in Node.js >= 18
// so fallback to using ws for v16
if (Number(process.version.split('.')[0].substring(1)) < 18) {
// @ts-ignore
WebSocket = (await import('ws')).WebSocket;
} else {
WebSocket = (await import('undici')).WebSocket;
}
const runtime = new EdgeRuntime({
initialCode: params.userCode,
extend: context => {
@@ -141,6 +152,8 @@ async function createEdgeRuntimeServer(params?: {
// This is required for esbuild wrapping logic to resolve
module: {},
WebSocket,
// This is required for environment variable access.
// In production, env var access is provided by static analysis
// so that only the used values are available.

View File

@@ -21,7 +21,11 @@ export function forkDevServer(options: {
*/
devServerPath?: string;
}) {
let nodeOptions = process.env.NODE_OPTIONS;
let nodeOptions = process.env.NODE_OPTIONS || '';
if (!nodeOptions.includes('--no-warnings')) {
nodeOptions += ' --no-warnings';
}
const tsNodePath = options.require_.resolve('ts-node');
const esmLoader = pathToFileURL(join(tsNodePath, '..', '..', 'esm.mjs'));
const cjsLoader = join(tsNodePath, '..', '..', 'register', 'index.js');
@@ -78,7 +82,7 @@ function checkForPid(
export async function readMessage(
child: ChildProcess
): Promise<
| { state: 'message'; value: { port: number } }
| { state: 'message'; value: { address?: string; port: number } }
| { state: 'exit'; value: [number, string | null] }
> {
const onMessage = once<{ port: number }>(child, 'message');

View File

@@ -54,7 +54,7 @@ import { getConfig } from '@vercel/static-config';
import { fixConfig, Register, register } from './typescript';
import {
EdgeRuntimes,
validateConfiguredRuntime,
entrypointToOutputPath,
getRegExpFromMatchers,
isEdgeRuntime,
@@ -76,8 +76,6 @@ interface DownloadOptions {
meta: Meta;
}
const ALLOWED_RUNTIMES = ['nodejs', ...Object.values(EdgeRuntimes)];
const require_ = eval('require');
const tscPath = resolve(dirname(require_.resolve('typescript')), '../bin/tsc');
@@ -121,6 +119,7 @@ async function compile(
baseDir: string,
entrypointPath: string,
config: Config,
meta: Meta,
nodeVersion: NodeVersion,
isEdgeFunction: boolean
): Promise<{
@@ -310,7 +309,9 @@ async function compile(
!file.endsWith('.mjs') &&
!file.match(libPathRegEx)
);
if (esmPaths.length) {
const babelCompileEnabled =
!isEdgeFunction || process.env.VERCEL_EDGE_NO_BABEL !== '1';
if (babelCompileEnabled && esmPaths.length) {
const babelCompile = require('./babel').compile;
for (const path of esmPaths) {
const pathDir = join(workPath, dirname(path));
@@ -335,6 +336,13 @@ async function compile(
stream: preparedFiles[path].toStream(),
});
if (!meta.compiledToCommonJS) {
meta.compiledToCommonJS = true;
console.warn(
'Warning: Node.js functions are compiled from ESM to CommonJS. If this is not intended, add "type": "module" to your package.json file.'
);
}
console.log(`Compiling "${filename}" from ESM to CommonJS...`);
const { code, map } = babelCompile(filename, source);
shouldAddSourcemapSupport = true;
preparedFiles[path] = new FileBlob({
@@ -409,20 +417,12 @@ export const build: BuildV3 = async ({
const project = new Project();
const staticConfig = getConfig(project, entrypointPath);
if (staticConfig?.runtime) {
if (!ALLOWED_RUNTIMES.includes(staticConfig.runtime)) {
throw new Error(
`Unsupported "runtime" property in \`config\`: ${JSON.stringify(
staticConfig.runtime
)} (must be one of: ${JSON.stringify(ALLOWED_RUNTIMES)})`
);
}
if (staticConfig.runtime === 'nodejs') {
console.log(
`Detected unused static config runtime "nodejs" in "${entrypointPath}"`
);
}
isEdgeFunction = isEdgeRuntime(staticConfig.runtime);
const runtime = staticConfig?.runtime;
validateConfiguredRuntime(runtime, entrypoint);
if (runtime) {
isEdgeFunction = isEdgeRuntime(runtime);
}
debug('Tracing input files...');
@@ -432,6 +432,7 @@ export const build: BuildV3 = async ({
baseDir,
entrypointPath,
config,
meta,
nodeVersion,
isEdgeFunction
);

View File

@@ -81,6 +81,31 @@ export function isEdgeRuntime(runtime?: string): runtime is EdgeRuntimes {
);
}
const ALLOWED_RUNTIMES: string[] = Object.values(EdgeRuntimes);
export function validateConfiguredRuntime(
runtime: string | undefined,
entrypoint: string
) {
if (runtime) {
if (runtime === 'nodejs') {
throw new Error(
`${entrypoint}: \`config.runtime: "nodejs"\` semantics will evolve soon. Please remove the \`runtime\` key to keep the existing behavior.`
);
}
if (!ALLOWED_RUNTIMES.includes(runtime)) {
throw new Error(
`${entrypoint}: unsupported "runtime" value in \`config\`: ${JSON.stringify(
runtime
)} (must be one of: ${JSON.stringify(
ALLOWED_RUNTIMES
)}). Learn more: https://vercel.link/creating-edge-functions`
);
}
}
}
export async function serializeBody(
request: IncomingMessage
): Promise<Buffer | undefined> {

View File

@@ -0,0 +1,42 @@
/*global TextEncoderStream, ReadableStream, Response, WebSocket */
export const config = { runtime: 'edge' };
const createWebSocket = url =>
new Promise((resolve, reject) => {
const ws = new WebSocket(url);
ws.onopen = () => resolve(ws);
ws.onerror = reject;
});
let data = [...Array(4).keys()];
export default async () => {
const ws = await createWebSocket('wss://ws.postman-echo.com/raw');
const interval = 100;
let timer;
const end = controller => {
clearInterval(timer);
setTimeout(() => {
controller.close();
ws.close();
}, interval);
};
const readable = new ReadableStream({
async start(controller) {
ws.onmessage = ({ data }) => controller.enqueue(data);
timer = setInterval(() => {
const value = data.pop();
if (value === undefined) return end(controller);
ws.send(value);
}, interval);
},
cancel() {
clearInterval(timer);
},
}).pipeThrough(new TextEncoderStream());
return new Response(readable);
};

View File

@@ -24,6 +24,31 @@ function testForkDevServer(entrypoint: string) {
});
}
test('runs an edge function that uses `WebSocket`', async () => {
const child = testForkDevServer('./edge-websocket.js');
try {
const result = await readMessage(child);
if (result.state !== 'message') {
throw new Error('Exited. error: ' + JSON.stringify(result.value));
}
const { address, port } = result.value;
const response = await fetch(
`http://${address}:${port}/api/edge-websocket`
);
expect({
status: response.status,
body: await response.text(),
}).toEqual({
status: 200,
body: '3210',
});
} finally {
child.kill(9);
}
});
test('runs an edge function that uses `buffer`', async () => {
const child = testForkDevServer('./edge-buffer.js');
try {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/static-build",
"version": "1.3.26",
"version": "1.3.27",
"license": "Apache-2.0",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/build-step",
@@ -20,7 +20,7 @@
},
"dependencies": {
"@vercel/gatsby-plugin-vercel-analytics": "1.0.10",
"@vercel/gatsby-plugin-vercel-builder": "1.3.0"
"@vercel/gatsby-plugin-vercel-builder": "1.3.1"
},
"devDependencies": {
"@types/aws-lambda": "8.10.64",
@@ -35,7 +35,7 @@
"@vercel/build-utils": "6.7.2",
"@vercel/error-utils": "1.0.10",
"@vercel/frameworks": "1.3.5",
"@vercel/fs-detectors": "3.8.12",
"@vercel/fs-detectors": "3.8.13",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.2.1",
"@vercel/static-config": "2.0.17",

775
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff