@vercel/build-utils 13.31.1 → 13.32.0

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,46 @@
1
1
  # @vercel/build-utils
2
2
 
3
+ ## 13.32.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 9fb2976: Add `services` as the canonical multi-service project configuration and keep `experimentalServicesV2` as a deprecated backwards-compatible alias.
8
+
9
+ ### Patch Changes
10
+
11
+ - 186014d: Add an experimental container service runtime. A service with
12
+ `runtime: "container"` either builds its `Dockerfile`/`Containerfile` and pushes
13
+ the resulting OCI image to the Vercel Container Registry (VCR), or passes a
14
+ prebuilt image reference through as build output.
15
+
16
+ - **`@vercel/container`** (new builder): authenticates to VCR with the project's
17
+ `VERCEL_OIDC_TOKEN`, ensures the repository exists, builds and pushes the
18
+ image, and emits a digest-pinned reference in `handler` (container functions
19
+ are `type: "Lambda"` with `runtime: "container"`; the platform surfaces
20
+ `handler` as the image downstream). Uses `docker` on developer machines and
21
+ `buildah` (daemonless) in the Vercel build container behind a shared
22
+ `ContainerEngine` interface. Supports `vc dev` via `startDevServer` (local
23
+ build/run, env parity, log forwarding) and `prepareCache` for buildah layer
24
+ reuse between builds. Build flow is instrumented with tracing spans
25
+ (non-secret diagnostics) and debug logging gated on `BUILDER_DEBUG`.
26
+ - **`@vercel/build-utils`**: add the `ContainerImage` build-output type.
27
+ - **`@vercel/fs-detectors`**: resolve container services from `vercel.json`
28
+ (the `services` config and its deprecated `experimentalServices` /
29
+ `experimentalServicesV2` aliases). A `Dockerfile`, `Containerfile`, or
30
+ `*.dockerfile` entrypoint triggers a build; any other entrypoint is treated as
31
+ a prebuilt OCI image reference.
32
+ - **`vercel`**: wire container output into `vercel build` result writing and
33
+ config validation.
34
+
35
+ Buildah specifics in the build container: host networking for `RUN` steps,
36
+ native `overlay` storage on the XFS `/vercel` volume (deferring to the image's
37
+ `storage.conf`), zstd push compression, and registry credentials read from the
38
+ provisioned auth file when present. Several knobs are available for debugging:
39
+ `VERCEL_CONTAINER_ENGINE`, `VERCEL_VCR_STRICT_STORAGE`,
40
+ `VERCEL_VCR_DISABLE_LAYER_CACHE`, and `VERCEL_VCR_FORCE_LOGIN`.
41
+
42
+ - cb0988f: Set the Node.js 20 discontinuation date to October 1, 2026.
43
+
3
44
  ## 13.31.1
4
45
 
5
46
  ### Patch Changes
