@vercel/build-utils 13.31.1 → 13.32.1

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,52 @@
1
1
  # @vercel/build-utils
2
2
 
3
+ ## 13.32.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 8dc4702: Fix `vercel dev` for standalone Node servers, including projects without a `package.json`, and reuse the server process between requests.
8
+
9
+ ## 13.32.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 9fb2976: Add `services` as the canonical multi-service project configuration and keep `experimentalServicesV2` as a deprecated backwards-compatible alias.
14
+
15
+ ### Patch Changes
16
+
17
+ - 186014d: Add an experimental container service runtime. A service with
18
+ `runtime: "container"` either builds its `Dockerfile`/`Containerfile` and pushes
19
+ the resulting OCI image to the Vercel Container Registry (VCR), or passes a
20
+ prebuilt image reference through as build output.
21
+
22
+ - **`@vercel/container`** (new builder): authenticates to VCR with the project's
23
+ `VERCEL_OIDC_TOKEN`, ensures the repository exists, builds and pushes the
24
+ image, and emits a digest-pinned reference in `handler` (container functions
25
+ are `type: "Lambda"` with `runtime: "container"`; the platform surfaces
26
+ `handler` as the image downstream). Uses `docker` on developer machines and
27
+ `buildah` (daemonless) in the Vercel build container behind a shared
28
+ `ContainerEngine` interface. Supports `vc dev` via `startDevServer` (local
29
+ build/run, env parity, log forwarding) and `prepareCache` for buildah layer
30
+ reuse between builds. Build flow is instrumented with tracing spans
31
+ (non-secret diagnostics) and debug logging gated on `BUILDER_DEBUG`.
32
+ - **`@vercel/build-utils`**: add the `ContainerImage` build-output type.
33
+ - **`@vercel/fs-detectors`**: resolve container services from `vercel.json`
34
+ (the `services` config and its deprecated `experimentalServices` /
35
+ `experimentalServicesV2` aliases). A `Dockerfile`, `Containerfile`, or
36
+ `*.dockerfile` entrypoint triggers a build; any other entrypoint is treated as
37
+ a prebuilt OCI image reference.
38
+ - **`vercel`**: wire container output into `vercel build` result writing and
39
+ config validation.
40
+
41
+ Buildah specifics in the build container: host networking for `RUN` steps,
42
+ native `overlay` storage on the XFS `/vercel` volume (deferring to the image's
43
+ `storage.conf`), zstd push compression, and registry credentials read from the
44
+ provisioned auth file when present. Several knobs are available for debugging:
45
+ `VERCEL_CONTAINER_ENGINE`, `VERCEL_VCR_STRICT_STORAGE`,
46
+ `VERCEL_VCR_DISABLE_LAYER_CACHE`, and `VERCEL_VCR_FORCE_LOGIN`.
47
+
48
+ - cb0988f: Set the Node.js 20 discontinuation date to October 1, 2026.
49
+
3
50
  ## 13.31.1
4
51
 
5
52
  ### 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 {
