@vercel/build-utils 13.12.2 → 13.14.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.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Support configuration via vercel.toml ([#15750](https://github.com/vercel/vercel/pull/15750))
8
+
9
+ ## 13.13.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 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))
14
+
3
15
  ## 13.12.2
4
16
 
5
17
  ### Patch Changes
@@ -7,7 +7,26 @@ import type { SupportsStreamingResult } from './process-serverless/get-lambda-su
7
7
  * Optional wrapper around async work, allowing callers to inject tracing
8
8
  * (e.g. dd-trace spans) without coupling the shared code to a tracer.
9
9
  */
10
- export type TraceFn = <T>(name: string, fn: () => Promise<T>) => Promise<T>;
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>;
11
30
  export interface FinalizeLambdaParams {
12
31
  lambda: Lambda | NodejsLambda;
13
32
  encryptedEnvFilename?: string;
@@ -18,10 +37,28 @@ export interface FinalizeLambdaParams {
18
37
  enableUncompressedLambdaSizeCheck?: boolean;
19
38
  /** Optional tracing wrapper for `collectUncompressedSize` and `createZip`. */
20
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;
21
52
  }
22
53
  export interface FinalizeLambdaResult {
23
- buffer: Buffer;
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. */
24
59
  digest: string;
60
+ /** Compressed size in bytes. */
61
+ size: number;
25
62
  uncompressedBytes: number;
26
63
  /** Non-fatal streaming detection error, if any. Caller decides how to log. */
27
64
  streamingError?: SupportsStreamingResult['error'];
@@ -32,10 +69,11 @@ export interface FinalizeLambdaResult {
32
69
  * This function:
33
70
  * 1. Injects encrypted env file into lambda.files when provided
34
71
  * 2. Collects uncompressed size when enabled
35
- * 3. Creates the ZIP buffer
36
- * 4. Computes SHA-256 digest
37
- * 5. Merges environment variables (bytecode caching, helpers, etc.)
38
- * 6. Detects streaming support
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
39
77
  *
40
78
  * Note: This function mutates the `lambda` (files, environment,
41
79
  * supportsResponseStreaming).
@@ -35,7 +35,9 @@ async function finalizeLambda(params) {
35
35
  bytecodeCachingOptions,
36
36
  forceStreamingRuntime,
37
37
  enableUncompressedLambdaSizeCheck,
38
- trace = defaultTrace
38
+ trace = defaultTrace,
39
+ createZip: createZipOverride,
40
+ validateZip
39
41
  } = params;
40
42
  const encryptedEnv = (0, import_get_encrypted_env_file.getEncryptedEnv)(
41
43
  encryptedEnvFilename,
@@ -58,11 +60,43 @@ async function finalizeLambda(params) {
58
60
  );
59
61
  }
60
62
  }
61
- const buffer = lambda.zipBuffer || await trace("createZip", () => lambda.createZip());
62
- const digest = (0, import_stream_to_digest_async.sha256)(buffer);
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
+ }
63
93
  lambda.environment = {
64
94
  ...lambda.environment,
65
- ...(0, import_get_lambda_environment.getLambdaEnvironment)(lambda, buffer, bytecodeCachingOptions)
95
+ ...(0, import_get_lambda_environment.getLambdaEnvironment)(
96
+ lambda,
97
+ zipResult.buffer ?? { byteLength: zipResult.size },
98
+ bytecodeCachingOptions
99
+ )
66
100
  };
67
101
  const streamingResult = await (0, import_get_lambda_supports_streaming.getLambdaSupportsStreaming)(
68
102
  lambda,
@@ -70,8 +104,10 @@ async function finalizeLambda(params) {
70
104
  );
71
105
  lambda.supportsResponseStreaming = streamingResult.supportsStreaming;
72
106
  return {
73
- buffer,
74
- digest,
107
+ buffer: zipResult.buffer,
108
+ zipPath: zipResult.zipPath,
109
+ digest: zipResult.digest,
110
+ size: zipResult.size,
75
111
  uncompressedBytes,
76
112
  streamingError: streamingResult.error
77
113
  };
@@ -83,7 +83,7 @@ async function get_ignore_filter_default(downloadPath, rootDirectory) {
83
83
  }
84
84
  const ignoreFilter = (0, import_ignore.default)().add(clearRelative(ignoreContents[0]));
85
85
  return function(p) {
86
- if (p === "now.json" || p === "vercel.json")
86
+ if (p === "now.json" || p === "vercel.json" || p === "vercel.toml")
87
87
  return false;
88
88
  return ignoreFilter.test(p).ignored;
89
89
  };
package/dist/index.d.ts CHANGED
@@ -47,5 +47,5 @@ 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
49
  export { collectUncompressedSize } from './collect-uncompressed-size';
50
- export { finalizeLambda, type FinalizeLambdaParams, type FinalizeLambdaResult, type TraceFn, } from './finalize-lambda';
50
+ export { finalizeLambda, type CreateZipResult, type CreateZipFn, type FinalizeLambdaParams, type FinalizeLambdaResult, type TraceFn, } from './finalize-lambda';
51
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
@@ -23233,7 +23233,7 @@ async function get_ignore_filter_default(downloadPath, rootDirectory) {
23233
23233
  }
23234
23234
  const ignoreFilter = (0, import_ignore.default)().add(clearRelative(ignoreContents[0]));
23235
23235
  return function(p) {
23236
- if (p === "now.json" || p === "vercel.json")
23236
+ if (p === "now.json" || p === "vercel.json" || p === "vercel.toml")
23237
23237
  return false;
23238
23238
  return ignoreFilter.test(p).ignored;
23239
23239
  };
@@ -24486,7 +24486,9 @@ async function finalizeLambda(params) {
24486
24486
  bytecodeCachingOptions,
24487
24487
  forceStreamingRuntime,
24488
24488
  enableUncompressedLambdaSizeCheck,
24489
- trace = defaultTrace
24489
+ trace = defaultTrace,
24490
+ createZip: createZipOverride,
24491
+ validateZip
24490
24492
  } = params;
24491
24493
  const encryptedEnv = getEncryptedEnv(
24492
24494
  encryptedEnvFilename,
@@ -24509,11 +24511,43 @@ async function finalizeLambda(params) {
24509
24511
  );
24510
24512
  }
24511
24513
  }
24512
- const buffer = lambda.zipBuffer || await trace("createZip", () => lambda.createZip());
24513
- const digest = sha256(buffer);
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
+ }
24514
24544
  lambda.environment = {
24515
24545
  ...lambda.environment,
24516
- ...getLambdaEnvironment(lambda, buffer, bytecodeCachingOptions)
24546
+ ...getLambdaEnvironment(
24547
+ lambda,
24548
+ zipResult.buffer ?? { byteLength: zipResult.size },
24549
+ bytecodeCachingOptions
24550
+ )
24517
24551
  };
24518
24552
  const streamingResult = await getLambdaSupportsStreaming(
24519
24553
  lambda,
@@ -24521,8 +24555,10 @@ async function finalizeLambda(params) {
24521
24555
  );
24522
24556
  lambda.supportsResponseStreaming = streamingResult.supportsStreaming;
24523
24557
  return {
24524
- buffer,
24525
- digest,
24558
+ buffer: zipResult.buffer,
24559
+ zipPath: zipResult.zipPath,
24560
+ digest: zipResult.digest,
24561
+ size: zipResult.size,
24526
24562
  uncompressedBytes,
24527
24563
  streamingError: streamingResult.error
24528
24564
  };
@@ -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 {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/build-utils",
3
- "version": "13.12.2",
3
+ "version": "13.14.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.js",