@@ -0,0 +1,22 @@
1
+ import type { Env, Files } from './types';
2
+ export interface ContainerImageConfig {
3
+ /**
4
+ * The OCI image reference (e.g. `vcr.vercel.com/team/project/svc@sha256:...`).
5
+ * Carried in `handler` per the build-output contract; api-builds surfaces it
6
+ * as `image` downstream (see vercel/api#76729).
7
+ */
8
+ handler: string;
9
+ runtime: 'container';
10
+ command?: string[];
11
+ environment?: Env;
12
+ }
13
+ export declare class ContainerImage {
14
+ type: 'ContainerImage';
15
+ files: Files;
16
+ /** The OCI image reference, carried in `handler` (see ContainerImageConfig). */
17
+ handler: string;
18
+ runtime: 'container';
19
+ command?: string[];
20
+ environment: Env;
21
+ constructor(params: Omit<ContainerImage, 'type'>);
22
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var container_image_exports = {};
20
+ __export(container_image_exports, {
21
+ ContainerImage: () => ContainerImage
22
+ });
23
+ module.exports = __toCommonJS(container_image_exports);
24
+ class ContainerImage {
25
+ constructor(params) {
26
+ this.type = "ContainerImage";
27
+ this.files = params.files;
28
+ this.handler = params.handler;
29
+ this.runtime = params.runtime;
30
+ this.command = params.command;
31
+ this.environment = params.environment;
32
+ }
33
+ }
34
+ // Annotate the CommonJS export names for ESM import in node:
35
+ 0 && (module.exports = {
36
+ ContainerImage
37
+ });
@@ -1,6 +1,7 @@
1
1
  import type { Lambda } from '../lambda';
2
2
  import type { NodejsLambda } from '../nodejs-lambda';
3
3
  import type { EdgeFunction } from '../edge-function';
4
+ import type { ContainerImage } from '../container-image';
4
5
  import type FileFsRef from '../file-fs-ref';
5
6
  import type { Prerender } from '../prerender';
6
7
  /**
@@ -25,4 +26,7 @@ export type SerializedPrerender = Properties<Omit<Prerender, 'lambda' | 'fallbac
25
26
  fallback: SerializedFileFsRef | null;
26
27
  };
27
28
  export type SerializedEdgeFunction = Properties<Omit<EdgeFunction, 'name' | 'files' | 'deploymentTarget'>> & FilesMapProp;
29
+ export type SerializedContainerImage = Omit<Properties<ContainerImage>, 'files' | 'type'> & {
30
+ type?: 'ContainerImage';
31
+ } & FilesMapProp;
28
32
  export {};
@@ -58,7 +58,8 @@ const NODE_VERSIONS = [
58
58
  new import_types.NodeVersion({
59
59
  major: 20,
60
60
  range: "20.x",
61
- runtime: "nodejs20.x"
61
+ runtime: "nodejs20.x",
62
+ discontinueDate: /* @__PURE__ */ new Date("2026-10-01")
62
63
  }),
