@vercel/build-utils 13.12.1 → 13.13.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,17 @@
1
1
  # @vercel/build-utils
2
2
 
3
+ ## 13.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Extend `finalizeLambda` with pluggable ZIP strategy (`createZip`), pre-digest validation hook (`validateZip`), and optional trace tags. Widen `getLambdaEnvironment` buffer param to `{ byteLength: number }`. ([#15856](https://github.com/vercel/vercel/pull/15856))
8
+
9
+ ## 13.12.2
10
+
11
+ ### Patch Changes
12
+
13
+ - Extract finalize/validate function utils for build-utils ([#15776](https://github.com/vercel/vercel/pull/15776))
14
+
3
15
  ## 13.12.1
4
16
 
5
17
  ### Patch Changes
@@ -0,0 +1,6 @@
1
+ import type { Files } from './types';
2
+ /**
3
+ * Collects the total uncompressed size of a set of Lambda files.
4
+ * Handles both FileBlob (in-memory) and FileFsRef (on-disk) file types.
5
+ */
6
+ export declare const collectUncompressedSize: (files: Files, ignoreFn?: ((fileKey: string) => boolean) | undefined) => Promise<number>;
@@ -0,0 +1,59 @@
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 collect_uncompressed_size_exports = {};
20
+ __export(collect_uncompressed_size_exports, {
21
+ collectUncompressedSize: () => collectUncompressedSize
22
+ });
23
+ module.exports = __toCommonJS(collect_uncompressed_size_exports);
24
+ var import_promises = require("fs/promises");
25
+ const fileSizeCache = /* @__PURE__ */ new Map();
26
+ const getFileSize = (path) => {
27
+ if (!path)
28
+ return Promise.resolve(0);
29
+ const cached = fileSizeCache.get(path);
30
+ if (cached) {
31
+ return cached;
32
+ }
33
+ const promise = (0, import_promises.lstat)(path).then((stats) => stats.size);
34
+ fileSizeCache.set(path, promise);
35
+ return promise;
36
+ };
37
+ const collectUncompressedSize = async (files, ignoreFn) => {
38
+ let size = 0;
39
+ await Promise.all(
40
+ Object.keys(files).map(async (fileKey) => {
41
+ if (ignoreFn?.(fileKey)) {
42
+ return;
43
+ }
44
+ const file = files[fileKey];
45
+ if (file.type === "FileBlob") {
46
+ size += file.data.length;
47
+ } else if (file.type === "FileFsRef") {
48
+ const fsRef = file;
49
+ const curSize = fsRef.size ?? await getFileSize(fsRef.fsPath);
50
+ size += curSize;
51
+ }
52
+ })
53
+ );
54
+ return size;
55
+ };
56
+ // Annotate the CommonJS export names for ESM import in node:
57
+ 0 && (module.exports = {
58
+ collectUncompressedSize
59
+ });
@@ -0,0 +1,81 @@
1
+ /// <reference types="node" />
2
+ import type { Lambda } from './lambda';
3
+ import type { NodejsLambda } from './nodejs-lambda';
4
+ import type { BytecodeCachingOptions } from './process-serverless/get-lambda-preload-scripts';
5
+ import type { SupportsStreamingResult } from './process-serverless/get-lambda-supports-streaming';
6
+ /**
7
+ * Optional wrapper around async work, allowing callers to inject tracing
8
+ * (e.g. dd-trace spans) without coupling the shared code to a tracer.
9
+ */
10
+ export type TraceFn = <T>(name: string, fn: () => Promise<T>, tags?: Record<string, string>) => Promise<T>;
11
+ /**
12
+ * Result of a custom ZIP creation strategy.
13
+ */
14
+ export interface CreateZipResult {
15
+ /** The zip as a Buffer (in-memory), or null for disk-based paths. */
16
+ buffer: Buffer | null;
17
+ /** Path to the zip file on disk, or undefined for in-memory. */
18
+ zipPath?: string;
19
+ /** SHA-256 hex digest of the zip contents. */
20
+ digest: string;
21
+ /** Compressed size in bytes. */
22
+ size: number;
23
+ }
24
+ /**
25
+ * Custom ZIP creation strategy. When provided, replaces the default
26
+ * in-memory `lambda.createZip()` + `sha256()` path. This allows callers
27
+ * to stream large zips to disk instead.
28
+ */
29
+ export type CreateZipFn = (lambda: Lambda | NodejsLambda) => Promise<CreateZipResult>;
30
+ export interface FinalizeLambdaParams {
31
+ lambda: Lambda | NodejsLambda;
32
+ encryptedEnvFilename?: string;
33
+ encryptedEnvContent?: string;
34
+ bytecodeCachingOptions: BytecodeCachingOptions;
35
+ forceStreamingRuntime: boolean;
36
+ /** When true, collect the uncompressed size of lambda files before zipping. */
37
+ enableUncompressedLambdaSizeCheck?: boolean;
38
+ /** Optional tracing wrapper for `collectUncompressedSize` and `createZip`. */
39
+ trace?: TraceFn;
40
+ /** Custom ZIP creation strategy. Defaults to in-memory lambda.createZip(). */
41
+ createZip?: CreateZipFn;
42
+ /**
43
+ * Called after ZIP creation but before digest/environment/streaming.
44
+ * Throw to abort (e.g. size validation). For the default in-memory path,
45
+ * this runs before sha256.
46
+ */
47
+ validateZip?: (zip: {
48
+ buffer: Buffer | null;
49
+ zipPath?: string;
50
+ size: number;
51
+ }) => void;
52
+ }
53
+ export interface FinalizeLambdaResult {
54
+ /** The zip as a Buffer, or null when a custom createZip returns a disk path. */
55
+ buffer: Buffer | null;
56
+ /** Path to zip on disk (set by custom createZip), undefined for in-memory. */
57
+ zipPath?: string;
58
+ /** SHA-256 hex digest. */
59
+ digest: string;
60
+ /** Compressed size in bytes. */
61
+ size: number;
62
+ uncompressedBytes: number;
63
+ /** Non-fatal streaming detection error, if any. Caller decides how to log. */
64
+ streamingError?: SupportsStreamingResult['error'];
65
+ }
66
+ /**
67
+ * Core Lambda finalization logic shared between BYOF and build-container.
68
+ *
69
+ * This function:
70
+ * 1. Injects encrypted env file into lambda.files when provided
71
+ * 2. Collects uncompressed size when enabled
72
+ * 3. Creates the ZIP (in-memory or via custom strategy)
73
+ * 4. Runs optional validateZip hook (e.g. size check)
74
+ * 5. Computes SHA-256 digest (default path only; custom path provides its own)
75
+ * 6. Merges environment variables (bytecode caching, helpers, etc.)
76
+ * 7. Detects streaming support
77
+ *
78
+ * Note: This function mutates the `lambda` (files, environment,
79
+ * supportsResponseStreaming).
80
+ */
81
+ export declare function finalizeLambda(params: FinalizeLambdaParams): Promise<FinalizeLambdaResult>;
@@ -0,0 +1,118 @@
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 finalize_lambda_exports = {};
20
+ __export(finalize_lambda_exports, {
21
+ finalizeLambda: () => finalizeLambda
22
+ });
23
+ module.exports = __toCommonJS(finalize_lambda_exports);
24
+ var import_get_encrypted_env_file = require("./process-serverless/get-encrypted-env-file");
25
+ var import_get_lambda_environment = require("./process-serverless/get-lambda-environment");
26
+ var import_get_lambda_supports_streaming = require("./process-serverless/get-lambda-supports-streaming");
27
+ var import_stream_to_digest_async = require("./fs/stream-to-digest-async");
28
+ var import_collect_uncompressed_size = require("./collect-uncompressed-size");
29
+ const defaultTrace = (_name, fn) => fn();
30
+ async function finalizeLambda(params) {
31
+ const {
32
+ lambda,
33
+ encryptedEnvFilename,
34
+ encryptedEnvContent,
35
+ bytecodeCachingOptions,
36
+ forceStreamingRuntime,
37
+ enableUncompressedLambdaSizeCheck,
38
+ trace = defaultTrace,
39
+ createZip: createZipOverride,
40
+ validateZip
41
+ } = params;
42
+ const encryptedEnv = (0, import_get_encrypted_env_file.getEncryptedEnv)(
43
+ encryptedEnvFilename,
44
+ encryptedEnvContent
45
+ );
46
+ if (encryptedEnv) {
47
+ const [envFilename, envFile] = encryptedEnv;
48
+ lambda.zipBuffer = void 0;
49
+ lambda.files = {
50
+ ...lambda.files,
51
+ [envFilename]: envFile
52
+ };
53
+ }
54
+ let uncompressedBytes = 0;
55
+ if (enableUncompressedLambdaSizeCheck) {
56
+ if (lambda.files) {
57
+ uncompressedBytes = await trace(
58
+ "collectUncompressedSize",
59
+ () => (0, import_collect_uncompressed_size.collectUncompressedSize)(lambda.files ?? {})
60
+ );
61
+ }
62
+ }
63
+ const zipTags = {
64
+ fileCount: String(Object.keys(lambda.files ?? {}).length),
65
+ uncompressedBytes: String(uncompressedBytes)
66
+ };
67
+ let zipResult;
68
+ if (createZipOverride) {
69
+ zipResult = await trace(
70
+ "createZip",
71
+ () => createZipOverride(lambda),
72
+ zipTags
73
+ );
74
+ } else {
75
+ const buffer = lambda.zipBuffer || await trace("createZip", () => lambda.createZip(), zipTags);
76
+ zipResult = {
77
+ buffer,
78
+ digest: "",
79
+ // computed in step 5
80
+ size: buffer.byteLength
81
+ };
82
+ }
83
+ if (validateZip) {
84
+ validateZip({
85
+ buffer: zipResult.buffer,
86
+ zipPath: zipResult.zipPath,
87
+ size: zipResult.size
88
+ });
89
+ }
90
+ if (!createZipOverride && zipResult.buffer) {
91
+ zipResult.digest = (0, import_stream_to_digest_async.sha256)(zipResult.buffer);
92
+ }
93
+ lambda.environment = {
94
+ ...lambda.environment,
95
+ ...(0, import_get_lambda_environment.getLambdaEnvironment)(
96
+ lambda,
97
+ zipResult.buffer ?? { byteLength: zipResult.size },
98
+ bytecodeCachingOptions
99
+ )
100
+ };
101
+ const streamingResult = await (0, import_get_lambda_supports_streaming.getLambdaSupportsStreaming)(
102
+ lambda,
103
+ forceStreamingRuntime
104
+ );
105
+ lambda.supportsResponseStreaming = streamingResult.supportsStreaming;
106
+ return {
107
+ buffer: zipResult.buffer,
108
+ zipPath: zipResult.zipPath,
109
+ digest: zipResult.digest,
110
+ size: zipResult.size,
111
+ uncompressedBytes,
112
+ streamingError: streamingResult.error
113
+ };
114
+ }
115
+ // Annotate the CommonJS export names for ESM import in node:
116
+ 0 && (module.exports = {
117
+ finalizeLambda
118
+ });
package/dist/index.d.ts CHANGED
@@ -46,3 +46,6 @@ export { getLambdaByOutputPath } from './collect-build-result/get-lambda-by-outp
46
46
  export { isRouteMiddleware } from './collect-build-result/is-route-middleware';
47
47
  export { getPrerenderChain } from './collect-build-result/get-prerender-chain';
48
48
  export { streamWithExtendedPayload, type ExtendedBodyData, } from './collect-build-result/stream-with-extended-payload';
49
+ export { collectUncompressedSize } from './collect-uncompressed-size';
50
+ export { finalizeLambda, type CreateZipResult, type CreateZipFn, type FinalizeLambdaParams, type FinalizeLambdaResult, type TraceFn, } from './finalize-lambda';
51
+ export { validateLambdaSize, validateUncompressedLambdaSize, FunctionSizeError, MAX_LAMBDA_SIZE, MAX_LAMBDA_UNCOMPRESSED_SIZE, validateEnvWrapperSupport, ENV_WRAPPER_SUPPORTED_FAMILIES, } from './validate-lambda-size';
package/dist/index.js CHANGED
@@ -11924,18 +11924,18 @@ var require_sync = __commonJS({
11924
11924
  if (this.follow)
11925
11925
  return this._readdir(abs, false);
11926
11926
  var entries;
11927
- var lstat2;
11927
+ var lstat3;
11928
11928
  var stat;
11929
11929
  try {
11930
- lstat2 = this.fs.lstatSync(abs);
11930
+ lstat3 = this.fs.lstatSync(abs);
11931
11931
  } catch (er) {
11932
11932
  if (er.code === "ENOENT") {
11933
11933
  return null;
11934
11934
  }
11935
11935
  }
11936
- var isSym = lstat2 && lstat2.isSymbolicLink();
11936
+ var isSym = lstat3 && lstat3.isSymbolicLink();
11937
11937
  this.symlinks[abs] = isSym;
11938
- if (!isSym && lstat2 && !lstat2.isDirectory())
11938
+ if (!isSym && lstat3 && !lstat3.isDirectory())
11939
11939
  this.cache[abs] = "FILE";
11940
11940
  else
11941
11941
  entries = this._readdir(abs, false);
@@ -12060,23 +12060,23 @@ var require_sync = __commonJS({
12060
12060
  var exists;
12061
12061
  var stat = this.statCache[abs];
12062
12062
  if (!stat) {
12063
- var lstat2;
12063
+ var lstat3;
12064
12064
  try {
12065
- lstat2 = this.fs.lstatSync(abs);
12065
+ lstat3 = this.fs.lstatSync(abs);
12066
12066
  } catch (er) {
12067
12067
  if (er && (er.code === "ENOENT" || er.code === "ENOTDIR")) {
12068
12068
  this.statCache[abs] = false;
12069
12069
  return false;
12070
12070
  }
12071
12071
  }
12072
- if (lstat2 && lstat2.isSymbolicLink()) {
12072
+ if (lstat3 && lstat3.isSymbolicLink()) {
12073
12073
  try {
12074
12074
  stat = this.fs.statSync(abs);
12075
12075
  } catch (er) {
12076
- stat = lstat2;
12076
+ stat = lstat3;
12077
12077
  }
12078
12078
  } else {
12079
- stat = lstat2;
12079
+ stat = lstat3;
12080
12080
  }
12081
12081
  }
12082
12082
  this.statCache[abs] = stat;
@@ -12493,12 +12493,12 @@ var require_glob = __commonJS({
12493
12493
  var lstatcb = inflight(lstatkey, lstatcb_);
12494
12494
  if (lstatcb)
12495
12495
  self2.fs.lstat(abs, lstatcb);
12496
- function lstatcb_(er, lstat2) {
12496
+ function lstatcb_(er, lstat3) {
12497
12497
  if (er && er.code === "ENOENT")
12498
12498
  return cb();
12499
- var isSym = lstat2 && lstat2.isSymbolicLink();
12499
+ var isSym = lstat3 && lstat3.isSymbolicLink();
12500
12500
  self2.symlinks[abs] = isSym;
12501
- if (!isSym && lstat2 && !lstat2.isDirectory()) {
12501
+ if (!isSym && lstat3 && !lstat3.isDirectory()) {
12502
12502
  self2.cache[abs] = "FILE";
12503
12503
  cb();
12504
12504
  } else
@@ -12666,16 +12666,16 @@ var require_glob = __commonJS({
12666
12666
  var statcb = inflight("stat\0" + abs, lstatcb_);
12667
12667
  if (statcb)
12668
12668
  self2.fs.lstat(abs, statcb);
12669
- function lstatcb_(er, lstat2) {
12670
- if (lstat2 && lstat2.isSymbolicLink()) {
12669
+ function lstatcb_(er, lstat3) {
12670
+ if (lstat3 && lstat3.isSymbolicLink()) {
12671
12671
  return self2.fs.stat(abs, function(er2, stat2) {
12672
12672
  if (er2)
12673
- self2._stat2(f, abs, null, lstat2, cb);
12673
+ self2._stat2(f, abs, null, lstat3, cb);
12674
12674
  else
12675
12675
  self2._stat2(f, abs, er2, stat2, cb);
12676
12676
  });
12677
12677
  } else {
12678
- self2._stat2(f, abs, er, lstat2, cb);
12678
+ self2._stat2(f, abs, er, lstat3, cb);
12679
12679
  }
12680
12680
  }
12681
12681
  };
@@ -19806,6 +19806,95 @@ var require_ignore = __commonJS({
19806
19806
  }
19807
19807
  });
19808
19808
 
19809
+ // ../../node_modules/.pnpm/bytes@3.1.2/node_modules/bytes/index.js
19810
+ var require_bytes = __commonJS({
19811
+ "../../node_modules/.pnpm/bytes@3.1.2/node_modules/bytes/index.js"(exports, module2) {
19812
+ "use strict";
19813
+ module2.exports = bytes2;
19814
+ module2.exports.format = format;
19815
+ module2.exports.parse = parse6;
19816
+ var formatThousandsRegExp = /\B(?=(\d{3})+(?!\d))/g;
19817
+ var formatDecimalsRegExp = /(?:\.0*|(\.[^0]+)0+)$/;
19818
+ var map = {
19819
+ b: 1,
19820
+ kb: 1 << 10,
19821
+ mb: 1 << 20,
19822
+ gb: 1 << 30,
19823
+ tb: Math.pow(1024, 4),
19824
+ pb: Math.pow(1024, 5)
19825
+ };
19826
+ var parseRegExp = /^((-|\+)?(\d+(?:\.\d+)?)) *(kb|mb|gb|tb|pb)$/i;
19827
+ function bytes2(value, options) {
19828
+ if (typeof value === "string") {
19829
+ return parse6(value);
19830
+ }
19831
+ if (typeof value === "number") {
19832
+ return format(value, options);
19833
+ }
19834
+ return null;
19835
+ }
19836
+ function format(value, options) {
19837
+ if (!Number.isFinite(value)) {
19838
+ return null;
19839
+ }
19840
+ var mag = Math.abs(value);
19841
+ var thousandsSeparator = options && options.thousandsSeparator || "";
19842
+ var unitSeparator = options && options.unitSeparator || "";
19843
+ var decimalPlaces = options && options.decimalPlaces !== void 0 ? options.decimalPlaces : 2;
19844
+ var fixedDecimals = Boolean(options && options.fixedDecimals);
19845
+ var unit = options && options.unit || "";
19846
+ if (!unit || !map[unit.toLowerCase()]) {
19847
+ if (mag >= map.pb) {
19848
+ unit = "PB";
19849
+ } else if (mag >= map.tb) {
19850
+ unit = "TB";
19851
+ } else if (mag >= map.gb) {
19852
+ unit = "GB";
19853
+ } else if (mag >= map.mb) {
19854
+ unit = "MB";
19855
+ } else if (mag >= map.kb) {
19856
+ unit = "KB";
19857
+ } else {
19858
+ unit = "B";
19859
+ }
19860
+ }
19861
+ var val = value / map[unit.toLowerCase()];
19862
+ var str = val.toFixed(decimalPlaces);
19863
+ if (!fixedDecimals) {
19864
+ str = str.replace(formatDecimalsRegExp, "$1");
19865
+ }
19866
+ if (thousandsSeparator) {
19867
+ str = str.split(".").map(function(s, i) {
19868
+ return i === 0 ? s.replace(formatThousandsRegExp, thousandsSeparator) : s;
19869
+ }).join(".");
19870
+ }
19871
+ return str + unitSeparator + unit;
19872
+ }
19873
+ function parse6(val) {
19874
+ if (typeof val === "number" && !isNaN(val)) {
19875
+ return val;
19876
+ }
19877
+ if (typeof val !== "string") {
19878
+ return null;
19879
+ }
19880
+ var results = parseRegExp.exec(val);
19881
+ var floatValue;
19882
+ var unit = "b";
19883
+ if (!results) {
19884
+ floatValue = parseInt(val, 10);
19885
+ unit = "b";
19886
+ } else {
19887
+ floatValue = parseFloat(results[1]);
19888
+ unit = results[4].toLowerCase();
19889
+ }
19890
+ if (isNaN(floatValue)) {
19891
+ return null;
19892
+ }
19893
+ return Math.floor(map[unit] * floatValue);
19894
+ }
19895
+ }
19896
+ });
19897
+
19809
19898
  // src/index.ts
19810
19899
  var src_exports = {};
19811
19900
  __export(src_exports, {
@@ -19814,11 +19903,15 @@ __export(src_exports, {
19814
19903
  BUILDER_COMPILE_STEP: () => BUILDER_COMPILE_STEP,
19815
19904
  BUILDER_INSTALLER_STEP: () => BUILDER_INSTALLER_STEP,
19816
19905
  BunVersion: () => BunVersion,
19906
+ ENV_WRAPPER_SUPPORTED_FAMILIES: () => ENV_WRAPPER_SUPPORTED_FAMILIES,
19817
19907
  EdgeFunction: () => EdgeFunction,
19818
19908
  FileBlob: () => FileBlob,
19819
19909
  FileFsRef: () => file_fs_ref_default,
19820
19910
  FileRef: () => FileRef,
19911
+ FunctionSizeError: () => FunctionSizeError,
19821
19912
  Lambda: () => Lambda,
19913
+ MAX_LAMBDA_SIZE: () => MAX_LAMBDA_SIZE,
19914
+ MAX_LAMBDA_UNCOMPRESSED_SIZE: () => MAX_LAMBDA_UNCOMPRESSED_SIZE,
19822
19915
  NODE_VERSIONS: () => NODE_VERSIONS,
19823
19916
  NodeVersion: () => NodeVersion,
19824
19917
  NodejsLambda: () => NodejsLambda,
@@ -19830,6 +19923,7 @@ __export(src_exports, {
19830
19923
  Version: () => Version,
19831
19924
  buildsSchema: () => buildsSchema,
19832
19925
  cloneEnv: () => cloneEnv,
19926
+ collectUncompressedSize: () => collectUncompressedSize,
19833
19927
  createLambda: () => createLambda,
19834
19928
  debug: () => debug,
19835
19929
  defaultCachePathGlob: () => defaultCachePathGlob,
@@ -19837,6 +19931,7 @@ __export(src_exports, {
19837
19931
  download: () => download,
19838
19932
  downloadFile: () => downloadFile,
19839
19933
  execCommand: () => execCommand,
19934
+ finalizeLambda: () => finalizeLambda,
19840
19935
  findPackageJson: () => findPackageJson,
19841
19936
  functionsSchema: () => functionsSchema,
19842
19937
  generateNodeBuilderFunctions: () => generateNodeBuilderFunctions,
@@ -19908,7 +20003,10 @@ __export(src_exports, {
19908
20003
  streamToDigestAsync: () => streamToDigestAsync,
19909
20004
  streamWithExtendedPayload: () => streamWithExtendedPayload,
19910
20005
  traverseUpDirectories: () => traverseUpDirectories,
20006
+ validateEnvWrapperSupport: () => validateEnvWrapperSupport,
20007
+ validateLambdaSize: () => validateLambdaSize,
19911
20008
  validateNpmrc: () => validateNpmrc,
20009
+ validateUncompressedLambdaSize: () => validateUncompressedLambdaSize,
19912
20010
  walkParentDirs: () => walkParentDirs
19913
20011
  });
19914
20012
  module.exports = __toCommonJS(src_exports);
@@ -24343,6 +24441,197 @@ var MultipartContentStream = class extends import_stream.Readable {
24343
24441
  _read() {
24344
24442
  }
24345
24443
  };
24444
+
24445
+ // src/collect-uncompressed-size.ts
24446
+ var import_promises2 = require("fs/promises");
24447
+ var fileSizeCache = /* @__PURE__ */ new Map();
24448
+ var getFileSize = (path7) => {
24449
+ if (!path7)
24450
+ return Promise.resolve(0);
24451
+ const cached = fileSizeCache.get(path7);
24452
+ if (cached) {
24453
+ return cached;
24454
+ }
24455
+ const promise = (0, import_promises2.lstat)(path7).then((stats) => stats.size);
24456
+ fileSizeCache.set(path7, promise);
24457
+ return promise;
24458
+ };
24459
+ var collectUncompressedSize = async (files, ignoreFn) => {
24460
+ let size = 0;
24461
+ await Promise.all(
24462
+ Object.keys(files).map(async (fileKey) => {
24463
+ if (ignoreFn?.(fileKey)) {
24464
+ return;
24465
+ }
24466
+ const file = files[fileKey];
24467
+ if (file.type === "FileBlob") {
24468
+ size += file.data.length;
24469
+ } else if (file.type === "FileFsRef") {
24470
+ const fsRef = file;
24471
+ const curSize = fsRef.size ?? await getFileSize(fsRef.fsPath);
24472
+ size += curSize;
24473
+ }
24474
+ })
24475
+ );
24476
+ return size;
24477
+ };
24478
+
24479
+ // src/finalize-lambda.ts
24480
+ var defaultTrace = (_name, fn) => fn();
24481
+ async function finalizeLambda(params) {
24482
+ const {
24483
+ lambda,
24484
+ encryptedEnvFilename,
24485
+ encryptedEnvContent,
24486
+ bytecodeCachingOptions,
24487
+ forceStreamingRuntime,
24488
+ enableUncompressedLambdaSizeCheck,
24489
+ trace = defaultTrace,
24490
+ createZip: createZipOverride,
24491
+ validateZip
24492
+ } = params;
24493
+ const encryptedEnv = getEncryptedEnv(
24494
+ encryptedEnvFilename,
24495
+ encryptedEnvContent
24496
+ );
24497
+ if (encryptedEnv) {
24498
+ const [envFilename, envFile] = encryptedEnv;
24499
+ lambda.zipBuffer = void 0;
24500
+ lambda.files = {
24501
+ ...lambda.files,
24502
+ [envFilename]: envFile
24503
+ };
24504
+ }
24505
+ let uncompressedBytes = 0;
24506
+ if (enableUncompressedLambdaSizeCheck) {
24507
+ if (lambda.files) {
24508
+ uncompressedBytes = await trace(
24509
+ "collectUncompressedSize",
24510
+ () => collectUncompressedSize(lambda.files ?? {})
24511
+ );
24512
+ }
24513
+ }
24514
+ const zipTags = {
24515
+ fileCount: String(Object.keys(lambda.files ?? {}).length),
24516
+ uncompressedBytes: String(uncompressedBytes)
24517
+ };
24518
+ let zipResult;
24519
+ if (createZipOverride) {
24520
+ zipResult = await trace(
24521
+ "createZip",
24522
+ () => createZipOverride(lambda),
24523
+ zipTags
24524
+ );
24525
+ } else {
24526
+ const buffer = lambda.zipBuffer || await trace("createZip", () => lambda.createZip(), zipTags);
24527
+ zipResult = {
24528
+ buffer,
24529
+ digest: "",
24530
+ // computed in step 5
24531
+ size: buffer.byteLength
24532
+ };
24533
+ }
24534
+ if (validateZip) {
24535
+ validateZip({
24536
+ buffer: zipResult.buffer,
24537
+ zipPath: zipResult.zipPath,
24538
+ size: zipResult.size
24539
+ });
24540
+ }
24541
+ if (!createZipOverride && zipResult.buffer) {
24542
+ zipResult.digest = sha256(zipResult.buffer);
24543
+ }
24544
+ lambda.environment = {
24545
+ ...lambda.environment,
24546
+ ...getLambdaEnvironment(
24547
+ lambda,
24548
+ zipResult.buffer ?? { byteLength: zipResult.size },
24549
+ bytecodeCachingOptions
24550
+ )
24551
+ };
24552
+ const streamingResult = await getLambdaSupportsStreaming(
24553
+ lambda,
24554
+ forceStreamingRuntime
24555
+ );
24556
+ lambda.supportsResponseStreaming = streamingResult.supportsStreaming;
24557
+ return {
24558
+ buffer: zipResult.buffer,
24559
+ zipPath: zipResult.zipPath,
24560
+ digest: zipResult.digest,
24561
+ size: zipResult.size,
24562
+ uncompressedBytes,
24563
+ streamingError: streamingResult.error
24564
+ };
24565
+ }
24566
+
24567
+ // src/validate-lambda-size.ts
24568
+ var import_bytes = __toESM(require_bytes());
24569
+ var MAX_LAMBDA_SIZE = (0, import_bytes.default)("300mb");
24570
+ var MAX_LAMBDA_UNCOMPRESSED_SIZE = 250 * 1024 * 1024;
24571
+ var FunctionSizeError = class extends NowBuildError {
24572
+ constructor(outputPath, size) {
24573
+ super({
24574
+ code: "NOW_SANDBOX_WORKER_MAX_LAMBDA_SIZE",
24575
+ message: `The Vercel Function "${outputPath}" is ${(0, import_bytes.default)(
24576
+ size
24577
+ ).toLowerCase()} which exceeds the maximum size limit of ${(0, import_bytes.default)(
24578
+ MAX_LAMBDA_SIZE
24579
+ ).toLowerCase()}.`,
24580
+ link: "https://vercel.link/serverless-function-size",
24581
+ action: "Learn More"
24582
+ });
24583
+ this.size = size;
24584
+ this.maxSize = MAX_LAMBDA_SIZE;
24585
+ }
24586
+ };
24587
+ function validateLambdaSize(outputPath, runtime, size) {
24588
+ if (runtime.startsWith("python")) {
24589
+ return;
24590
+ }
24591
+ if (size > MAX_LAMBDA_SIZE) {
24592
+ throw new FunctionSizeError(outputPath, size);
24593
+ }
24594
+ }
24595
+ function validateUncompressedLambdaSize(outputPath, uncompressedBytes) {
24596
+ if (uncompressedBytes >= MAX_LAMBDA_UNCOMPRESSED_SIZE) {
24597
+ throw new NowBuildError({
24598
+ code: "NOW_SANDBOX_WORKER_MAX_UNCOMPRESSED_LAMBDA_SIZE",
24599
+ message: `The Vercel Function "${outputPath}" is ${(0, import_bytes.default)(
24600
+ uncompressedBytes
24601
+ ).toLowerCase()} uncompressed which exceeds the maximum uncompressed size limit of ${(0, import_bytes.default)(
24602
+ MAX_LAMBDA_UNCOMPRESSED_SIZE
24603
+ ).toLowerCase()}.`,
24604
+ link: "https://vercel.link/serverless-function-size",
24605
+ action: "Learn More"
24606
+ });
24607
+ }
24608
+ }
24609
+ var ENV_WRAPPER_SUPPORTED_FAMILIES = [
24610
+ "nodejs",
24611
+ "python",
24612
+ "ruby",
24613
+ "java",
24614
+ "dotnetcore",
24615
+ "bun",
24616
+ "executable"
24617
+ ];
24618
+ function validateEnvWrapperSupport(encryptedEnvFilename, encryptedEnvContent, lambda) {
24619
+ if (!encryptedEnvFilename || !encryptedEnvContent) {
24620
+ return;
24621
+ }
24622
+ if (!lambda.supportsWrapper && !ENV_WRAPPER_SUPPORTED_FAMILIES.some(
24623
+ (family) => lambda.runtime.startsWith(family)
24624
+ )) {
24625
+ throw new Error(
24626
+ `Serverless Function runtime ${lambda.runtime} does not support more than 4KB for environment variables`
24627
+ );
24628
+ }
24629
+ if (typeof lambda.createZip !== "function") {
24630
+ throw new Error(
24631
+ `Serverless Function with runtime ${lambda.runtime} has no createZip function`
24632
+ );
24633
+ }
24634
+ }
24346
24635
  // Annotate the CommonJS export names for ESM import in node:
24347
24636
  0 && (module.exports = {
24348
24637
  BACKEND_BUILDERS,
@@ -24350,11 +24639,15 @@ var MultipartContentStream = class extends import_stream.Readable {
24350
24639
  BUILDER_COMPILE_STEP,
24351
24640
  BUILDER_INSTALLER_STEP,
24352
24641
  BunVersion,
24642
+ ENV_WRAPPER_SUPPORTED_FAMILIES,
24353
24643
  EdgeFunction,
24354
24644
  FileBlob,
24355
24645
  FileFsRef,
24356
24646
  FileRef,
24647
+ FunctionSizeError,
24357
24648
  Lambda,
24649
+ MAX_LAMBDA_SIZE,
24650
+ MAX_LAMBDA_UNCOMPRESSED_SIZE,
24358
24651
  NODE_VERSIONS,
24359
24652
  NodeVersion,
24360
24653
  NodejsLambda,
@@ -24366,6 +24659,7 @@ var MultipartContentStream = class extends import_stream.Readable {
24366
24659
  Version,
24367
24660
  buildsSchema,
24368
24661
  cloneEnv,
24662
+ collectUncompressedSize,
24369
24663
  createLambda,
24370
24664
  debug,
24371
24665
  defaultCachePathGlob,
@@ -24373,6 +24667,7 @@ var MultipartContentStream = class extends import_stream.Readable {
24373
24667
  download,
24374
24668
  downloadFile,
24375
24669
  execCommand,
24670
+ finalizeLambda,
24376
24671
  findPackageJson,
24377
24672
  functionsSchema,
24378
24673
  generateNodeBuilderFunctions,
@@ -24444,11 +24739,22 @@ var MultipartContentStream = class extends import_stream.Readable {
24444
24739
  streamToDigestAsync,
24445
24740
  streamWithExtendedPayload,
24446
24741
  traverseUpDirectories,
24742
+ validateEnvWrapperSupport,
24743
+ validateLambdaSize,
24447
24744
  validateNpmrc,
24745
+ validateUncompressedLambdaSize,
24448
24746
  walkParentDirs
24449
24747
  });
24450
24748
  /*! Bundled license information:
24451
24749
 
24750
+ bytes/index.js:
24751
+ (*!
24752
+ * bytes
24753
+ * Copyright(c) 2012-2014 TJ Holowaychuk
24754
+ * Copyright(c) 2015 Jed Watson
24755
+ * MIT Licensed
24756
+ *)
24757
+
24452
24758
  smol-toml/dist/error.js:
24453
24759
  (*!
24454
24760
  * Copyright (c) Squirrel Chat et al., All rights reserved.
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import { type BytecodeCachingOptions } from './get-lambda-preload-scripts';
3
2
  interface LambdaLike {
4
3
  awsLambdaHandler?: string;
@@ -14,5 +13,7 @@ interface LambdaLike {
14
13
  * Buffer is required just to determine if Bytecode Caching should be enabled
15
14
  * but it doesn't need to be super precise.
16
15
  */
17
- export declare function getLambdaEnvironment(lambda: LambdaLike, buffer: Buffer, options: BytecodeCachingOptions): Record<string, string>;
16
+ export declare function getLambdaEnvironment(lambda: LambdaLike, buffer: {
17
+ byteLength: number;
18
+ }, options: BytecodeCachingOptions): Record<string, string>;
18
19
  export {};
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  export interface BytecodeCachingOptions {
3
2
  vercelEnv: string | undefined;
4
3
  useBytecodeCaching: string | undefined;
@@ -17,5 +16,7 @@ interface LambdaLike {
17
16
  * The `buffer` parameter is needed to decide wether or not to enable Bytecode
18
17
  * Caching so it doesn't **need** to be exact (we can leave out the env layer)
19
18
  */
20
- export declare function getLambdaPreloadScripts(lambda: LambdaLike, buffer: Buffer, options: BytecodeCachingOptions): string[];
19
+ export declare function getLambdaPreloadScripts(lambda: LambdaLike, buffer: {
20
+ byteLength: number;
21
+ }, options: BytecodeCachingOptions): string[];
21
22
  export {};
@@ -0,0 +1,46 @@
1
+ /// <reference types="node" />
2
+ import { NowBuildError } from './errors';
3
+ /**
4
+ * Max compressed ZIP size (300 MB).
5
+ * Limit is 250 MB uncompressed; we set 300 MB compressed as a safety
6
+ * buffer. Python is exempt because AI workloads commonly exceed this.
7
+ */
8
+ export declare const MAX_LAMBDA_SIZE: number;
9
+ /**
10
+ * Max uncompressed size (250 MB).
11
+ */
12
+ export declare const MAX_LAMBDA_UNCOMPRESSED_SIZE: number;
13
+ /**
14
+ * Error thrown when a Lambda's compressed ZIP exceeds the allowed size.
15
+ */
16
+ export declare class FunctionSizeError extends NowBuildError {
17
+ size: number;
18
+ maxSize: number;
19
+ constructor(outputPath: string, size: number);
20
+ }
21
+ /**
22
+ * Validates the compressed size of a Lambda function.
23
+ * Python runtimes are exempt because AI workloads commonly exceed 300 MB.
24
+ */
25
+ export declare function validateLambdaSize(outputPath: string, runtime: string, size: number): void;
26
+ /**
27
+ * Validates the uncompressed size of a Lambda function.
28
+ */
29
+ export declare function validateUncompressedLambdaSize(outputPath: string, uncompressedBytes: number): void;
30
+ /**
31
+ * Runtimes that support env wrapper.
32
+ */
33
+ export declare const ENV_WRAPPER_SUPPORTED_FAMILIES: string[];
34
+ interface LambdaLikeForEnvWrapper {
35
+ createZip?: () => Promise<Buffer>;
36
+ runtime: string;
37
+ supportsWrapper?: boolean;
38
+ }
39
+ /**
40
+ * When the function requires a file for the encrypted environment variables,
41
+ * it needs to support wrappers. Also, the function must have a `createZip`
42
+ * function since we need to "re-compress" to include the file in the final
43
+ * lambda.
44
+ */
45
+ export declare function validateEnvWrapperSupport(encryptedEnvFilename: string | undefined, encryptedEnvContent: string | undefined, lambda: LambdaLikeForEnvWrapper): void;
46
+ export {};
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var validate_lambda_size_exports = {};
30
+ __export(validate_lambda_size_exports, {
31
+ ENV_WRAPPER_SUPPORTED_FAMILIES: () => ENV_WRAPPER_SUPPORTED_FAMILIES,
32
+ FunctionSizeError: () => FunctionSizeError,
33
+ MAX_LAMBDA_SIZE: () => MAX_LAMBDA_SIZE,
34
+ MAX_LAMBDA_UNCOMPRESSED_SIZE: () => MAX_LAMBDA_UNCOMPRESSED_SIZE,
35
+ validateEnvWrapperSupport: () => validateEnvWrapperSupport,
36
+ validateLambdaSize: () => validateLambdaSize,
37
+ validateUncompressedLambdaSize: () => validateUncompressedLambdaSize
38
+ });
39
+ module.exports = __toCommonJS(validate_lambda_size_exports);
40
+ var import_errors = require("./errors");
41
+ var import_bytes = __toESM(require("bytes"));
42
+ const MAX_LAMBDA_SIZE = (0, import_bytes.default)("300mb");
43
+ const MAX_LAMBDA_UNCOMPRESSED_SIZE = 250 * 1024 * 1024;
44
+ class FunctionSizeError extends import_errors.NowBuildError {
45
+ constructor(outputPath, size) {
46
+ super({
47
+ code: "NOW_SANDBOX_WORKER_MAX_LAMBDA_SIZE",
48
+ message: `The Vercel Function "${outputPath}" is ${(0, import_bytes.default)(
49
+ size
50
+ ).toLowerCase()} which exceeds the maximum size limit of ${(0, import_bytes.default)(
51
+ MAX_LAMBDA_SIZE
52
+ ).toLowerCase()}.`,
53
+ link: "https://vercel.link/serverless-function-size",
54
+ action: "Learn More"
55
+ });
56
+ this.size = size;
57
+ this.maxSize = MAX_LAMBDA_SIZE;
58
+ }
59
+ }
60
+ function validateLambdaSize(outputPath, runtime, size) {
61
+ if (runtime.startsWith("python")) {
62
+ return;
63
+ }
64
+ if (size > MAX_LAMBDA_SIZE) {
65
+ throw new FunctionSizeError(outputPath, size);
66
+ }
67
+ }
68
+ function validateUncompressedLambdaSize(outputPath, uncompressedBytes) {
69
+ if (uncompressedBytes >= MAX_LAMBDA_UNCOMPRESSED_SIZE) {
70
+ throw new import_errors.NowBuildError({
71
+ code: "NOW_SANDBOX_WORKER_MAX_UNCOMPRESSED_LAMBDA_SIZE",
72
+ message: `The Vercel Function "${outputPath}" is ${(0, import_bytes.default)(
73
+ uncompressedBytes
74
+ ).toLowerCase()} uncompressed which exceeds the maximum uncompressed size limit of ${(0, import_bytes.default)(
75
+ MAX_LAMBDA_UNCOMPRESSED_SIZE
76
+ ).toLowerCase()}.`,
77
+ link: "https://vercel.link/serverless-function-size",
78
+ action: "Learn More"
79
+ });
80
+ }
81
+ }
82
+ const ENV_WRAPPER_SUPPORTED_FAMILIES = [
83
+ "nodejs",
84
+ "python",
85
+ "ruby",
86
+ "java",
87
+ "dotnetcore",
88
+ "bun",
89
+ "executable"
90
+ ];
91
+ function validateEnvWrapperSupport(encryptedEnvFilename, encryptedEnvContent, lambda) {
92
+ if (!encryptedEnvFilename || !encryptedEnvContent) {
93
+ return;
94
+ }
95
+ if (!lambda.supportsWrapper && !ENV_WRAPPER_SUPPORTED_FAMILIES.some(
96
+ (family) => lambda.runtime.startsWith(family)
97
+ )) {
98
+ throw new Error(
99
+ `Serverless Function runtime ${lambda.runtime} does not support more than 4KB for environment variables`
100
+ );
101
+ }
102
+ if (typeof lambda.createZip !== "function") {
103
+ throw new Error(
104
+ `Serverless Function with runtime ${lambda.runtime} has no createZip function`
105
+ );
106
+ }
107
+ }
108
+ // Annotate the CommonJS export names for ESM import in node:
109
+ 0 && (module.exports = {
110
+ ENV_WRAPPER_SUPPORTED_FAMILIES,
111
+ FunctionSizeError,
112
+ MAX_LAMBDA_SIZE,
113
+ MAX_LAMBDA_UNCOMPRESSED_SIZE,
114
+ validateEnvWrapperSupport,
115
+ validateLambdaSize,
116
+ validateUncompressedLambdaSize
117
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/build-utils",
3
- "version": "13.12.1",
3
+ "version": "13.13.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.js",
@@ -16,8 +16,10 @@
16
16
  "@vercel/python-analysis": "0.11.0"
17
17
  },
18
18
  "devDependencies": {
19
+ "bytes": "3.1.2",
19
20
  "smol-toml": "1.5.2",
20
21
  "@types/async-retry": "^1.2.1",
22
+ "@types/bytes": "3.1.1",
21
23
  "@types/cross-spawn": "6.0.0",
22
24
  "@types/end-of-stream": "^1.4.0",
23
25
  "@types/fs-extra": "9.0.13",