Compare commits

..

12 Commits

Author SHA1 Message Date
Nathan Rajlich
70a53515bd Publish Stable
- vercel@28.16.2
 - @vercel/fs-detectors@3.8.0
 - @vercel/next@3.5.0
 - @vercel/remix@1.3.4
 - @vercel/ruby@1.3.66
 - @vercel/static-build@1.3.10
2023-02-16 15:12:31 -08:00
Sean Massa
4d4f0fa672 [next] Add Operation Types to Next.js Lambdas (#9196)
In order to have Next.js Lambdas show their operation types more specifically in the build output in the dashboard, the builder needs to return the Lambdas with `operationType` set to the appropriate value.

This PR adds those values. This allows the Richer Deployment Outputs to show the different types of serverless functions:
<img width="228" alt="Screenshot 2023-02-03 at 3 49 42 PM" src="https://user-images.githubusercontent.com/41545/216717479-d02fbd4a-fa62-479d-8b65-bd77fdcdb26c.png">
2023-02-16 22:59:14 +00:00
Felix Haus
46c0fd153a [fs-detectors] Remove increments of 64 limit for function memory (#9465)
Missed this occurrence so it still prevents the upload of serverless
functions that have a mem value that is not dividable by 64.
Should be the last place before we can ship the documentation update.

#### Related PRs
- #9440

Co-authored-by: Steven <steven@ceriously.com>
2023-02-16 16:42:42 -05:00
Marc Greenstock
1c8b4717e3 [ruby] fix: HEAD requests (#9436)
When WEBrick receives `HEAD` requests it discards the body (i.e.
`req.body.nil? => true`), this causes Vercel to throw a
`BODY_NOT_A_STRING_FROM_FUNCTION` since it is expecting the serverless
function to respond with a string in the body.

---------

Co-authored-by: Nathan Rajlich <n@n8.io>
Co-authored-by: Steven <steven@ceriously.com>
2023-02-16 16:41:56 -05:00
Nathan Rajlich
d52d26eaac [remix] Install Node globals (#9467)
Fixes https://github.com/vercel/community/discussions/1547.
Fixes https://github.com/vercel/community/discussions/1549.
2023-02-16 21:25:19 +00:00
Vincent Voyer
db65728fc4 Publish Stable
- vercel@28.16.1
2023-02-16 15:36:16 +01:00
Vincent Voyer
a788d06f85 [cli]: fix merging of vercel.json and build result crons (#9464)
Ensures that existing crons and crons from vercel.json are merged
together correctly.
2023-02-16 15:34:56 +01:00
Vincent Voyer
3d98d1cdea Publish Stable
- @vercel/build-utils@6.3.0
 - vercel@28.16.0
 - @vercel/client@12.4.0
 - @vercel/fs-detectors@3.7.14
 - @vercel/gatsby-plugin-vercel-builder@1.1.7
 - @vercel/go@2.3.7
 - @vercel/hydrogen@0.0.53
 - @vercel/next@3.4.7
 - @vercel/node@2.9.6
 - @vercel/python@3.1.49
 - @vercel/redwood@1.1.5
 - @vercel/remix@1.3.3
 - @vercel/ruby@1.3.65
 - @vercel/static-build@1.3.9
 - @vercel/static-config@2.0.13
2023-02-16 12:08:50 +01:00
Vincent Voyer
667af829c4 [build-utils][cli][client][node][next][static-config]: forward crons from vercel.json to config.json (#9454)
This PR changes the way cron jobs are being created in the build output
API. This is my first time contributing here. If you see something
unusual, let me know.

 Good for review

Our goal is to:
- Allow creating cron jobs via the `crons` property of `vercel.json` for
end users
- Allow framework authors to create cron jobs on Vercel via the `crons`
property of the Build Output API configuration

---

As you can see, we removed the previous implementation where cron jobs
could be configured at the function code level (export const cron = ""),
on top of vercel.json `functions` property. Here's why:

- All frameworks would have to implement the configure at the function
code level
- Not all frameworks can easily map a path to a specific function
(example: SvelteKit) and would have to bail on bundling functions inside
the same lambda
- Configuring a path + scheduler provides a better mapping of what cron
jobs are as of today: API routes on a schedule and not functions on a
schedule
- Dynamic routes Cron Jobs will be supported:
/api/crons/sync-slack-team/230
- Query parameters will be supported support:
/api/crons/sync-slack-team/230?secret=32k13l2k13lk21 (= securing cron
jobs v0)
- 100% frameworks compatibility from day one

Next.js and other frameworks may choose to implement their own cron jobs
feature that will then need to be configured through the `crons`
property of `config.json` (build output API).

cc @timneutkens @Rich-Harris 

Internal thread:
https://vercel.slack.com/archives/C04DWF5HB6K/p1676366892714349
2023-02-16 11:49:09 +01:00
Nathan Rajlich
1bb7b37e0c Publish Stable
- vercel@28.15.7
 - @vercel/next@3.4.6
 - @vercel/remix@1.3.2
2023-02-15 23:41:25 -08:00
Nathan Rajlich
ca81c133b9 [remix] Fix root-level splat path (#9457)
Fixes https://github.com/orgs/vercel/discussions/1545.
2023-02-16 07:32:53 +00:00
Luba Kravchenko
f332b7856c Adds framework fields to build outputs (#9449)
Adds framework to Lambda and edge build outputs so that we can distinguish which framework they originated from when certain features should be applied to specific frameworks.

This PR adds framework to the outputs. Part 1: https://github.com/vercel/vercel/pull/9448

ticket: ED-131

x-ref: [slack channel](https://vercel.slack.com/archives/C042LHPJ1NX)
2023-02-15 22:56:29 +00:00
56 changed files with 480 additions and 244 deletions

View File

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

View File

@@ -1,4 +1,4 @@
import type { Cron, Files, FunctionFramework } from './types';
import type { Files, FunctionFramework } from './types';
/**
* An Edge Functions output
@@ -41,9 +41,6 @@ export class EdgeFunction {
/** The regions where the edge function will be executed on */
regions?: string | string[];
/** Cronjob definition for the edge function */
cron?: Cron;
/** The framework */
framework?: FunctionFramework;
@@ -56,7 +53,6 @@ export class EdgeFunction {
this.envVarsInUse = params.envVarsInUse;
this.assets = params.assets;
this.regions = params.regions;
this.cron = params.cron;
this.framework = params.framework;
}
}

View File

@@ -5,7 +5,7 @@ import minimatch from 'minimatch';
import { readlink } from 'fs-extra';
import { isSymbolicLink, isDirectory } from './fs/download';
import streamToBuffer from './fs/stream-to-buffer';
import type { Files, Config, Cron, FunctionFramework } from './types';
import type { Files, Config, FunctionFramework } from './types';
interface Environment {
[key: string]: string;
@@ -25,7 +25,6 @@ export interface LambdaOptionsBase {
supportsWrapper?: boolean;
experimentalResponseStreaming?: boolean;
operationType?: string;
cron?: Cron;
framework?: FunctionFramework;
}
@@ -64,7 +63,6 @@ export class Lambda {
environment: Environment;
allowQuery?: string[];
regions?: string[];
cron?: Cron;
/**
* @deprecated Use `await lambda.createZip()` instead.
*/
@@ -83,7 +81,6 @@ export class Lambda {
environment = {},
allowQuery,
regions,
cron,
supportsMultiPayloads,
supportsWrapper,
experimentalResponseStreaming,
@@ -138,10 +135,6 @@ export class Lambda {
);
}
if (cron !== undefined) {
assert(typeof cron === 'string', '"cron" is not a string');
}
if (framework !== undefined) {
assert(typeof framework === 'object', '"framework" is not an object');
assert(
@@ -166,7 +159,6 @@ export class Lambda {
this.environment = environment;
this.allowQuery = allowQuery;
this.regions = regions;
this.cron = cron;
this.zipBuffer = 'zipBuffer' in opts ? opts.zipBuffer : undefined;
this.supportsMultiPayloads = supportsMultiPayloads;
this.supportsWrapper = supportsWrapper;
@@ -246,7 +238,7 @@ export async function getLambdaOptionsFromFunction({
sourceFile,
config,
}: GetLambdaOptionsFromFunctionOptions): Promise<
Pick<LambdaOptions, 'memory' | 'maxDuration' | 'cron'>
Pick<LambdaOptions, 'memory' | 'maxDuration'>
> {
if (config?.functions) {
for (const [pattern, fn] of Object.entries(config.functions)) {
@@ -254,7 +246,6 @@ export async function getLambdaOptionsFromFunction({
return {
memory: fn.memory,
maxDuration: fn.maxDuration,
cron: fn.cron,
};
}
}

View File

@@ -29,11 +29,6 @@ export const functionsSchema = {
type: 'string',
maxLength: 256,
},
cron: {
type: 'string',
minLength: 9,
maxLength: 256,
},
},
},
},

View File

@@ -319,7 +319,6 @@ export interface BuilderFunctions {
runtime?: string;
includeFiles?: string;
excludeFiles?: string;
cron?: Cron;
};
}
@@ -411,7 +410,11 @@ export interface BuildResultBuildOutput {
buildOutputPath: string;
}
export type Cron = string;
export interface Cron {
path: string;
schedule: string;
}
/** The framework which created the function */
export interface FunctionFramework {
slug: string;

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.15.6",
"version": "28.16.2",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -41,16 +41,16 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "6.2.4",
"@vercel/go": "2.3.6",
"@vercel/hydrogen": "0.0.52",
"@vercel/next": "3.4.5",
"@vercel/node": "2.9.5",
"@vercel/python": "3.1.48",
"@vercel/redwood": "1.1.4",
"@vercel/remix": "1.3.1",
"@vercel/ruby": "1.3.64",
"@vercel/static-build": "1.3.8"
"@vercel/build-utils": "6.3.0",
"@vercel/go": "2.3.7",
"@vercel/hydrogen": "0.0.53",
"@vercel/next": "3.5.0",
"@vercel/node": "2.9.6",
"@vercel/python": "3.1.49",
"@vercel/redwood": "1.1.5",
"@vercel/remix": "1.3.4",
"@vercel/ruby": "1.3.66",
"@vercel/static-build": "1.3.10"
},
"devDependencies": {
"@alex_neo/jest-expect-message": "1.0.5",
@@ -93,10 +93,10 @@
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.3.10",
"@vercel/client": "12.4.0",
"@vercel/error-utils": "1.0.8",
"@vercel/frameworks": "1.3.1",
"@vercel/fs-detectors": "3.7.13",
"@vercel/fs-detectors": "3.8.0",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.1.9",

View File

@@ -16,6 +16,7 @@ import {
BuildResultV2Typical,
BuildResultV3,
NowBuildError,
Cron,
} from '@vercel/build-utils';
import {
detectBuilders,
@@ -88,6 +89,7 @@ interface BuildOutputConfig {
framework?: {
version: string;
};
crons?: Cron[];
}
/**
@@ -623,6 +625,7 @@ async function doBuild(
});
const mergedImages = mergeImages(localConfig.images, buildResults.values());
const mergedCrons = mergeCrons(localConfig.crons, buildResults.values());
const mergedWildcard = mergeWildcard(buildResults.values());
const mergedOverrides: Record<string, PathOverride> =
overrides.length > 0 ? Object.assign({}, ...overrides) : undefined;
@@ -638,6 +641,7 @@ async function doBuild(
wildcard: mergedWildcard,
overrides: mergedOverrides,
framework,
crons: mergedCrons,
};
await fs.writeJSON(join(outputDir, 'config.json'), config, { spaces: 2 });
@@ -746,6 +750,18 @@ function mergeImages(
return images;
}
function mergeCrons(
crons: BuildOutputConfig['crons'] = [],
buildResults: Iterable<BuildResult | BuildOutputConfig>
): BuildOutputConfig['crons'] {
for (const result of buildResults) {
if ('crons' in result && result.crons) {
crons = crons.concat(result.crons);
}
}
return crons;
}
function mergeWildcard(
buildResults: Iterable<BuildResult | BuildOutputConfig>
): BuildResultV2Typical['wildcard'] {

View File

@@ -13,7 +13,6 @@ import {
Builder,
BuildResultV2,
BuildResultV3,
Cron,
File,
FileFsRef,
BuilderV2,
@@ -41,7 +40,6 @@ export const OUTPUT_DIR = join(VERCEL_DIR, 'output');
* An entry in the "functions" object in `vercel.json`.
*/
interface FunctionConfiguration {
cron?: Cron;
memory?: number;
maxDuration?: number;
}
@@ -372,14 +370,12 @@ async function writeLambda(
throw new Error('Malformed `Lambda` - no "files" present');
}
const cron = functionConfiguration?.cron ?? lambda.cron;
const memory = functionConfiguration?.memory ?? lambda.memory;
const maxDuration = functionConfiguration?.maxDuration ?? lambda.maxDuration;
const config = {
...lambda,
handler: normalizePath(lambda.handler),
cron,
memory,
maxDuration,
type: undefined,

View File

@@ -93,6 +93,29 @@ const imagesSchema = {
},
};
const cronsSchema = {
type: 'array',
minItems: 0,
items: {
type: 'object',
additionalProperties: false,
required: ['path', 'schedule'],
properties: {
path: {
type: 'string',
minLength: 1,
maxLength: 512,
pattern: '^/.*',
},
schedule: {
type: 'string',
minLength: 9,
maxLength: 256,
},
},
},
};
const vercelConfigSchema = {
type: 'object',
// These are not all possibilities because `vc dev`
@@ -108,6 +131,7 @@ const vercelConfigSchema = {
trailingSlash: trailingSlashSchema,
functions: functionsSchema,
images: imagesSchema,
crons: cronsSchema,
},
};

View File

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

View File

@@ -1,3 +1,3 @@
export default function (req, res) {
res.json('hello from the edge');
res.send('Hello from cron job!');
}

View File

@@ -0,0 +1,9 @@
const fs = require('fs');
const path = require('path');
fs.rmSync(path.join(__dirname, '.vercel', 'output'), { recursive: true });
fs.mkdirSync(path.join(__dirname, '.vercel', 'output'));
fs.copyFileSync(
path.join(__dirname, 'output', 'config.json'),
path.join(__dirname, '.vercel', 'output', 'config.json')
);

View File

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

View File

@@ -0,0 +1,9 @@
{
"version": 3,
"crons": [
{
"path": "/api/cron-job-build-output",
"schedule": "0 0 * * *"
}
]
}

View File

@@ -0,0 +1,5 @@
{
"scripts": {
"build": "node build"
}
}

View File

@@ -0,0 +1,8 @@
{
"crons": [
{
"path": "/api/cron-job",
"schedule": "0 0 * * *"
}
]
}

View File

@@ -1,3 +1,3 @@
export default function (req, res) {
res.end('serverless says hello');
res.send('Hello from cron job!');
}

View File

@@ -1,17 +0,0 @@
export const config = {
runtime: 'edge',
cron: '* * * * *',
};
export default async function edge(request, event) {
const requestBody = await request.text();
return new Response(
JSON.stringify({
headerContentType: request.headers.get('content-type'),
url: request.url,
method: request.method,
body: requestBody,
})
);
}

View File

@@ -1,7 +0,0 @@
export default function (req, res) {
res.json({ memory: parseInt(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE) });
}
export const config = {
cron: '* * * * *',
};

View File

@@ -1,10 +1,8 @@
{
"functions": {
"api/overwrite/serverless.js": {
"cron": "0 10-20 * * *"
},
"api/overwrite/edge.js": {
"cron": "10 * * * *"
"crons": [
{
"path": "/api/cron-job",
"schedule": "0 0 * * *"
}
}
]
}

View File

@@ -2635,7 +2635,7 @@ test('next unsupported functions config shows warning link', async t => {
t.is(output.exitCode, 0, formatOutput(output));
t.regex(
output.stderr,
/Ignoring function property `runtime`\. When using Next\.js, only `memory`, `maxDuration`, and `cron` can be used\./gm,
/Ignoring function property `runtime`\. When using Next\.js, only `memory` and `maxDuration` can be used\./gm,
formatOutput(output)
);
t.regex(

View File

@@ -1104,32 +1104,46 @@ describe('build', () => {
it('should include crons property in build output', async () => {
const cwd = fixture('with-cron');
const output = join(cwd, '.vercel', 'output', 'functions', 'api');
const output = join(cwd, '.vercel', 'output');
try {
process.chdir(cwd);
const exitCode = await build(client);
expect(exitCode).toBe(0);
const edge = await fs.readJSON(
join(output, 'edge.func', '.vc-config.json')
);
expect(edge).toHaveProperty('cron', '* * * * *');
const config = await fs.readJSON(join(output, 'config.json'));
expect(config).toHaveProperty('crons', [
{
path: '/api/cron-job',
schedule: '0 0 * * *',
},
]);
} finally {
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
const serverless = await fs.readJSON(
join(output, 'serverless.func', '.vc-config.json')
);
expect(serverless).toHaveProperty('cron', '* * * * *');
it('should merge crons property from build output with vercel.json crons property', async () => {
const cwd = fixture('with-cron-merge');
const output = join(cwd, '.vercel', 'output');
const overwriteServerless = await fs.readJSON(
join(output, 'overwrite', 'serverless.func', '.vc-config.json')
);
expect(overwriteServerless).toHaveProperty('cron', '0 10-20 * * *');
try {
process.chdir(cwd);
const exitCode = await build(client);
expect(exitCode).toBe(0);
const overwriteEdge = await fs.readJSON(
join(output, 'overwrite', 'edge.func', '.vc-config.json')
);
expect(overwriteEdge).toHaveProperty('cron', '10 * * * *');
const config = await fs.readJSON(join(output, 'config.json'));
expect(config).toHaveProperty('crons', [
{
path: '/api/cron-job',
schedule: '0 0 * * *',
},
{
path: '/api/cron-job-build-output',
schedule: '0 0 * * *',
},
]);
} finally {
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;

View File

@@ -285,4 +285,90 @@ describe('validateConfig', () => {
expect(error!.link).toEqual('https://vercel.link/functions-and-builds');
});
it('should error when crons have missing schedule', () => {
const error = validateConfig({
// @ts-ignore
crons: [{ path: '/api/test.js' }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0]` missing required property `schedule`.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
it('should error when crons have missing path', () => {
const error = validateConfig({
// @ts-ignore
crons: [{ schedule: '* * * * *' }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0]` missing required property `path`.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
it('should error when path is too long', () => {
const error = validateConfig({
crons: [{ path: '/' + 'x'.repeat(512), schedule: '* * * * *' }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0].path` should NOT be longer than 512 characters.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
it('should error when schedule is too long', () => {
const error = validateConfig({
crons: [{ path: '/', schedule: '*'.repeat(257) }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0].schedule` should NOT be longer than 256 characters.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
it('should error when path is empty', () => {
const error = validateConfig({
crons: [{ path: '', schedule: '* * * * *' }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0].path` should NOT be shorter than 1 characters.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
it('should error when schedule is too short', () => {
const error = validateConfig({
crons: [{ path: '/', schedule: '* * * * ' }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0].schedule` should NOT be shorter than 9 characters.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
it("should error when path doesn't start with `/`", () => {
const error = validateConfig({
crons: [{ path: 'api/cron', schedule: '* * * * *' }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0].path` should match pattern "^/.*".'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "12.3.10",
"version": "12.4.0",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -43,7 +43,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "6.2.4",
"@vercel/build-utils": "6.3.0",
"@vercel/routing-utils": "2.1.9",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",

View File

@@ -3,6 +3,7 @@ import type {
BuilderFunctions,
Images,
ProjectSettings,
Cron,
} from '@vercel/build-utils';
import type { Header, Route, Redirect, Rewrite } from '@vercel/routing-utils';
@@ -154,6 +155,7 @@ export interface VercelConfig {
framework?: string | null;
outputDirectory?: string | null;
images?: Images;
crons?: Cron[];
}
export interface GitMetadata {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "3.7.13",
"version": "3.8.0",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -35,7 +35,7 @@
"@types/minimatch": "3.0.5",
"@types/node": "14.18.33",
"@types/semver": "7.3.10",
"@vercel/build-utils": "6.2.4",
"@vercel/build-utils": "6.3.0",
"typescript": "4.3.4"
}
}

View File

@@ -602,12 +602,11 @@ function validateFunctions({ functions = {} }: Options) {
if (
func.memory !== undefined &&
(func.memory < 128 || func.memory > 3008 || func.memory % 64 !== 0)
(func.memory < 128 || func.memory > 3008)
) {
return {
code: 'invalid_function_memory',
message:
'Functions must have a memory value between 128 and 3008 in steps of 64.',
message: 'Functions must have a memory value between 128 and 3008',
};
}

View File

@@ -473,7 +473,7 @@ describe('Test `detectBuilders`', () => {
});
it('invalid function memory', async () => {
const functions = { 'pages/index.ts': { memory: 200 } };
const functions = { 'pages/index.ts': { memory: 127 } };
const files = ['pages/index.ts'];
const { builders, errors } = await detectBuilders(files, null, {
functions,
@@ -484,6 +484,17 @@ describe('Test `detectBuilders`', () => {
expect(errors![0].code).toBe('invalid_function_memory');
});
it('should build with function memory not dividable by 64', async () => {
const functions = { 'api/index.ts': { memory: 1000 } };
const files = ['api/index.ts'];
const { builders, errors } = await detectBuilders(files, null, {
functions,
});
expect(builders![0].use).toBe('@vercel/node');
expect(errors).toBeNull();
});
it('missing runtime version', async () => {
const functions = { 'pages/index.ts': { runtime: 'haha' } };
const files = ['pages/index.ts'];
@@ -1720,7 +1731,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
});
it('invalid function memory', async () => {
const functions = { 'pages/index.ts': { memory: 200 } };
const functions = { 'pages/index.ts': { memory: 127 } };
const files = ['pages/index.ts'];
const { builders, errors } = await detectBuilders(files, null, {
functions,
@@ -1732,6 +1743,18 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
expect(errors![0].code).toBe('invalid_function_memory');
});
it('should build with function memory not dividable by 64', async () => {
const functions = { 'api/index.ts': { memory: 1000 } };
const files = ['api/index.ts'];
const { builders, errors } = await detectBuilders(files, null, {
functions,
featHandleMiss,
});
expect(builders![0].use).toBe('@vercel/node');
expect(errors).toBeNull();
});
it('missing runtime version', async () => {
const functions = { 'pages/index.ts': { runtime: 'haha' } };
const files = ['pages/index.ts'];

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/gatsby-plugin-vercel-builder",
"version": "1.1.6",
"version": "1.1.7",
"main": "dist/index.js",
"files": [
"dist",
@@ -14,8 +14,8 @@
"build:src": "tsc -p tsconfig.src.json"
},
"dependencies": {
"@vercel/build-utils": "6.2.4",
"@vercel/node": "2.9.5",
"@vercel/build-utils": "6.3.0",
"@vercel/node": "2.9.6",
"@vercel/routing-utils": "2.1.9",
"ajv": "8.12.0",
"esbuild": "0.14.47",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "2.3.6",
"version": "2.3.7",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -36,7 +36,7 @@
"@types/node": "14.18.33",
"@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0",
"@vercel/build-utils": "6.2.4",
"@vercel/build-utils": "6.3.0",
"@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.52",
"version": "0.0.53",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -21,8 +21,8 @@
"devDependencies": {
"@types/jest": "27.5.1",
"@types/node": "14.18.33",
"@vercel/build-utils": "6.2.4",
"@vercel/static-config": "2.0.12",
"@vercel/build-utils": "6.3.0",
"@vercel/static-config": "2.0.13",
"execa": "3.2.0",
"fs-extra": "11.1.0",
"ts-morph": "12.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/next",
"version": "3.4.5",
"version": "3.5.0",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
@@ -45,7 +45,7 @@
"@types/semver": "6.0.0",
"@types/text-table": "0.2.1",
"@types/webpack-sources": "3.2.0",
"@vercel/build-utils": "6.2.4",
"@vercel/build-utils": "6.3.0",
"@vercel/nft": "0.22.5",
"@vercel/routing-utils": "2.1.9",
"async-sema": "3.0.1",

View File

@@ -88,6 +88,8 @@ import {
PseudoLayerResult,
updateRouteSrc,
validateEntrypoint,
getOperationType,
isApiPage,
} from './utils';
export const version = 2;
@@ -1090,10 +1092,14 @@ export const build: BuildV2 = async ({
handler: '___next_launcher.cjs',
runtime: nodeVersion.runtime,
...lambdaOptions,
operationType: 'SSR',
operationType: 'SSR', // always SSR because we're in legacy mode
shouldAddHelpers: false,
shouldAddSourcemapSupport: false,
supportsMultiPayloads: !!process.env.NEXT_PRIVATE_MULTI_PAYLOAD,
framework: {
slug: 'nextjs',
version: nextVersion,
},
});
debug(`Created serverless function for page: "${page}"`);
})
@@ -1122,10 +1128,6 @@ export const build: BuildV2 = async ({
outputDirectory,
appPathRoutesManifest,
});
const isApiPage = (page: string) =>
page
.replace(/\\/g, '/')
.match(/(serverless|server)\/pages\/api(\/|\.js$)/);
const canUsePreviewMode = Object.keys(pages).some(page =>
isApiPage(pages[page].fsPath)
@@ -1594,6 +1596,10 @@ export const build: BuildV2 = async ({
internalPages: [],
});
for (const group of initialApiLambdaGroups) {
group.isApiLambda = true;
}
debug(
JSON.stringify(
{
@@ -1815,7 +1821,12 @@ export const build: BuildV2 = async ({
path.relative(baseDir, entryPath),
'___next_launcher.cjs'
),
operationType: getOperationType({
prerenderManifest,
pageFileName,
}),
runtime: nodeVersion.runtime,
nextVersion,
...lambdaOptions,
});
} else {
@@ -1834,7 +1845,9 @@ export const build: BuildV2 = async ({
path.relative(baseDir, entryPath),
'___next_launcher.cjs'
),
operationType: getOperationType({ pageFileName }), // can only be API or SSR
runtime: nodeVersion.runtime,
nextVersion,
...lambdaOptions,
});
}
@@ -2034,6 +2047,11 @@ export const build: BuildV2 = async ({
pageLambdaMap[page] = group.lambdaIdentifier;
}
const operationType = getOperationType({
group,
prerenderManifest,
});
lambdas[group.lambdaIdentifier] =
await createLambdaFromPseudoLayers({
files: {
@@ -2045,7 +2063,9 @@ export const build: BuildV2 = async ({
path.relative(baseDir, entryPath),
'___next_launcher.cjs'
),
operationType,
runtime: nodeVersion.runtime,
nextVersion,
});
}
)

View File

@@ -43,6 +43,7 @@ import {
getMiddlewareBundle,
getFilesMapFromReasons,
UnwrapPromise,
getOperationType,
} from './utils';
import {
nodeFileTrace,
@@ -748,6 +749,10 @@ export async function serverBuild({
internalPages,
});
for (const group of apiLambdaGroups) {
group.isApiLambda = true;
}
debug(
JSON.stringify(
{
@@ -856,6 +861,8 @@ export async function serverBuild({
}
}
const operationType = getOperationType({ group, prerenderManifest });
const lambda = await createLambdaFromPseudoLayers({
files: {
...launcherFiles,
@@ -869,11 +876,12 @@ export async function serverBuild({
),
'___next_launcher.cjs'
),
operationType,
memory: group.memory,
runtime: nodeVersion.runtime,
maxDuration: group.maxDuration,
isStreaming: group.isStreaming,
cron: group.cron,
nextVersion,
});
for (const page of group.pages) {
@@ -974,6 +982,7 @@ export async function serverBuild({
routesManifest,
isCorrectMiddlewareOrder,
prerenderBypassToken: prerenderManifest.bypassToken || '',
nextVersion,
});
const isNextDataServerResolving =

View File

@@ -15,7 +15,6 @@ import {
NodejsLambda,
EdgeFunction,
Images,
Cron,
} from '@vercel/build-utils';
import { NodeFileTraceReasons } from '@vercel/nft';
import type {
@@ -775,6 +774,7 @@ export async function createPseudoLayer(files: {
interface CreateLambdaFromPseudoLayersOptions extends LambdaOptionsWithFiles {
layers: PseudoLayer[];
isStreaming?: boolean;
nextVersion?: string;
}
// measured with 1, 2, 5, 10, and `os.cpus().length || 5`
@@ -785,6 +785,7 @@ export async function createLambdaFromPseudoLayers({
files: baseFiles,
layers,
isStreaming,
nextVersion,
...lambdaOptions
}: CreateLambdaFromPseudoLayersOptions) {
await createLambdaSema.acquire();
@@ -828,6 +829,10 @@ export async function createLambdaFromPseudoLayers({
shouldAddHelpers: false,
shouldAddSourcemapSupport: false,
supportsMultiPayloads: !!process.env.NEXT_PRIVATE_MULTI_PAYLOAD,
framework: {
slug: 'nextjs',
version: nextVersion,
},
});
}
@@ -1305,10 +1310,10 @@ export function addLocaleOrDefault(
export type LambdaGroup = {
pages: string[];
memory?: number;
cron?: Cron;
maxDuration?: number;
isStreaming?: boolean;
isPrerenders?: boolean;
isApiLambda: boolean;
pseudoLayer: PseudoLayer;
pseudoLayerBytes: number;
pseudoLayerUncompressedBytes: number;
@@ -1358,7 +1363,7 @@ export async function getPageLambdaGroups({
const routeName = normalizePage(page.replace(/\.js$/, ''));
const isPrerenderRoute = prerenderRoutes.has(routeName);
let opts: { memory?: number; maxDuration?: number; cron?: Cron } = {};
let opts: { memory?: number; maxDuration?: number } = {};
if (config && config.functions) {
const sourceFile = await getSourceFilePathFromPage({
@@ -1376,8 +1381,7 @@ export async function getPageLambdaGroups({
const matches =
group.maxDuration === opts.maxDuration &&
group.memory === opts.memory &&
group.isPrerenders === isPrerenderRoute &&
!opts.cron; // Functions with a cronjob must be on their own
group.isPrerenders === isPrerenderRoute;
if (matches) {
let newTracedFilesSize = group.pseudoLayerBytes;
@@ -1416,6 +1420,7 @@ export async function getPageLambdaGroups({
pages: [page],
...opts,
isPrerenders: isPrerenderRoute,
isApiLambda: !!isApiPage(page),
pseudoLayerBytes: initialPseudoLayer.pseudoLayerBytes,
pseudoLayerUncompressedBytes: initialPseudoLayerUncompressed,
pseudoLayer: Object.assign({}, initialPseudoLayer.pseudoLayer),
@@ -2312,12 +2317,12 @@ interface EdgeFunctionMatcher {
}
export async function getMiddlewareBundle({
config = {},
entryPath,
outputDirectory,
routesManifest,
isCorrectMiddlewareOrder,
prerenderBypassToken,
nextVersion,
}: {
config: Config;
entryPath: string;
@@ -2325,6 +2330,7 @@ export async function getMiddlewareBundle({
prerenderBypassToken: string;
routesManifest: RoutesManifest;
isCorrectMiddlewareOrder: boolean;
nextVersion: string;
}): Promise<{
staticRoutes: Route[];
dynamicRouteMap: Map<string, RouteWithSrc>;
@@ -2375,21 +2381,6 @@ export async function getMiddlewareBundle({
edgeFunction.wasm
);
const edgeFunctionOptions: { cron?: Cron } = {};
if (config.functions) {
const sourceFile = await getSourceFilePathFromPage({
workPath: entryPath,
page: `${edgeFunction.page}.js`,
});
const opts = await getLambdaOptionsFromFunction({
sourceFile,
config,
});
edgeFunctionOptions.cron = opts.cron;
}
return {
type,
page: edgeFunction.page,
@@ -2434,7 +2425,6 @@ export async function getMiddlewareBundle({
);
return new EdgeFunction({
...edgeFunctionOptions,
deploymentTarget: 'v8-worker',
name: edgeFunction.name,
files: {
@@ -2462,6 +2452,10 @@ export async function getMiddlewareBundle({
path: `assets/${name}`,
};
}),
framework: {
slug: 'nextjs',
version: nextVersion,
},
});
})(),
routeMatchers: getRouteMatchers(edgeFunction, routesManifest),
@@ -2685,3 +2679,49 @@ function transformSourceMap(
return { ...sourcemap, sources };
}
interface LambdaGroupTypeInterface {
isApiLambda: boolean;
isPrerenders?: boolean;
}
export function getOperationType({
group,
prerenderManifest,
pageFileName,
}: {
group?: LambdaGroupTypeInterface;
prerenderManifest?: NextPrerenderedRoutes;
pageFileName?: string;
}) {
if (group?.isApiLambda || isApiPage(pageFileName)) {
return 'API';
}
if (group?.isPrerenders) {
return 'ISR';
}
if (pageFileName && prerenderManifest) {
const { blockingFallbackRoutes = {}, fallbackRoutes = {} } =
prerenderManifest;
if (
pageFileName in blockingFallbackRoutes ||
pageFileName in fallbackRoutes
) {
return 'ISR';
}
}
return 'SSR';
}
export function isApiPage(page: string | undefined) {
if (!page) {
return false;
}
return page
.replace(/\\/g, '/')
.match(/(serverless|server)\/pages\/api(\/|\.js$)/);
}

View File

@@ -134,41 +134,70 @@ it('should build using server build', async () => {
expect(output['index'].allowQuery).toBe(undefined);
expect(output['index'].memory).toBe(512);
expect(output['index'].maxDuration).toBe(5);
expect(output['index'].operationType).toBe('SSR');
expect(output['another'].type).toBe('Lambda');
expect(output['another'].memory).toBe(512);
expect(output['another'].maxDuration).toBe(5);
expect(output['another'].allowQuery).toBe(undefined);
expect(output['another'].operationType).toBe('SSR');
expect(output['dynamic/[slug]'].type).toBe('Lambda');
expect(output['dynamic/[slug]'].memory).toBe(undefined);
expect(output['dynamic/[slug]'].maxDuration).toBe(5);
expect(output['dynamic/[slug]'].operationType).toBe('SSR');
expect(output['fallback/[slug]'].type).toBe('Prerender');
expect(output['fallback/[slug]'].allowQuery).toEqual(['slug']);
expect(output['fallback/[slug]'].lambda.operationType).toBe('ISR');
expect(output['_next/data/testing-build-id/fallback/[slug].json'].type).toBe(
'Prerender'
);
expect(
output['_next/data/testing-build-id/fallback/[slug].json'].allowQuery
).toEqual(['slug']);
expect(
output['_next/data/testing-build-id/fallback/[slug].json'].lambda
.operationType
).toBe('ISR');
expect(output['fallback/first'].type).toBe('Prerender');
expect(output['fallback/first'].allowQuery).toEqual([]);
expect(output['fallback/first'].lambda.operationType).toBe('ISR');
expect(output['_next/data/testing-build-id/fallback/first.json'].type).toBe(
'Prerender'
);
expect(
output['_next/data/testing-build-id/fallback/first.json'].allowQuery
).toEqual([]);
expect(
output['_next/data/testing-build-id/fallback/first.json'].lambda
.operationType
).toBe('ISR');
expect(output['api'].type).toBe('Lambda');
expect(output['api'].allowQuery).toBe(undefined);
expect(output['api'].memory).toBe(128);
expect(output['api'].maxDuration).toBe(5);
expect(output['api'].operationType).toBe('API');
expect(output['api/another'].type).toBe('Lambda');
expect(output['api/another'].allowQuery).toBe(undefined);
expect(output['api/another'].operationType).toBe('API');
expect(output['api/blog/[slug]'].type).toBe('Lambda');
expect(output['api/blog/[slug]'].allowQuery).toBe(undefined);
expect(output['api/blog/[slug]'].operationType).toBe('API');
expect(output['static'].type).toBe('FileFsRef');
expect(output['static'].allowQuery).toBe(undefined);
expect(output['static'].operationType).toBe(undefined);
expect(output['ssg'].type).toBe('Prerender');
expect(output['ssg'].allowQuery).toEqual([]);
expect(output['ssg'].lambda.operationType).toBe('ISR');
expect(output['index'] === output['another']).toBe(true);
expect(output['dynamic/[slug]'] !== output['fallback/[slug]'].lambda).toBe(

View File

@@ -1,44 +0,0 @@
const path = require('node:path');
const fs = require('fs-extra');
const { build } = require('../../dist');
function getFixture(name) {
return path.join(__dirname, 'fixtures', name);
}
const initialCorepackValue = process.env.COREPACK_ENABLE_STRICT;
beforeEach(() => {
process.env.COREPACK_ENABLE_STRICT = '0';
});
afterEach(() => {
process.env.COREPACK_ENABLE_STRICT = initialCorepackValue;
});
it('should include cron property from config', async () => {
const cwd = getFixture('03-with-api-routes');
await fs.remove(path.join(cwd, '.next'));
const result = await build({
workPath: cwd,
repoRootPath: cwd,
entrypoint: 'package.json',
config: {
functions: {
'pages/api/edge.js': {
cron: '* * * * *',
},
'pages/api/serverless.js': {
cron: '* * * * *',
},
},
},
meta: {
skipDownload: true,
},
});
expect(result.output['api/serverless']).toHaveProperty('cron', '* * * * *');
expect(result.output['api/edge']).toHaveProperty('cron', '* * * * *');
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node",
"version": "2.9.5",
"version": "2.9.6",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -31,9 +31,9 @@
"dependencies": {
"@edge-runtime/vm": "2.0.0",
"@types/node": "14.18.33",
"@vercel/build-utils": "6.2.4",
"@vercel/build-utils": "6.3.0",
"@vercel/node-bridge": "3.1.11",
"@vercel/static-config": "2.0.12",
"@vercel/static-config": "2.0.13",
"edge-runtime": "2.0.0",
"esbuild": "0.14.47",
"exit-hook": "2.2.1",

View File

@@ -424,8 +424,6 @@ export const build: BuildV3 = async ({
isEdgeFunction = isEdgeRuntime(staticConfig.runtime);
}
const cron = staticConfig?.cron;
debug('Tracing input files...');
const traceTime = Date.now();
const { preparedFiles, shouldAddSourcemapSupport } = await compile(
@@ -475,7 +473,6 @@ export const build: BuildV3 = async ({
// TODO: remove - these two properties should not be required
name: outputPath,
deploymentTarget: 'v8-worker',
cron,
});
} else {
// "nodejs" runtime is the default
@@ -494,7 +491,6 @@ export const build: BuildV3 = async ({
shouldAddSourcemapSupport,
awsLambdaHandler,
experimentalResponseStreaming,
cron,
});
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/python",
"version": "3.1.48",
"version": "3.1.49",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -23,7 +23,7 @@
"@types/execa": "^0.9.0",
"@types/jest": "27.4.1",
"@types/node": "14.18.33",
"@vercel/build-utils": "6.2.4",
"@vercel/build-utils": "6.3.0",
"@vercel/ncc": "0.24.0",
"execa": "^1.0.0",
"typescript": "4.3.4"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/redwood",
"version": "1.1.4",
"version": "1.1.5",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://vercel.com/docs",
@@ -27,7 +27,7 @@
"@types/aws-lambda": "8.10.19",
"@types/node": "14.18.33",
"@types/semver": "6.0.0",
"@vercel/build-utils": "6.2.4",
"@vercel/build-utils": "6.3.0",
"execa": "3.2.0",
"fs-extra": "11.1.0",
"typescript": "4.3.4"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/remix",
"version": "1.3.1",
"version": "1.3.4",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -22,14 +22,14 @@
"dependencies": {
"@remix-run/dev": "1.12.0",
"@vercel/nft": "0.22.5",
"@vercel/static-config": "2.0.12",
"@vercel/static-config": "2.0.13",
"path-to-regexp": "6.2.1",
"ts-morph": "12.0.0"
},
"devDependencies": {
"@types/jest": "27.5.1",
"@types/node": "14.18.33",
"@vercel/build-utils": "6.2.4",
"@vercel/build-utils": "6.3.0",
"typescript": "4.9.4"
}
}

View File

@@ -4,7 +4,11 @@ import {
Headers as NodeHeaders,
Request as NodeRequest,
writeReadableStreamToWritable,
installGlobals,
} from '@remix-run/node';
installGlobals();
import build from './index.js';
const handleRequest = createRemixRequestHandler(build, process.env.NODE_ENV);

View File

@@ -266,7 +266,9 @@ module.exports = config;`;
// If this is a dynamic route then add a Vercel route
const keys: Key[] = [];
// Replace "/*" at the end to handle "splat routes"
const rePath = `/${path.replace(/\/\*$/, '/:params+')}`;
const splatPath = '/:params+';
const rePath =
path === '*' ? splatPath : `/${path.replace(/\/\*$/, splatPath)}`;
const re = pathToRegexp(rePath, keys);
if (keys.length > 0) {
routes.push({

View File

@@ -0,0 +1,13 @@
import { json } from '@remix-run/server-runtime';
import { useLoaderData } from '@remix-run/react';
import type { LoaderArgs } from '@remix-run/server-runtime';
export const loader = ({ request }: LoaderArgs) => {
const instanceOfRequest = request instanceof Request;
return json({ instanceOfRequest });
};
export default function InstanceOf() {
const data = useLoaderData<typeof loader>();
return <div>{`InstanceOfRequest: ${data.instanceOfRequest}`}</div>;
}

View File

@@ -16,6 +16,7 @@
{ "path": "/nested", "mustContain": "Nested index page" },
{ "path": "/nested/another", "mustContain": "Nested another page" },
{ "path": "/nested/index", "mustContain": "Not Found" },
{ "path": "/asdf", "mustContain": "Not Found" }
{ "path": "/asdf", "mustContain": "Not Found" },
{ "path": "/instanceof", "mustContain": "InstanceOfRequest: true" }
]
}

View File

@@ -0,0 +1,6 @@
import { useParams } from "@remix-run/react";
export default function CatchAll() {
const params = useParams();
return <div>{params['*']}</div>;
}

View File

@@ -15,7 +15,7 @@
{ "path": "/b", "mustContain": "B page" },
{ "path": "/nested", "mustContain": "Nested index page" },
{ "path": "/nested/another", "mustContain": "Nested another page" },
{ "path": "/nested/index", "mustContain": "Not Found" },
{ "path": "/asdf", "mustContain": "Not Found" }
{ "path": "/nested/index", "mustContain": "<div>nested/index</div>" },
{ "path": "/asdf", "mustContain": "<div>asdf</div>" }
]
}

View File

@@ -1,7 +1,7 @@
{
"name": "@vercel/ruby",
"author": "Nathan Cahill <nathan@nathancahill.com>",
"version": "1.3.64",
"version": "1.3.66",
"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": "6.2.4",
"@vercel/build-utils": "6.3.0",
"@vercel/ncc": "0.24.0",
"execa": "2.0.4",
"fs-extra": "^7.0.1",

View File

@@ -1,5 +1,8 @@
{
"version": 2,
"builds": [{ "src": "index.rb", "use": "@vercel/ruby" }],
"probes": [{ "path": "/", "mustContain": "gem:RANDOMNESS_PLACEHOLDER" }]
"probes": [
{ "path": "/", "mustContain": "gem:RANDOMNESS_PLACEHOLDER" },
{ "path": "/", "method": "HEAD", "status": 200 }
]
}

View File

@@ -73,7 +73,7 @@ def webrick_handler(httpMethod, path, body, headers)
{
:statusCode => res.code.to_i,
:headers => res_headers,
:body => res.body,
:body => res.body.nil? ? "" : res.body,
}
end

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/static-build",
"version": "1.3.8",
"version": "1.3.10",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/build-step",
@@ -30,7 +30,7 @@
},
"dependencies": {
"@vercel/gatsby-plugin-vercel-analytics": "1.0.7",
"@vercel/gatsby-plugin-vercel-builder": "1.1.6"
"@vercel/gatsby-plugin-vercel-builder": "1.1.7"
},
"devDependencies": {
"@types/aws-lambda": "8.10.64",
@@ -42,12 +42,12 @@
"@types/node-fetch": "2.5.4",
"@types/promise-timeout": "1.3.0",
"@types/semver": "7.3.13",
"@vercel/build-utils": "6.2.4",
"@vercel/build-utils": "6.3.0",
"@vercel/frameworks": "1.3.1",
"@vercel/fs-detectors": "3.7.13",
"@vercel/fs-detectors": "3.8.0",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.1.9",
"@vercel/static-config": "2.0.12",
"@vercel/static-config": "2.0.13",
"execa": "3.2.0",
"fs-extra": "10.0.0",
"get-port": "5.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/static-config",
"version": "2.0.12",
"version": "2.0.13",
"license": "MIT",
"main": "./dist/index",
"repository": {

View File

@@ -14,7 +14,6 @@ export const BaseFunctionConfigSchema = {
type: 'object',
properties: {
runtime: { type: 'string' },
cron: { type: 'string' },
memory: { type: 'number' },
maxDuration: { type: 'number' },
regions: {

62
pnpm-lock.yaml generated
View File

@@ -204,23 +204,23 @@ importers:
'@types/which': 1.3.2
'@types/write-json-file': 2.2.1
'@types/yauzl-promise': 2.1.0
'@vercel/build-utils': 6.2.4
'@vercel/client': 12.3.10
'@vercel/build-utils': 6.3.0
'@vercel/client': 12.4.0
'@vercel/error-utils': 1.0.8
'@vercel/frameworks': 1.3.1
'@vercel/fs-detectors': 3.7.13
'@vercel/fs-detectors': 3.8.0
'@vercel/fun': 1.0.4
'@vercel/go': 2.3.6
'@vercel/hydrogen': 0.0.52
'@vercel/go': 2.3.7
'@vercel/hydrogen': 0.0.53
'@vercel/ncc': 0.24.0
'@vercel/next': 3.4.5
'@vercel/node': 2.9.5
'@vercel/python': 3.1.48
'@vercel/redwood': 1.1.4
'@vercel/remix': 1.3.1
'@vercel/next': 3.5.0
'@vercel/node': 2.9.6
'@vercel/python': 3.1.49
'@vercel/redwood': 1.1.5
'@vercel/remix': 1.3.4
'@vercel/routing-utils': 2.1.9
'@vercel/ruby': 1.3.64
'@vercel/static-build': 1.3.8
'@vercel/ruby': 1.3.66
'@vercel/static-build': 1.3.10
'@zeit/source-map-support': 0.6.2
ajv: 6.12.2
alpha-sort: 2.0.1
@@ -446,7 +446,7 @@ importers:
'@types/node-fetch': 2.5.4
'@types/recursive-readdir': 2.2.0
'@types/tar-fs': 1.16.1
'@vercel/build-utils': 6.2.4
'@vercel/build-utils': 6.3.0
'@vercel/routing-utils': 2.1.9
'@zeit/fetch': 5.2.0
async-retry: 1.2.3
@@ -551,7 +551,7 @@ importers:
'@types/minimatch': 3.0.5
'@types/node': 14.18.33
'@types/semver': 7.3.10
'@vercel/build-utils': 6.2.4
'@vercel/build-utils': 6.3.0
'@vercel/error-utils': 1.0.8
'@vercel/frameworks': 1.3.1
'@vercel/routing-utils': 2.1.9
@@ -599,8 +599,8 @@ importers:
'@types/fs-extra': 11.0.1
'@types/node': 14.18.33
'@types/react': 18.0.26
'@vercel/build-utils': 6.2.4
'@vercel/node': 2.9.5
'@vercel/build-utils': 6.3.0
'@vercel/node': 2.9.6
'@vercel/routing-utils': 2.1.9
ajv: 8.12.0
esbuild: 0.14.47
@@ -634,7 +634,7 @@ importers:
'@types/node': 14.18.33
'@types/node-fetch': ^2.3.0
'@types/tar': ^4.0.0
'@vercel/build-utils': 6.2.4
'@vercel/build-utils': 6.3.0
'@vercel/ncc': 0.24.0
async-retry: 1.3.1
execa: ^1.0.0
@@ -666,8 +666,8 @@ importers:
specifiers:
'@types/jest': 27.5.1
'@types/node': 14.18.33
'@vercel/build-utils': 6.2.4
'@vercel/static-config': 2.0.12
'@vercel/build-utils': 6.3.0
'@vercel/static-config': 2.0.13
execa: 3.2.0
fs-extra: 11.1.0
ts-morph: 12.0.0
@@ -697,7 +697,7 @@ importers:
'@types/semver': 6.0.0
'@types/text-table': 0.2.1
'@types/webpack-sources': 3.2.0
'@vercel/build-utils': 6.2.4
'@vercel/build-utils': 6.3.0
'@vercel/nft': 0.22.5
'@vercel/routing-utils': 2.1.9
async-sema: 3.0.1
@@ -776,11 +776,11 @@ importers:
'@types/node': 14.18.33
'@types/node-fetch': ^2.6.1
'@types/test-listen': 1.1.0
'@vercel/build-utils': 6.2.4
'@vercel/build-utils': 6.3.0
'@vercel/ncc': 0.24.0
'@vercel/nft': 0.22.5
'@vercel/node-bridge': 3.1.11
'@vercel/static-config': 2.0.12
'@vercel/static-config': 2.0.13
content-type: 1.0.4
cookie: 0.4.0
edge-runtime: 2.0.0
@@ -860,7 +860,7 @@ importers:
'@types/execa': ^0.9.0
'@types/jest': 27.4.1
'@types/node': 14.18.33
'@vercel/build-utils': 6.2.4
'@vercel/build-utils': 6.3.0
'@vercel/ncc': 0.24.0
execa: ^1.0.0
typescript: 4.3.4
@@ -878,7 +878,7 @@ importers:
'@types/aws-lambda': 8.10.19
'@types/node': 14.18.33
'@types/semver': 6.0.0
'@vercel/build-utils': 6.2.4
'@vercel/build-utils': 6.3.0
'@vercel/nft': 0.22.5
'@vercel/routing-utils': 2.1.9
execa: 3.2.0
@@ -903,9 +903,9 @@ importers:
'@remix-run/dev': 1.12.0
'@types/jest': 27.5.1
'@types/node': 14.18.33
'@vercel/build-utils': 6.2.4
'@vercel/build-utils': 6.3.0
'@vercel/nft': 0.22.5
'@vercel/static-config': 2.0.12
'@vercel/static-config': 2.0.13
path-to-regexp: 6.2.1
ts-morph: 12.0.0
typescript: 4.9.4
@@ -960,7 +960,7 @@ importers:
specifiers:
'@types/fs-extra': 8.0.0
'@types/semver': 6.0.0
'@vercel/build-utils': 6.2.4
'@vercel/build-utils': 6.3.0
'@vercel/ncc': 0.24.0
execa: 2.0.4
fs-extra: ^7.0.1
@@ -987,14 +987,14 @@ importers:
'@types/node-fetch': 2.5.4
'@types/promise-timeout': 1.3.0
'@types/semver': 7.3.13
'@vercel/build-utils': 6.2.4
'@vercel/build-utils': 6.3.0
'@vercel/frameworks': 1.3.1
'@vercel/fs-detectors': 3.7.13
'@vercel/fs-detectors': 3.8.0
'@vercel/gatsby-plugin-vercel-analytics': 1.0.7
'@vercel/gatsby-plugin-vercel-builder': 1.1.6
'@vercel/gatsby-plugin-vercel-builder': 1.1.7
'@vercel/ncc': 0.24.0
'@vercel/routing-utils': 2.1.9
'@vercel/static-config': 2.0.12
'@vercel/static-config': 2.0.13
execa: 3.2.0
fs-extra: 10.0.0
get-port: 5.0.0

View File

@@ -384,7 +384,7 @@ async function fetchDeploymentUrl(url, opts) {
for (let i = 0; i < 50; i += 1) {
const resp = await fetch(url, opts);
const text = await resp.text();
if (text && !text.includes('Join Free')) {
if (typeof text !== 'undefined' && !text.includes('Join Free')) {
return { resp, text };
}