@vercel/build-utils 13.2.13 → 13.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @vercel/build-utils
2
2
 
3
+ ## 13.2.15
4
+
5
+ ### Patch Changes
6
+
7
+ - Optimize `getAvailableNodeVersions` to skip discontinued versions and use non-throwing `statSync` ([#14686](https://github.com/vercel/vercel/pull/14686))
8
+
9
+ ## 13.2.14
10
+
11
+ ### Patch Changes
12
+
13
+ - Add `experimentalServices` to `vercel.json` ([#14612](https://github.com/vercel/vercel/pull/14612))
14
+
3
15
  ## 13.2.13
4
16
 
5
17
  ### Patch Changes
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import { FileBase } from './types';
3
4
  interface FileBlobOptions {
4
5
  mode?: number;
@@ -111,14 +111,11 @@ function getOptions() {
111
111
  return NODE_VERSIONS;
112
112
  }
113
113
  function isNodeVersionAvailable(version) {
114
- try {
115
- return (0, import_fs.statSync)(`/node${version.major}`).isDirectory();
116
- } catch {
117
- }
118
- return false;
114
+ const stat = (0, import_fs.statSync)(`/node${version.major}`, { throwIfNoEntry: false });
115
+ return stat?.isDirectory() ?? false;
119
116
  }
120
117
  function getAvailableNodeVersions() {
121
- return getOptions().filter(isNodeVersionAvailable).map((n) => n.major);
118
+ return getOptions().filter((v) => v.major >= 18).filter(isNodeVersionAvailable).map((n) => n.major);
122
119
  }
123
120
  function getHint(isAuto = false, availableVersions) {
124
121
  const { major, range } = getLatestNodeVersion(availableVersions);
@@ -153,7 +150,7 @@ async function getSupportedNodeVersion(engineRange, isAuto = false, availableVer
153
150
  throw new import_errors.NowBuildError({
154
151
  code: "BUILD_UTILS_NODE_VERSION_INVALID",
155
152
  link: "https://vercel.link/node-version",
156
- message: `Found invalid Node.js Version: "${engineRange}". ${getHint(
153
+ message: `Found invalid or discontinued Node.js Version: "${engineRange}". ${getHint(
157
154
  isAuto,
158
155
  availableVersions
159
156
  )}`
@@ -211,7 +211,17 @@ export declare function runCustomInstallCommand({ destPath, installCommand, spaw
211
211
  }): Promise<boolean>;
212
212
  export declare function runPackageJsonScript(destPath: string, scriptNames: string | Iterable<string>, spawnOpts?: SpawnOptions, projectCreatedAt?: number): Promise<boolean>;
213
213
  export declare function runBundleInstall(destPath: string, args?: string[], spawnOpts?: SpawnOptions, meta?: Meta): Promise<void>;
214
- export declare function runPipInstall(destPath: string, args?: string[], spawnOpts?: SpawnOptions, meta?: Meta): Promise<void>;
214
+ export type PipInstallResult = {
215
+ installed: false;
216
+ } | {
217
+ installed: true;
218
+ /**
219
+ * The directory where packages were installed.
220
+ * Add this to PYTHONPATH when running Python commands.
221
+ */
222
+ targetDir: string;
223
+ };
224
+ export declare function runPipInstall(destPath: string, args?: string[], spawnOpts?: SpawnOptions, meta?: Meta): Promise<PipInstallResult>;
215
225
  export declare function getScriptName(pkg: Pick<PackageJson, 'scripts'> | null | undefined, possibleNames: Iterable<string>): string | undefined;
216
226
  /**
217
227
  * @deprecate installDependencies() is deprecated.
@@ -1073,15 +1073,21 @@ async function runBundleInstall(destPath, args = [], spawnOpts, meta) {
1073
1073
  async function runPipInstall(destPath, args = [], spawnOpts, meta) {
1074
1074
  if (meta && meta.isDev) {
1075
1075
  (0, import_debug.default)("Skipping dependency installation because dev mode is enabled");
1076
- return;
1076
+ return { installed: false };
1077
1077
  }
1078
1078
  (0, import_assert.default)(import_path.default.isAbsolute(destPath));
1079
- const opts = { ...spawnOpts, cwd: destPath, prettyCommand: "pip3 install" };
1079
+ const targetDir = import_path.default.join(destPath, ".vercel_python_packages");
1080
+ const opts = {
1081
+ ...spawnOpts,
1082
+ cwd: destPath,
1083
+ prettyCommand: "uv pip install"
1084
+ };
1080
1085
  await spawnAsync(
1081
- "pip3",
1082
- ["install", "--disable-pip-version-check", ...args],
1086
+ "uv",
1087
+ ["pip", "install", "--target", targetDir, ...args],
1083
1088
  opts
1084
1089
  );
1090
+ return { installed: true, targetDir };
1085
1091
  }
1086
1092
  function getScriptName(pkg, possibleNames) {
1087
1093
  if (pkg?.scripts) {
@@ -1,3 +1,4 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  export default function streamToBuffer(stream: NodeJS.ReadableStream): Promise<Buffer>;
3
4
  export declare function streamToBufferChunks(stream: NodeJS.ReadableStream, chunkSize?: number): Promise<Buffer[]>;
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ import download, { downloadFile, DownloadedFiles, isSymbolicLink, isDirectory }
8
8
  import getWriteableDirectory from './fs/get-writable-directory';
9
9
  import glob, { GlobOptions } from './fs/glob';
10
10
  import rename from './fs/rename';
11
- import { spawnAsync, execCommand, spawnCommand, walkParentDirs, getScriptName, installDependencies, runPackageJsonScript, runNpmInstall, runBundleInstall, runPipInstall, runShellScript, runCustomInstallCommand, resetCustomInstallCommandSet, getEnvForPackageManager, getNodeVersion, getPathForPackageManager, detectPackageManager, getSpawnOptions, getNodeBinPath, getNodeBinPaths, scanParentDirs, findPackageJson, traverseUpDirectories } from './fs/run-user-scripts';
11
+ import { spawnAsync, execCommand, spawnCommand, walkParentDirs, getScriptName, installDependencies, runPackageJsonScript, runNpmInstall, runBundleInstall, runPipInstall, runShellScript, runCustomInstallCommand, resetCustomInstallCommandSet, getEnvForPackageManager, getNodeVersion, getPathForPackageManager, detectPackageManager, getSpawnOptions, getNodeBinPath, getNodeBinPaths, scanParentDirs, findPackageJson, traverseUpDirectories, PipInstallResult } from './fs/run-user-scripts';
12
12
  import { getLatestNodeVersion, getDiscontinuedNodeVersions, getSupportedNodeVersion, isBunVersion, getSupportedBunVersion } from './fs/node-version';
13
13
  import streamToBuffer, { streamToBufferChunks } from './fs/stream-to-buffer';
14
14
  import debug from './debug';
@@ -18,7 +18,7 @@ import { getPrefixedEnvVars } from './get-prefixed-env-vars';
18
18
  import { cloneEnv } from './clone-env';
19
19
  import { hardLinkDir } from './hard-link-dir';
20
20
  import { validateNpmrc } from './validate-npmrc';
21
- export { FileBlob, FileFsRef, FileRef, Lambda, NodejsLambda, createLambda, Prerender, download, downloadFile, DownloadedFiles, getWriteableDirectory, glob, GlobOptions, rename, spawnAsync, getScriptName, installDependencies, runPackageJsonScript, execCommand, spawnCommand, walkParentDirs, getNodeBinPath, getNodeBinPaths, getSupportedNodeVersion, isBunVersion, getSupportedBunVersion, detectPackageManager, runNpmInstall, runBundleInstall, runPipInstall, runShellScript, runCustomInstallCommand, resetCustomInstallCommandSet, getEnvForPackageManager, getNodeVersion, getPathForPackageManager, getLatestNodeVersion, getDiscontinuedNodeVersions, getSpawnOptions, getPlatformEnv, getPrefixedEnvVars, streamToBuffer, streamToBufferChunks, debug, isSymbolicLink, isDirectory, getLambdaOptionsFromFunction, scanParentDirs, findPackageJson, getIgnoreFilter, cloneEnv, hardLinkDir, traverseUpDirectories, validateNpmrc, };
21
+ export { FileBlob, FileFsRef, FileRef, Lambda, NodejsLambda, createLambda, Prerender, download, downloadFile, DownloadedFiles, getWriteableDirectory, glob, GlobOptions, rename, spawnAsync, getScriptName, installDependencies, runPackageJsonScript, execCommand, spawnCommand, walkParentDirs, getNodeBinPath, getNodeBinPaths, getSupportedNodeVersion, isBunVersion, getSupportedBunVersion, detectPackageManager, runNpmInstall, runBundleInstall, runPipInstall, PipInstallResult, runShellScript, runCustomInstallCommand, resetCustomInstallCommandSet, getEnvForPackageManager, getNodeVersion, getPathForPackageManager, getLatestNodeVersion, getDiscontinuedNodeVersions, getSpawnOptions, getPlatformEnv, getPrefixedEnvVars, streamToBuffer, streamToBufferChunks, debug, isSymbolicLink, isDirectory, getLambdaOptionsFromFunction, scanParentDirs, findPackageJson, getIgnoreFilter, cloneEnv, hardLinkDir, traverseUpDirectories, validateNpmrc, };
22
22
  export { EdgeFunction } from './edge-function';
23
23
  export { readConfigFile, getPackageJson } from './fs/read-config-file';
24
24
  export { normalizePath } from './fs/normalize-path';
package/dist/index.js CHANGED
@@ -24940,14 +24940,11 @@ function getOptions() {
24940
24940
  return NODE_VERSIONS;
24941
24941
  }
24942
24942
  function isNodeVersionAvailable(version) {
24943
- try {
24944
- return (0, import_fs.statSync)(`/node${version.major}`).isDirectory();
24945
- } catch {
24946
- }
24947
- return false;
24943
+ const stat = (0, import_fs.statSync)(`/node${version.major}`, { throwIfNoEntry: false });
24944
+ return stat?.isDirectory() ?? false;
24948
24945
  }
24949
24946
  function getAvailableNodeVersions() {
24950
- return getOptions().filter(isNodeVersionAvailable).map((n) => n.major);
24947
+ return getOptions().filter((v) => v.major >= 18).filter(isNodeVersionAvailable).map((n) => n.major);
24951
24948
  }
24952
24949
  function getHint(isAuto = false, availableVersions) {
24953
24950
  const { major, range } = getLatestNodeVersion(availableVersions);
@@ -24982,7 +24979,7 @@ async function getSupportedNodeVersion(engineRange, isAuto = false, availableVer
24982
24979
  throw new NowBuildError({
24983
24980
  code: "BUILD_UTILS_NODE_VERSION_INVALID",
24984
24981
  link: "https://vercel.link/node-version",
24985
- message: `Found invalid Node.js Version: "${engineRange}". ${getHint(
24982
+ message: `Found invalid or discontinued Node.js Version: "${engineRange}". ${getHint(
24986
24983
  isAuto,
24987
24984
  availableVersions
24988
24985
  )}`
@@ -26114,15 +26111,21 @@ async function runBundleInstall(destPath, args = [], spawnOpts, meta) {
26114
26111
  async function runPipInstall(destPath, args = [], spawnOpts, meta) {
26115
26112
  if (meta && meta.isDev) {
26116
26113
  debug("Skipping dependency installation because dev mode is enabled");
26117
- return;
26114
+ return { installed: false };
26118
26115
  }
26119
26116
  (0, import_assert6.default)(import_path6.default.isAbsolute(destPath));
26120
- const opts = { ...spawnOpts, cwd: destPath, prettyCommand: "pip3 install" };
26117
+ const targetDir = import_path6.default.join(destPath, ".vercel_python_packages");
26118
+ const opts = {
26119
+ ...spawnOpts,
26120
+ cwd: destPath,
26121
+ prettyCommand: "uv pip install"
26122
+ };
26121
26123
  await spawnAsync(
26122
- "pip3",
26123
- ["install", "--disable-pip-version-check", ...args],
26124
+ "uv",
26125
+ ["pip", "install", "--target", targetDir, ...args],
26124
26126
  opts
26125
26127
  );
26128
+ return { installed: true, targetDir };
26126
26129
  }
26127
26130
  function getScriptName(pkg, possibleNames) {
26128
26131
  if (pkg?.scripts) {
package/dist/types.d.ts CHANGED
@@ -549,3 +549,57 @@ export interface TriggerEvent {
549
549
  */
550
550
  initialDelaySeconds?: number;
551
551
  }
552
+ export type ServiceRuntime = 'node' | 'python' | 'go' | 'rust' | 'ruby';
553
+ export type ServiceType = 'web' | 'cron' | 'worker';
554
+ /**
555
+ * Configuration for a service in vercel.json.
556
+ * @experimental This feature is experimental and may change.
557
+ */
558
+ export interface ExperimentalServiceConfig {
559
+ type?: ServiceType;
560
+ /**
561
+ * Entry file for the service, relative to the workspace directory.
562
+ * @example "src/index.ts", "main.py", "api/server.go"
563
+ */
564
+ entrypoint?: string;
565
+ /**
566
+ * Path to the directory containing the service's manifest file
567
+ * (package.json, pyproject.toml, etc.).
568
+ * Defaults to "." (project root) if not specified.
569
+ */
570
+ workspace?: string;
571
+ /** Framework to use */
572
+ framework?: string;
573
+ /** Builder to use, e.g. @vercel/node, @vercel/python */
574
+ builder?: string;
575
+ /** Specific lambda runtime to use, e.g. nodejs24.x, python3.14 */
576
+ runtime?: string;
577
+ buildCommand?: string;
578
+ installCommand?: string;
579
+ /** Lambda config */
580
+ memory?: number;
581
+ maxDuration?: number;
582
+ includeFiles?: string | string[];
583
+ excludeFiles?: string | string[];
584
+ /** URL prefix for routing */
585
+ routePrefix?: string;
586
+ /** Cron schedule expression (e.g., "0 0 * * *") */
587
+ schedule?: string;
588
+ topic?: string;
589
+ consumer?: string;
590
+ }
591
+ /**
592
+ * Map of service name to service configuration.
593
+ * @experimental This feature is experimental and may change.
594
+ */
595
+ export type ExperimentalServices = Record<string, ExperimentalServiceConfig>;
596
+ /**
597
+ * Map of service group name to array of service names belonging to that group.
598
+ * @experimental This feature is experimental and may change.
599
+ * @example
600
+ * {
601
+ * "app": ["site", "backend"],
602
+ * "admin": ["admin", "backend"]
603
+ * }
604
+ */
605
+ export type ExperimentalServiceGroups = Record<string, string[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/build-utils",
3
- "version": "13.2.13",
3
+ "version": "13.2.15",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.js",
@@ -22,7 +22,7 @@
22
22
  "@types/minimatch": "^5.1.2",
23
23
  "@types/ms": "0.7.31",
24
24
  "@types/multistream": "2.1.1",
25
- "@types/node": "14.18.33",
25
+ "@types/node": "20.11.0",
26
26
  "@types/node-fetch": "^2.1.6",
27
27
  "@types/semver": "6.0.0",
28
28
  "@types/yazl": "2.4.2",