@@ -228,6 +229,11 @@ export interface StartDevServerSuccess {
228
229
  * dev server will forcefully be killed.
229
230
  */
230
231
  shutdown?: () => Promise<void>;
232
+ /**
233
+ * Whether the builder owns this dev server's lifecycle across requests.
234
+ * Persistent servers are still shut down when `vercel dev` exits.
235
+ */
236
+ persistent?: boolean;
231
237
  /**
232
238
  * Cron entries produced by the builder for this service.
233
239
  * Used by the dev orchestrator to schedule cron triggers.
@@ -546,6 +552,8 @@ export interface ExperimentalServiceV2 {
546
552
  runtime?: string;
547
553
  /** Resolved entrypoint, relative to the service root. */
548
554
  entrypoint?: string;
555
+ /** Command override for `runtime: "container"` services. */
556
+ command?: string[];
549
557
  /** Builder selected by the resolver. */
550
558
  builder: Builder;
551
559
  installCommand?: string;
@@ -610,7 +618,7 @@ export interface BuildResultV2Typical {
610
618
  routes?: any[];
611
619
  images?: Images;
612
620
  output: {
613
- [key: string]: File | Lambda | Prerender | EdgeFunction;
621
+ [key: string]: File | Lambda | Prerender | EdgeFunction | ContainerImage;
614
622
  };
615
623
  wildcard?: Array<{
616
624
  domain: string;
@@ -744,7 +752,7 @@ export interface TriggerEvent extends TriggerEventBase {
744
752
  /** Name of the consumer group for this trigger (always present in processed output) */
745
753
  consumer: string;
746
754
  }
747
- export type ServiceRuntime = 'node' | 'python' | 'go' | 'rust' | 'ruby';
755
+ export type ServiceRuntime = 'node' | 'python' | 'go' | 'rust' | 'ruby' | 'container';
748
756
  export type ServiceType = 'web' | 'cron' | 'worker' | 'job';
749
757
  export interface ExperimentalServiceMount {
750
758
  /** URL path prefix where the service is mounted. */
@@ -780,8 +788,10 @@ export interface ExperimentalServiceConfig {
780
788
  framework?: string;
781
789
  /** Builder to use, e.g. @vercel/node, @vercel/python */
782
790
  builder?: string;
783
- /** Specific lambda runtime to use, e.g. nodejs24.x, python3.14 */
791
+ /** Specific lambda runtime to use, e.g. nodejs24.x, python3.14, container */
784
792
  runtime?: string;
793
+ /** Optional command override for container image services. */
794
+ command?: string | string[];
785
795
  workspace?: string;
786
796
  buildCommand?: string;
787
797
  installCommand?: string;
@@ -817,22 +827,18 @@ export type ExperimentalServices = Record<string, ExperimentalServiceConfig>;
817
827
  * }
818
828
  */
819
829
  export type ExperimentalServiceGroups = Record<string, string[]>;
820
- export interface ExperimentalServiceV2Binding {
830
+ export interface ServiceBinding {
821
831
  /** Must be `"service"` for Service-to-Service HTTP bindings. */
822
832
  type: 'service';
823
- /** Target service name from `experimentalServicesV2`. */
833
+ /** Target service name from `services`. */
824
834
  service: string;
825
835
  /** Generated value shape, must be `"url"`. */
826
836
  format: 'url';
827
837
  /** Environment variable name that will store the generated value */
828
838
  env: string;
829
839
  }
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 {
840
+ /** Configuration for a service in `vercel.json#services`. */
841
+ export interface ServiceConfig {
836
842
  /** Path to the service root, relative to `vercel.json`. */
837
843
  root: string;
838
844
  /** Framework for this service. */
@@ -842,15 +848,19 @@ export interface ExperimentalServiceV2Config {
842
848
  /**
843
849
  * Service entrypoint, relative to the service root directory.
844
850
  * Can be a file path or a module specification (for Python).
851
+ * For `runtime: "container"`, a Dockerfile path (built & pushed) or a
852
+ * prebuilt OCI image reference.
845
853
  */
846
854
  entrypoint?: string;
855
+ /** Command override for `runtime: "container"` services. */
856
+ command?: string | string[];
847
857
  installCommand?: string;
848
858
  buildCommand?: string;
849
859
  devCommand?: string;
850
860
  ignoreCommand?: string;
851
861
  outputDirectory?: string;
852
862
  /** Caller-side bindings that grant this service access to another service. */
853
- bindings?: ExperimentalServiceV2Binding[];
863
+ bindings?: ServiceBinding[];
854
864
  /** Function configuration scoped to this service root. */
855
865
  functions?: BuilderFunctions;
856
866
  headers?: Header[];
@@ -860,12 +870,14 @@ export interface ExperimentalServiceV2Config {
860
870
  cleanUrls?: boolean;
861
871
  trailingSlash?: boolean;
862
872
  }
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>;
873
+ /** Map of service name to service configuration for `vercel.json#services`. */
874
+ export type Services = Record<string, ServiceConfig>;
875
+ /** @deprecated Use `ServiceBinding` instead. */
876
+ export type ExperimentalServiceV2Binding = ServiceBinding;
877
+ /** @deprecated Use `ServiceConfig` instead. */
878
+ export type ExperimentalServiceV2Config = ServiceConfig;
879
+ /** @deprecated Use `Services` instead. */
880
+ export type ExperimentalServicesV2 = Services;
869
881
  /**
870
882
  * Result of a runtime builder's normalized entrypoint detection.
871
883
  *
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.1",
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",