Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@vercel/build-utils

Package Overview
Dependencies
Maintainers
2
Versions
399
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vercel/build-utils - npm Package Compare versions

Comparing version
13.4.0-canary.20260211174907.cdd2da6
to
13.4.0
+15
-0
CHANGELOG.md
# @vercel/build-utils
## 13.4.0
### Minor Changes
- [services] synchronize dependencies in dev mode for JS/TS and Python services ([#14987](https://github.com/vercel/vercel/pull/14987))
- [services] inject service URLs into web services as local paths ([#15024](https://github.com/vercel/vercel/pull/15024))
### Patch Changes
- Add new expirementalTrigger format for queues v2beta ([#14970](https://github.com/vercel/vercel/pull/14970))
- Updated dependencies [[`a960cf23a42ff1a570c808ee9567670c24422f98`](https://github.com/vercel/vercel/commit/a960cf23a42ff1a570c808ee9567670c24422f98)]:
- @vercel/python-analysis@0.4.1
## 13.3.5

@@ -4,0 +19,0 @@

+22
-1
/// <reference types="node" />
/// <reference types="node" />
import { SpawnOptions } from 'child_process';

@@ -76,3 +77,23 @@ import { Meta, PackageJson, NodeVersion, Config, BunVersion } from '../types';

ignoreNon0Exit?: boolean;
/**
* Writable stream to pipe stdout to (e.g., for prefixing output in multi-service mode).
* When provided, stdio is automatically set to 'pipe'.
*/
outputStream?: NodeJS.WritableStream;
/**
* Writable stream to pipe stderr to (e.g., for prefixing output in multi-service mode).
* When provided, stdio is automatically set to 'pipe'.
*/
errorStream?: NodeJS.WritableStream;
}
export interface NpmInstallOutput {
/**
* Writable stream for stdout (e.g., for prefixing output in multi-service mode)
*/
stdout?: NodeJS.WritableStream;
/**
* Writable stream for stderr (e.g., for prefixing output in multi-service mode)
*/
stderr?: NodeJS.WritableStream;
}
export declare function spawnAsync(command: string, args: string[], opts?: SpawnOptionsExtended): Promise<void>;

@@ -115,3 +136,3 @@ export declare function spawnCommand(command: string, options?: SpawnOptions): import("child_process").ChildProcess;

export declare function resetCustomInstallCommandSet(): void;
export declare function runNpmInstall(destPath: string, args?: string[], spawnOpts?: SpawnOptions, meta?: Meta, projectCreatedAt?: number): Promise<boolean>;
export declare function runNpmInstall(destPath: string, args?: string[], spawnOpts?: SpawnOptions, meta?: Meta, projectCreatedAt?: number, output?: NpmInstallOutput): Promise<boolean>;
/**

@@ -118,0 +139,0 @@ * Prepares the input environment based on the used package manager and lockfile

+27
-7

@@ -82,5 +82,17 @@ "use strict";

const stderrLogs = [];
opts = { stdio: "inherit", ...opts };
const hasCustomStreams = opts.outputStream || opts.errorStream;
if (hasCustomStreams) {
opts = { ...opts, stdio: ["inherit", "pipe", "pipe"] };
} else {
opts = { stdio: "inherit", ...opts };
}
const child = (0, import_cross_spawn.default)(command, args, opts);
if (opts.stdio === "pipe" && child.stderr) {
if (hasCustomStreams) {
if (child.stdout && opts.outputStream) {
child.stdout.pipe(opts.outputStream);
}
if (child.stderr && opts.errorStream) {
child.stderr.pipe(opts.errorStream);
}
} else if (opts.stdio === "pipe" && child.stderr) {
child.stderr.on("data", (data) => stderrLogs.push(data));

@@ -97,3 +109,3 @@ }

code: `BUILD_UTILS_SPAWN_${code || signal}`,
message: opts.stdio === "inherit" ? `${cmd} exited with ${code || signal}` : stderrLogs.map((line) => line.toString()).join("")
message: opts.stdio === "inherit" || hasCustomStreams ? `${cmd} exited with ${code || signal}` : stderrLogs.map((line) => line.toString()).join("")
})

@@ -523,3 +535,4 @@ );

args,
opts
opts,
output
}) {

@@ -531,2 +544,4 @@ const { commandArguments, prettyCommand } = getInstallCommandForPackageManager(packageManager, args);

}
opts.outputStream = output?.stdout;
opts.errorStream = output?.stderr;
await spawnAsync(packageManager, commandArguments, opts);

@@ -551,3 +566,3 @@ }

}
async function runNpmInstall(destPath, args = [], spawnOpts, meta, projectCreatedAt) {
async function runNpmInstall(destPath, args = [], spawnOpts, meta, projectCreatedAt, output) {
if (meta?.isDev) {

@@ -604,3 +619,7 @@ (0, import_debug.default)("Skipping dependency installation because dev mode is enabled");

const installTime = Date.now();
console.log("Installing dependencies...");
if (output?.stdout) {
output.stdout.write("Installing dependencies...\n");
} else {
console.log("Installing dependencies...");
}
(0, import_debug.default)(`Installing to ${destPath}`);

@@ -628,3 +647,4 @@ const opts = { cwd: destPath, ...spawnOpts };

args,
opts
opts,
output
});

@@ -631,0 +651,0 @@ (0, import_debug.default)(`Install complete [${Date.now() - installTime}ms]`);

@@ -14,2 +14,3 @@ import type { Service } from './types';

deploymentUrl?: string;
origin?: string;
}

@@ -16,0 +17,0 @@ /**

@@ -27,8 +27,11 @@ "use strict";

}
function computeServiceUrl(deploymentUrl, routePrefix) {
function computeServiceUrl(baseUrl, routePrefix, isOrigin) {
if (!isOrigin) {
baseUrl = `https://${baseUrl}`;
}
if (routePrefix === "/") {
return `https://${deploymentUrl}`;
return baseUrl;
}
const normalizedPrefix = routePrefix.startsWith("/") ? routePrefix.slice(1) : routePrefix;
return `https://${deploymentUrl}/${normalizedPrefix}`;
return `${baseUrl}/${normalizedPrefix}`;
}

@@ -44,4 +47,11 @@ function getFrameworkEnvPrefix(frameworkSlug, frameworkList) {

function getServiceUrlEnvVars(options) {
const { services, frameworkList, currentEnv = {}, deploymentUrl } = options;
if (!deploymentUrl || !services || services.length === 0) {
const {
services,
frameworkList,
currentEnv = {},
deploymentUrl,
origin
} = options;
const baseUrl = origin || deploymentUrl;
if (!baseUrl || !services || services.length === 0) {
return {};

@@ -62,3 +72,7 @@ }

const baseEnvVarName = serviceNameToEnvVar(service.name);
const absoluteUrl = computeServiceUrl(deploymentUrl, service.routePrefix);
const absoluteUrl = computeServiceUrl(
baseUrl,
service.routePrefix,
!!origin
);
if (!(baseEnvVarName in currentEnv)) {

@@ -65,0 +79,0 @@ envVars[baseEnvVarName] = absoluteUrl;

import FileBlob from './file-blob';
import FileFsRef from './file-fs-ref';
import FileRef from './file-ref';
import { Lambda, createLambda, getLambdaOptionsFromFunction } from './lambda';
import { Lambda, createLambda, getLambdaOptionsFromFunction, sanitizeConsumerName } from './lambda';
import { NodejsLambda } from './nodejs-lambda';

@@ -11,3 +11,3 @@ import { Prerender } from './prerender';

import rename from './fs/rename';
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';
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, NpmInstallOutput } from './fs/run-user-scripts';
import { getLatestNodeVersion, getDiscontinuedNodeVersions, getSupportedNodeVersion, isBunVersion, getSupportedBunVersion } from './fs/node-version';

@@ -23,3 +23,3 @@ import streamToBuffer, { streamToBufferChunks } from './fs/stream-to-buffer';

import { validateNpmrc } from './validate-npmrc';
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, getServiceUrlEnvVars, streamToBuffer, streamToBufferChunks, debug, isSymbolicLink, isDirectory, getLambdaOptionsFromFunction, scanParentDirs, findPackageJson, getIgnoreFilter, cloneEnv, hardLinkDir, traverseUpDirectories, validateNpmrc, };
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, streamToBuffer, streamToBufferChunks, debug, isSymbolicLink, isDirectory, getLambdaOptionsFromFunction, sanitizeConsumerName, scanParentDirs, findPackageJson, getIgnoreFilter, cloneEnv, hardLinkDir, traverseUpDirectories, validateNpmrc, };
export { EdgeFunction } from './edge-function';

@@ -26,0 +26,0 @@ export { readConfigFile, getPackageJson } from './fs/read-config-file';

/// <reference types="node" />
import type { Config, Env, Files, FunctionFramework, TriggerEvent } from './types';
export type { TriggerEvent };
import type { Config, Env, Files, FunctionFramework, TriggerEvent, TriggerEventInput } from './types';
export type { TriggerEvent, TriggerEventInput };
/**
* Encodes a function path into a valid consumer name using mnemonic escapes.
* For queue/v2beta triggers, the consumer name is derived from the function path.
* This encoding is collision-free (bijective/reversible).
*
* Encoding scheme:
* - `_` → `__` (escape character itself)
* - `/` → `_S` (slash)
* - `.` → `_D` (dot)
* - Other invalid chars → `_XX` (hex code)
*
* @example
* sanitizeConsumerName('api/test.js') // => 'api_Stest_Djs'
* sanitizeConsumerName('api/users/handler.ts') // => 'api_Susers_Shandler_Dts'
* sanitizeConsumerName('my_func.ts') // => 'my__func_Dts'
*/
export declare function sanitizeConsumerName(functionPath: string): string;
export type LambdaOptions = LambdaOptionsWithFiles | LambdaOptionsWithZipBuffer;

@@ -5,0 +22,0 @@ export type LambdaExecutableRuntimeLanguages = 'rust' | 'go';

@@ -34,3 +34,4 @@ "use strict";

createZip: () => createZip,
getLambdaOptionsFromFunction: () => getLambdaOptionsFromFunction
getLambdaOptionsFromFunction: () => getLambdaOptionsFromFunction,
sanitizeConsumerName: () => sanitizeConsumerName
});

@@ -45,2 +46,19 @@ module.exports = __toCommonJS(lambda_exports);

var import_stream_to_buffer = __toESM(require("./fs/stream-to-buffer"));
function sanitizeConsumerName(functionPath) {
let result = "";
for (const char of functionPath) {
if (char === "_") {
result += "__";
} else if (char === "/") {
result += "_S";
} else if (char === ".") {
result += "_D";
} else if (/[A-Za-z0-9-]/.test(char)) {
result += char;
} else {
result += "_" + char.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0");
}
}
return result;
}
function getDefaultLambdaArchitecture(architecture) {

@@ -167,4 +185,4 @@ if (architecture) {

(0, import_assert.default)(
trigger.type === "queue/v1beta",
`${prefix}.type must be "queue/v1beta"`
trigger.type === "queue/v1beta" || trigger.type === "queue/v2beta",
`${prefix}.type must be "queue/v1beta" or "queue/v2beta"`
);

@@ -184,2 +202,8 @@ (0, import_assert.default)(

);
if (trigger.type === "queue/v2beta") {
(0, import_assert.default)(
experimentalTriggers.length === 1,
'"experimentalTriggers" can only have one item for queue/v2beta'
);
}
if (trigger.maxDeliveries !== void 0) {

@@ -325,2 +349,13 @@ (0, import_assert.default)(

if (sourceFile === pattern || (0, import_minimatch.default)(sourceFile, pattern)) {
const experimentalTriggers = fn.experimentalTriggers?.map(
(trigger) => {
if (trigger.type === "queue/v2beta") {
return {
...trigger,
consumer: sanitizeConsumerName(pattern)
};
}
return trigger;
}
);
return {

@@ -331,3 +366,3 @@ architecture: fn.architecture,

regions: fn.regions,
experimentalTriggers: fn.experimentalTriggers,
experimentalTriggers,
supportsCancellation: fn.supportsCancellation

@@ -345,3 +380,4 @@ };

createZip,
getLambdaOptionsFromFunction
getLambdaOptionsFromFunction,
sanitizeConsumerName
});

@@ -45,35 +45,33 @@ export declare const functionsSchema: {

items: {
type: string;
properties: {
type: {
type: string;
const: string;
oneOf: {
type: string;
properties: {
type: {
type: string;
const: string;
};
topic: {
type: string;
minLength: number;
};
maxDeliveries: {
type: string;
minimum: number;
};
retryAfterSeconds: {
type: string;
exclusiveMinimum: number;
};
initialDelaySeconds: {
type: string;
minimum: number;
};
maxConcurrency: {
type: string;
minimum: number;
};
};
topic: {
type: string;
minLength: number;
};
consumer: {
type: string;
minLength: number;
};
maxDeliveries: {
type: string;
minimum: number;
};
retryAfterSeconds: {
type: string;
exclusiveMinimum: number;
};
initialDelaySeconds: {
type: string;
minimum: number;
};
maxConcurrency: {
type: string;
minimum: number;
};
};
required: string[];
additionalProperties: boolean;
required: string[];
additionalProperties: boolean;
}[];
};

@@ -80,0 +78,0 @@ };

@@ -25,3 +25,3 @@ "use strict";

module.exports = __toCommonJS(schemas_exports);
const triggerEventSchema = {
const triggerEventSchemaV1 = {
type: "object",

@@ -61,2 +61,36 @@ properties: {

};
const triggerEventSchemaV2 = {
type: "object",
properties: {
type: {
type: "string",
const: "queue/v2beta"
},
topic: {
type: "string",
minLength: 1
},
maxDeliveries: {
type: "number",
minimum: 1
},
retryAfterSeconds: {
type: "number",
exclusiveMinimum: 0
},
initialDelaySeconds: {
type: "number",
minimum: 0
},
maxConcurrency: {
type: "number",
minimum: 1
}
},
required: ["type", "topic"],
additionalProperties: false
};
const triggerEventSchema = {
oneOf: [triggerEventSchemaV1, triggerEventSchemaV2]
};
const functionsSchema = {

@@ -63,0 +97,0 @@ type: "object",

@@ -361,3 +361,3 @@ /// <reference types="node" />

excludeFiles?: string;
experimentalTriggers?: TriggerEvent[];
experimentalTriggers?: TriggerEventInput[];
supportsCancellation?: boolean;

@@ -563,13 +563,5 @@ };

}
/**
* Queue trigger event for Vercel's queue system.
* Handles "queue/v1beta" events with queue-specific configuration.
*/
export interface TriggerEvent {
/** Event type - must be "queue/v1beta" (REQUIRED) */
type: 'queue/v1beta';
interface TriggerEventBase {
/** Name of the queue topic to consume from (REQUIRED) */
topic: string;
/** Name of the consumer group for this trigger (REQUIRED) */
consumer: string;
/**

@@ -600,2 +592,36 @@ * Maximum number of delivery attempts for message processing (OPTIONAL)

}
/**
* Queue trigger input event for v1beta (from vercel.json config).
* Requires explicit consumer name.
*/
export interface TriggerEventInputV1 extends TriggerEventBase {
/** Event type - must be "queue/v1beta" (REQUIRED) */
type: 'queue/v1beta';
/** Name of the consumer group for this trigger (REQUIRED) */
consumer: string;
}
/**
* Queue trigger input event for v2beta (from vercel.json config).
* Consumer name is implicitly derived from the function path.
* Only one trigger per function is allowed.
*/
export interface TriggerEventInputV2 extends TriggerEventBase {
/** Event type - must be "queue/v2beta" (REQUIRED) */
type: 'queue/v2beta';
}
/**
* Queue trigger input event from vercel.json config.
* v1beta requires explicit consumer, v2beta derives consumer from function path.
*/
export type TriggerEventInput = TriggerEventInputV1 | TriggerEventInputV2;
/**
* Processed queue trigger event for Lambda.
* Consumer is always present (explicitly provided for v1beta, derived for v2beta).
*/
export interface TriggerEvent extends TriggerEventBase {
/** Event type */
type: 'queue/v1beta' | 'queue/v2beta';
/** Name of the consumer group for this trigger (always present in processed output) */
consumer: string;
}
export type ServiceRuntime = 'node' | 'python' | 'go' | 'rust' | 'ruby';

@@ -602,0 +628,0 @@ export type ServiceType = 'web' | 'cron' | 'worker';

{
"name": "@vercel/build-utils",
"version": "13.4.0-canary.20260211174907.cdd2da6",
"version": "13.4.0",
"license": "Apache-2.0",

@@ -14,3 +14,3 @@ "main": "./dist/index.js",

"dependencies": {
"@vercel/python-analysis": "0.5.0-canary.20260211174907.cdd2da6"
"@vercel/python-analysis": "0.4.1"
},

@@ -54,4 +54,4 @@ "devDependencies": {

"json5": "2.2.3",
"@vercel/error-utils": "2.1.0-canary.20260211174907.cdd2da6",
"@vercel/routing-utils": "5.4.0-canary.20260211174907.cdd2da6"
"@vercel/routing-utils": "5.3.2",
"@vercel/error-utils": "2.0.3"
},

@@ -58,0 +58,0 @@ "scripts": {

Sorry, the diff of this file is too big to display