63
64
  new import_types.NodeVersion({
64
65
  major: 18,
package/dist/index.d.ts CHANGED
@@ -22,6 +22,8 @@ import { validateNpmrc } from './validate-npmrc';
22
22
  export type { NodejsLambdaOptions };
23
23
  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, NpmInstallOutput, runBundleInstall, runPipInstall, PipInstallResult, runShellScript, runCustomInstallCommand, resetCustomInstallCommandSet, getEnvForPackageManager, getNodeVersion, getPathForPackageManager, getLatestNodeVersion, getDiscontinuedNodeVersions, getSpawnOptions, getPlatformEnv, getPrefixedEnvVars, getServiceUrlEnvVars, getExperimentalServiceUrlEnvVars, streamToBuffer, streamToBufferChunks, debug, isSymbolicLink, isDirectory, isExternalSymlink, isExternalSymlinkTarget, getSymlinkTarget, getLambdaOptionsFromFunction, sanitizeConsumerName, scanParentDirs, findPackageJson, getIgnoreFilter, cloneEnv, hardLinkDir, traverseUpDirectories, validateNpmrc, type CliType, };
24
24
  export { EdgeFunction } from './edge-function';
25
+ export { ContainerImage } from './container-image';
26
+ export type { ContainerImageConfig } from './container-image';
25
27
  export { readConfigFile, getPackageJson } from './fs/read-config-file';
26
28
  export { normalizePath } from './fs/normalize-path';
27
29
  export { getOsRelease, getProvidedRuntime } from './os';
package/dist/index.js CHANGED
@@ -34510,6 +34510,7 @@ __export(src_exports, {
34510
34510
  BUILDER_INSTALLER_STEP: () => BUILDER_INSTALLER_STEP,
34511
34511
  BUILDER_PRE_DEPLOY_STEP: () => BUILDER_PRE_DEPLOY_STEP,
34512
34512
  BunVersion: () => BunVersion,
34513
+ ContainerImage: () => ContainerImage,
34513
34514
  DEFAULT_MAX_DURATION_LIMIT: () => DEFAULT_MAX_DURATION_LIMIT,
34514
34515
  ENV_WRAPPER_SUPPORTED_FAMILIES: () => ENV_WRAPPER_SUPPORTED_FAMILIES,
34515
34516
  EdgeFunction: () => EdgeFunction,
@@ -35953,7 +35954,8 @@ var NODE_VERSIONS = [
35953
35954
  new NodeVersion({
35954
35955
  major: 20,
35955
35956
  range: "20.x",
35956
- runtime: "nodejs20.x"
35957
+ runtime: "nodejs20.x",
35958
+ discontinueDate: /* @__PURE__ */ new Date("2026-10-01")
35957
35959
  }),
35958
35960
  new NodeVersion({
35959
35961
  major: 18,
@@ -38259,6 +38261,18 @@ var EdgeFunction = class {
38259
38261
  }
38260
38262
  };
38261
38263
 
38264
+ // src/container-image.ts
38265
+ var ContainerImage = class {
38266
+ constructor(params) {
38267
+ this.type = "ContainerImage";
38268
+ this.files = params.files;
38269
+ this.handler = params.handler;
38270
+ this.runtime = params.runtime;
38271
+ this.command = params.command;
38272
+ this.environment = params.environment;
38273
+ }
38274
+ };
38275
+
38262
38276
  // src/os.ts
38263
38277
  var import_fs_extra9 = __toESM(require_lib());
38264
38278
  var import_error_utils2 = __toESM(require_dist());
@@ -40796,6 +40810,7 @@ function getExtendedPayload({
40796
40810
  BUILDER_INSTALLER_STEP,
40797
40811
  BUILDER_PRE_DEPLOY_STEP,
40798
40812
  BunVersion,
40813
+ ContainerImage,
40799
40814
  DEFAULT_MAX_DURATION_LIMIT,
40800
40815
  ENV_WRAPPER_SUPPORTED_FAMILIES,
40801
40816
  EdgeFunction,
package/dist/types.d.ts CHANGED
@@ -4,6 +4,7 @@ import type FileBlob from './file-blob';
4
4
  import type { Lambda, LambdaArchitecture } from './lambda';
5
5
  import type { Prerender } from './prerender';
6
6
  import type { EdgeFunction } from './edge-function';
7
+ import type { ContainerImage } from './container-image';
7
8
  import type { Span } from './trace';
8
9
  import type { HasField, Route, Rewrite, Redirect, Header } from '@vercel/routing-utils';
9
10
  export interface Env {
@@ -546,6 +547,8 @@ export interface ExperimentalServiceV2 {
546
547
  runtime?: string;
547
548
  /** Resolved entrypoint, relative to the service root. */
548
549
  entrypoint?: string;
550
+ /** Command override for `runtime: "container"` services. */
551
+ command?: string[];
549
552
  /** Builder selected by the resolver. */
550
553
  builder: Builder;
551
554
  installCommand?: string;
@@ -610,7 +613,7 @@ export interface BuildResultV2Typical {
610
613
  routes?: any[];
611
614
  images?: Images;
612
615
  output: {
613
- [key: string]: File | Lambda | Prerender | EdgeFunction;
616
+ [key: string]: File | Lambda | Prerender | EdgeFunction | ContainerImage;
614
617
  };
615
618
  wildcard?: Array<{
616
619
  domain: string;
@@ -744,7 +747,7 @@ export interface TriggerEvent extends TriggerEventBase {
744
747
  /** Name of the consumer group for this trigger (always present in processed output) */
745
748
  consumer: string;
746
749
  }
747
- export type ServiceRuntime = 'node' | 'python' | 'go' | 'rust' | 'ruby';
750
+ export type ServiceRuntime = 'node' | 'python' | 'go' | 'rust' | 'ruby' | 'container';
748
751
  export type ServiceType = 'web' | 'cron' | 'worker' | 'job';
749
752
  export interface ExperimentalServiceMount {
750
753
  /** URL path prefix where the service is mounted. */
@@ -780,8 +783,10 @@ export interface ExperimentalServiceConfig {
780
783
  framework?: string;
781
784
  /** Builder to use, e.g. @vercel/node, @vercel/python */
782
785
  builder?: string;
783
- /** Specific lambda runtime to use, e.g. nodejs24.x, python3.14 */
786
+ /** Specific lambda runtime to use, e.g. nodejs24.x, python3.14, container */
784
787
  runtime?: string;
788
+ /** Optional command override for container image services. */
789
+ command?: string | string[];
785
790
  workspace?: string;
786
791
  buildCommand?: string;
787
792
  installCommand?: string;
@@ -817,22 +822,18 @@ export type ExperimentalServices = Record<string, ExperimentalServiceConfig>;
817
822
  * }
818
823
  */
819
824
  export type ExperimentalServiceGroups = Record<string, string[]>;
820
- export interface ExperimentalServiceV2Binding {
825
+ export interface ServiceBinding {
821
826
  /** Must be `"service"` for Service-to-Service HTTP bindings. */
822
827
  type: 'service';
823
- /** Target service name from `experimentalServicesV2`. */
828
+ /** Target service name from `services`. */
824
829
  service: string;
825
830
  /** Generated value shape, must be `"url"`. */
826
831
  format: 'url';
827
832
  /** Environment variable name that will store the generated value */
828
833
  env: string;
829
834
  }
830
- /**
831
- * Configuration for a service in `experimentalServicesV2` in `vercel.json`.
832
- *
833
- * @experimental This feature is experimental and may change.
834
- */
835
- export interface ExperimentalServiceV2Config {
835
+ /** Configuration for a service in `vercel.json#services`. */
836
+ export interface ServiceConfig {
836
837
  /** Path to the service root, relative to `vercel.json`. */
837
838
  root: string;
838
839
  /** Framework for this service. */
@@ -842,15 +843,19 @@ export interface ExperimentalServiceV2Config {
842
843
  /**
843
844
  * Service entrypoint, relative to the service root directory.
844
845
  * Can be a file path or a module specification (for Python).
846
+ * For `runtime: "container"`, a Dockerfile path (built & pushed) or a
847
+ * prebuilt OCI image reference.
845
848
  */
846
849
  entrypoint?: string;
850
+ /** Command override for `runtime: "container"` services. */
851
+ command?: string | string[];
847
852
  installCommand?: string;
848
853
  buildCommand?: string;
849
854
  devCommand?: string;
850
855
  ignoreCommand?: string;
851
856
  outputDirectory?: string;
852
857
  /** Caller-side bindings that grant this service access to another service. */
853
- bindings?: ExperimentalServiceV2Binding[];
858
+ bindings?: ServiceBinding[];
854
859
  /** Function configuration scoped to this service root. */
855
860
  functions?: BuilderFunctions;
856
861
  headers?: Header[];
@@ -860,12 +865,14 @@ export interface ExperimentalServiceV2Config {
860
865
  cleanUrls?: boolean;
861
866
  trailingSlash?: boolean;
862
867
  }
863
- /**
864
- * Map of service name to service configuration for `experimentalServicesV2`.
865
- *
866
- * @experimental This feature is experimental and may change.
867
- */
868
- export type ExperimentalServicesV2 = Record<string, ExperimentalServiceV2Config>;
868
+ /** Map of service name to service configuration for `vercel.json#services`. */
869
+ export type Services = Record<string, ServiceConfig>;
870
+ /** @deprecated Use `ServiceBinding` instead. */
871
+ export type ExperimentalServiceV2Binding = ServiceBinding;
872
+ /** @deprecated Use `ServiceConfig` instead. */
873
+ export type ExperimentalServiceV2Config = ServiceConfig;
874
+ /** @deprecated Use `Services` instead. */
875
+ export type ExperimentalServicesV2 = Services;
869
876
  /**
870
877
  * Result of a runtime builder's normalized entrypoint detection.
871
878
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/build-utils",
3
- "version": "13.31.1",
3
+ "version": "13.32.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.js",
@@ -55,8 +55,8 @@
55
55
  "vitest": "2.0.1",
56
56
  "typescript": "4.9.5",
57
57
  "yazl": "2.5.1",
58
- "@vercel/error-utils": "2.2.0",
59
- "@vercel/routing-utils": "6.3.1"
58
+ "@vercel/routing-utils": "6.3.1",
59
+ "@vercel/error-utils": "2.2.0"
60
60
  },
61
61
  "scripts": {
62
62
  "build": "node build.mjs",