@vercel/build-utils
Advanced tools
| import type FileFsRef from '../file-fs-ref'; | ||
| import type { Lambda } from '../lambda'; | ||
| import type { Prerender } from '../prerender'; | ||
| import type { BuildResultV2Typical, Files } from '../types'; | ||
| import type { SerializedEdgeFunction, SerializedLambda, SerializedNodejsLambda, SerializedPrerender } from './serialized-types'; | ||
| export interface DeserializeBuildOutputPathOverride { | ||
| contentType?: string; | ||
| mode?: number; | ||
| path?: string; | ||
| } | ||
| export interface DeserializeBuildOutputConfig<TFlags = unknown> { | ||
| version?: 3; | ||
| wildcard?: BuildResultV2Typical['wildcard']; | ||
| images?: BuildResultV2Typical['images']; | ||
| routes?: BuildResultV2Typical['routes']; | ||
| overrides?: Record<string, DeserializeBuildOutputPathOverride>; | ||
| framework?: BuildResultV2Typical['framework']; | ||
| crons?: BuildResultV2Typical['crons']; | ||
| flags?: TFlags; | ||
| deploymentId?: string; | ||
| } | ||
| export type DeserializeBuildOutputResult<TFlags = unknown, TMeta = unknown> = Omit<BuildResultV2Typical, 'flags'> & { | ||
| flags?: TFlags; | ||
| meta?: TMeta; | ||
| }; | ||
| export type DeserializeBuildOutputLambdaOptions = { | ||
| forceNodejsStreaming?: boolean; | ||
| useOnlyStreamingLambda?: boolean; | ||
| }; | ||
| export type GroupLambdasOptions = { | ||
| force: 'all' | undefined; | ||
| maxBundleSizeMb: number | undefined; | ||
| debug: boolean | undefined; | ||
| }; | ||
| export type DeserializeBuildOutputLambda<TLambda extends Lambda> = (files: Files, config: SerializedLambda | SerializedNodejsLambda, repoRootPath: string, fileFsRefsCache: Map<string, FileFsRef>, options?: DeserializeBuildOutputLambdaOptions) => Promise<TLambda>; | ||
| export type GroupLambdas<TLambda extends Lambda> = (lambdas: Record<string, TLambda>, options: GroupLambdasOptions) => Promise<Record<string, TLambda>>; | ||
| export type InspectSerializedLambda = (path: string, config: SerializedLambda | SerializedNodejsLambda, repoRootPath: string, hasServerActions: boolean) => Promise<boolean>; | ||
| export interface DeserializeBuildOutputOptions<TResult extends DeserializeBuildOutputResult = DeserializeBuildOutputResult, TLambda extends Lambda = Lambda> { | ||
| outputDir: string; | ||
| repoRootPath: string; | ||
| maxBundleSizeMb?: number; | ||
| debugGroupLambdas?: boolean; | ||
| useOnlyStreamingLambda?: boolean; | ||
| forceNodejsStreaming?: boolean; | ||
| deserializeLambda: DeserializeBuildOutputLambda<TLambda>; | ||
| groupLambdas: GroupLambdas<TLambda>; | ||
| inspectSerializedLambda?: InspectSerializedLambda; | ||
| warn?: (message: string) => void; | ||
| includeDeploymentId?: boolean; | ||
| getMeta?: (hasServerActions: boolean) => TResult extends { | ||
| meta?: infer TMeta; | ||
| } ? TMeta : never; | ||
| } | ||
| export type DeserializeBuildOutputFiles = BuildResultV2Typical['output']; | ||
| export type DeserializeBuildOutputPrerenderFallback = Prerender['fallback']; | ||
| export type DeserializeBuildOutputSerializedConfig = SerializedEdgeFunction | SerializedLambda | SerializedNodejsLambda; | ||
| export type DeserializeBuildOutputSerializedPrerender = SerializedPrerender; |
| "use strict"; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| var deserialize_build_output_types_exports = {}; | ||
| module.exports = __toCommonJS(deserialize_build_output_types_exports); |
| import type { Lambda } from '../lambda'; | ||
| import type { DeserializeBuildOutputConfig, DeserializeBuildOutputOptions, DeserializeBuildOutputResult } from './deserialize-build-output-types'; | ||
| export declare function validateDeploymentId(deploymentId?: string): void; | ||
| export declare function deserializeBuildOutput<TConfig extends DeserializeBuildOutputConfig = DeserializeBuildOutputConfig, TResult extends DeserializeBuildOutputResult = DeserializeBuildOutputResult, TLambda extends Lambda = Lambda>(options: DeserializeBuildOutputOptions<TResult, TLambda>): Promise<TResult>; |
| "use strict"; | ||
| var __create = Object.create; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __getProtoOf = Object.getPrototypeOf; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
| // If the importer is in node compatibility mode or this is not an ESM | ||
| // file that has been converted to a CommonJS file using a Babel- | ||
| // compatible transform (i.e. "__esModule" has not been set), then set | ||
| // "default" to the CommonJS "module.exports" for node compatibility. | ||
| isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
| mod | ||
| )); | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| var deserialize_build_output_exports = {}; | ||
| __export(deserialize_build_output_exports, { | ||
| deserializeBuildOutput: () => deserializeBuildOutput, | ||
| validateDeploymentId: () => validateDeploymentId | ||
| }); | ||
| module.exports = __toCommonJS(deserialize_build_output_exports); | ||
| var fs = __toESM(require("fs-extra")); | ||
| var import_path = require("path"); | ||
| var import_errors = require("../errors"); | ||
| var import_file_fs_ref = __toESM(require("../file-fs-ref")); | ||
| var import_glob = __toESM(require("../fs/glob")); | ||
| var import_prerender = require("../prerender"); | ||
| var import_create_functions_iterator = require("./create-functions-iterator"); | ||
| var import_deserialize_edge_function = require("./deserialize-edge-function"); | ||
| var import_maybe_read_json = require("./maybe-read-json"); | ||
| var import_validate_framework_version = require("./validate-framework-version"); | ||
| const MAX_DEPLOYMENT_ID_LENGTH = 32; | ||
| const VALID_DEPLOYMENT_ID_PATTERN = /^[a-zA-Z0-9_-]+$/; | ||
| function validateDeploymentId(deploymentId) { | ||
| if (deploymentId && deploymentId.length > MAX_DEPLOYMENT_ID_LENGTH) { | ||
| throw new import_errors.NowBuildError({ | ||
| message: `The configured deploymentId "${deploymentId}" exceeds the maximum length of ${MAX_DEPLOYMENT_ID_LENGTH} characters. Please use a shorter deploymentId.`, | ||
| code: "VC_BUILD_INVALID_DEPLOYMENT_ID_LENGTH" | ||
| }); | ||
| } | ||
| if (deploymentId && !VALID_DEPLOYMENT_ID_PATTERN.test(deploymentId)) { | ||
| throw new import_errors.NowBuildError({ | ||
| message: `The configured deploymentId "${deploymentId}" contains invalid characters. Only alphanumeric characters (a-z, A-Z, 0-9), hyphens (-), and underscores (_) are allowed.`, | ||
| code: "VC_BUILD_INVALID_DEPLOYMENT_ID_CHARACTERS" | ||
| }); | ||
| } | ||
| } | ||
| function applyOutputOverrides(output, overrides, warn) { | ||
| for (const [name, override] of Object.entries(overrides || {})) { | ||
| const entry = output[name]; | ||
| if (entry) { | ||
| if (override.contentType) { | ||
| entry.contentType = override.contentType; | ||
| } | ||
| if (override.mode) { | ||
| entry.mode = override.mode; | ||
| } | ||
| if (override.path) { | ||
| output[override.path] = entry; | ||
| delete output[name]; | ||
| } | ||
| } else { | ||
| warn?.( | ||
| `Warning: Override path "${name}" was not detected as an output path` | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| async function deserializePrerenderFallback(prerenderConfigPath, fallbackConfig) { | ||
| if (typeof fallbackConfig === "string") { | ||
| return import_file_fs_ref.default.fromFsPath({ | ||
| fsPath: (0, import_path.join)((0, import_path.dirname)(prerenderConfigPath), fallbackConfig) | ||
| }); | ||
| } | ||
| if (fallbackConfig) { | ||
| return import_file_fs_ref.default.fromFsPath({ | ||
| mode: fallbackConfig.mode, | ||
| contentType: fallbackConfig.contentType, | ||
| fsPath: (0, import_path.join)((0, import_path.dirname)(prerenderConfigPath), fallbackConfig.fsPath) | ||
| }); | ||
| } | ||
| return null; | ||
| } | ||
| function applyFunctionSymlinks(output, prerenders, functionSymlinks) { | ||
| for (const [path, target] of functionSymlinks.entries()) { | ||
| const targetOutput = prerenders.get(target) || output[target]; | ||
| let targetFunction; | ||
| if (targetOutput?.type === "Prerender") { | ||
| targetFunction = targetOutput.lambda; | ||
| } else if (targetOutput?.type === "Lambda" || targetOutput?.type === "EdgeFunction") { | ||
| targetFunction = targetOutput; | ||
| } | ||
| if (!targetFunction) { | ||
| throw new Error( | ||
| `Could not find target "${target}" Lambda or EdgeFunction for path "${path}"` | ||
| ); | ||
| } | ||
| const srcOutput = prerenders.get(path); | ||
| if (srcOutput) { | ||
| if (srcOutput.type === "Prerender") { | ||
| if (targetFunction.type === "Lambda") { | ||
| srcOutput.lambda = targetFunction; | ||
| } else { | ||
| throw new Error( | ||
| `Unexpected function type "${targetFunction.type}" at path "${path}"` | ||
| ); | ||
| } | ||
| } else { | ||
| throw new Error( | ||
| `Unexpected output type "${srcOutput.type}" at path "${path}"` | ||
| ); | ||
| } | ||
| } else { | ||
| output[path] = targetFunction; | ||
| } | ||
| } | ||
| } | ||
| function appendSortedPrerenders(output, prerenders) { | ||
| const sortedPrerenders = Array.from(prerenders.entries()).sort((a, b) => { | ||
| return (a[1].group ?? 0) - (b[1].group ?? 0); | ||
| }).reduce((o, [path, prerender]) => { | ||
| o[path] = prerender; | ||
| return o; | ||
| }, {}); | ||
| Object.assign(output, sortedPrerenders); | ||
| } | ||
| function getBundleableLambdas(output) { | ||
| const bundleableLambdas = {}; | ||
| for (const [outputName, curOutput] of Object.entries(output)) { | ||
| if (curOutput.type === "Lambda" && curOutput.experimentalAllowBundling) { | ||
| bundleableLambdas[outputName] = curOutput; | ||
| } else if (curOutput.type === "Prerender" && curOutput.lambda && curOutput.lambda.experimentalAllowBundling) { | ||
| bundleableLambdas[outputName] = curOutput.lambda; | ||
| } | ||
| } | ||
| return bundleableLambdas; | ||
| } | ||
| function applyGroupedLambdas(output, groupedLambdas) { | ||
| for (const outputName of Object.keys(groupedLambdas)) { | ||
| const groupedLambda = groupedLambdas[outputName]; | ||
| const origOutput = output[outputName]; | ||
| if (origOutput.type === "Lambda") { | ||
| output[outputName] = groupedLambda; | ||
| } else if (origOutput.type === "Prerender" && origOutput.lambda) { | ||
| origOutput.lambda = groupedLambda; | ||
| } | ||
| } | ||
| } | ||
| async function deserializeBuildOutput(options) { | ||
| const { | ||
| outputDir, | ||
| repoRootPath, | ||
| maxBundleSizeMb, | ||
| debugGroupLambdas, | ||
| useOnlyStreamingLambda, | ||
| forceNodejsStreaming, | ||
| deserializeLambda, | ||
| groupLambdas, | ||
| inspectSerializedLambda, | ||
| warn, | ||
| includeDeploymentId, | ||
| getMeta | ||
| } = options; | ||
| let hasServerActions = false; | ||
| const configPath = (0, import_path.join)(outputDir, "config.json"); | ||
| const config = await (0, import_maybe_read_json.maybeReadJSON)(configPath); | ||
| if (!config) { | ||
| throw new Error(`Config file was not found at "${configPath}"`); | ||
| } | ||
| if (config.version !== 3) { | ||
| throw new Error( | ||
| `Expected \`version: 3\` in "${configPath}" file (received \`${config.version}\`)` | ||
| ); | ||
| } | ||
| validateDeploymentId(config.deploymentId); | ||
| const flags = await (0, import_maybe_read_json.maybeReadJSON)( | ||
| (0, import_path.join)(outputDir, "flags.json") | ||
| ); | ||
| const staticDir = (0, import_path.join)(outputDir, "static"); | ||
| const output = await (0, import_glob.default)("**", { | ||
| cwd: staticDir, | ||
| follow: true | ||
| }); | ||
| applyOutputOverrides(output, config.overrides, warn); | ||
| const fileFsRefsCache = /* @__PURE__ */ new Map(); | ||
| const prerenders = /* @__PURE__ */ new Map(); | ||
| const functionsDir = (0, import_path.join)(outputDir, "functions"); | ||
| const functionSymlinks = /* @__PURE__ */ new Map(); | ||
| for await (const path of (0, import_create_functions_iterator.createFunctionsIterator)(functionsDir)) { | ||
| let lambda = void 0; | ||
| const fnDir = (0, import_path.join)(functionsDir, `${path}.func`); | ||
| try { | ||
| const link = await fs.readlink(fnDir); | ||
| const target = (0, import_path.join)((0, import_path.dirname)(path), link).slice(0, -5); | ||
| functionSymlinks.set(path, target); | ||
| } catch (err) { | ||
| if (err.code !== "EINVAL") | ||
| throw err; | ||
| const funcConfigPath = (0, import_path.join)(fnDir, ".vc-config.json"); | ||
| const funcConfig = await (0, import_maybe_read_json.maybeReadJSON)( | ||
| funcConfigPath | ||
| ); | ||
| if (!funcConfig) { | ||
| throw new Error(`Could not load function config: "${funcConfigPath}"`); | ||
| } | ||
| const files = await (0, import_glob.default)("**", { cwd: fnDir, includeDirectories: true }); | ||
| delete files[".vc-config.json"]; | ||
| if (funcConfig.type === "EdgeFunction" || funcConfig.runtime === "edge") { | ||
| output[path] = await (0, import_deserialize_edge_function.deserializeEdgeFunction)( | ||
| files, | ||
| funcConfig, | ||
| repoRootPath, | ||
| fileFsRefsCache | ||
| ); | ||
| continue; | ||
| } | ||
| lambda = await deserializeLambda( | ||
| files, | ||
| funcConfig, | ||
| repoRootPath, | ||
| fileFsRefsCache, | ||
| { useOnlyStreamingLambda, forceNodejsStreaming } | ||
| ); | ||
| if (inspectSerializedLambda) { | ||
| hasServerActions = await inspectSerializedLambda( | ||
| path, | ||
| funcConfig, | ||
| repoRootPath, | ||
| hasServerActions | ||
| ); | ||
| } | ||
| } | ||
| const prerenderConfigPath = (0, import_path.join)( | ||
| functionsDir, | ||
| `${path}.prerender-config.json` | ||
| ); | ||
| const prerenderConfig = await (0, import_maybe_read_json.maybeReadJSON)( | ||
| prerenderConfigPath | ||
| ); | ||
| if (prerenderConfig) { | ||
| const fallback = await deserializePrerenderFallback( | ||
| prerenderConfigPath, | ||
| prerenderConfig.fallback | ||
| ); | ||
| const prerender = new import_prerender.Prerender({ | ||
| ...prerenderConfig, | ||
| lambda, | ||
| fallback | ||
| }); | ||
| prerenders.set(path, prerender); | ||
| } else if (lambda) { | ||
| output[path] = lambda; | ||
| } | ||
| } | ||
| applyFunctionSymlinks(output, prerenders, functionSymlinks); | ||
| appendSortedPrerenders(output, prerenders); | ||
| const groupedLambdas = await groupLambdas( | ||
| getBundleableLambdas(output), | ||
| { | ||
| force: void 0, | ||
| maxBundleSizeMb, | ||
| debug: debugGroupLambdas | ||
| } | ||
| ); | ||
| applyGroupedLambdas(output, groupedLambdas); | ||
| const framework = (0, import_validate_framework_version.validateFrameworkVersion)(config?.framework?.version); | ||
| const meta = getMeta?.(hasServerActions); | ||
| return { | ||
| wildcard: config.wildcard, | ||
| images: config.images, | ||
| crons: config.crons, | ||
| flags: flags ? flags : config.flags, | ||
| routes: config.routes, | ||
| output, | ||
| framework, | ||
| ...includeDeploymentId ? { deploymentId: config.deploymentId } : {}, | ||
| ...meta !== void 0 ? { meta } : {} | ||
| }; | ||
| } | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| deserializeBuildOutput, | ||
| validateDeploymentId | ||
| }); |
+10
-0
| # @vercel/build-utils | ||
| ## 13.20.0 | ||
| ### Minor Changes | ||
| - Add framework to package manifest for python and backends builders. ([#16072](https://github.com/vercel/vercel/pull/16072)) | ||
| ### Patch Changes | ||
| - Add shared build output deserialization helpers for existing callers. ([#16072](https://github.com/vercel/vercel/pull/16072)) | ||
| ## 13.19.1 | ||
@@ -4,0 +14,0 @@ |
+2
-0
@@ -60,2 +60,4 @@ import FileBlob from './file-blob'; | ||
| export { maybeReadJSON } from './deserialize/maybe-read-json'; | ||
| export { deserializeBuildOutput, validateDeploymentId, } from './deserialize/deserialize-build-output'; | ||
| export type { DeserializeBuildOutputConfig, DeserializeBuildOutputResult, DeserializeBuildOutputPathOverride, DeserializeBuildOutputOptions, DeserializeBuildOutputLambdaOptions, GroupLambdasOptions, DeserializeBuildOutputSerializedConfig, DeserializeBuildOutputSerializedPrerender, } from './deserialize/deserialize-build-output-types'; | ||
| export { deserializeLambda, type DeserializeLambdaOptions, } from './deserialize/deserialize-lambda'; | ||
@@ -62,0 +64,0 @@ export { deserializeEdgeFunction } from './deserialize/deserialize-edge-function'; |
@@ -14,2 +14,3 @@ import type { Diagnostics } from './types'; | ||
| runtime: string; | ||
| framework?: string; | ||
| runtimeVersion?: { | ||
@@ -16,0 +17,0 @@ requested?: string; |
@@ -143,2 +143,6 @@ export declare const functionsSchema: { | ||
| }; | ||
| readonly framework: { | ||
| readonly type: "string"; | ||
| readonly description: "Detected framework slug, e.g. \"fastapi\", \"flask\", \"hono\"."; | ||
| }; | ||
| readonly runtimeVersion: { | ||
@@ -145,0 +149,0 @@ readonly type: "object"; |
+4
-0
@@ -190,2 +190,6 @@ "use strict"; | ||
| }, | ||
| framework: { | ||
| type: "string", | ||
| description: 'Detected framework slug, e.g. "fastapi", "flask", "hono".' | ||
| }, | ||
| runtimeVersion: { | ||
@@ -192,0 +196,0 @@ type: "object", |
+1
-1
| { | ||
| "name": "@vercel/build-utils", | ||
| "version": "13.19.1", | ||
| "version": "13.20.0", | ||
| "license": "Apache-2.0", | ||
@@ -5,0 +5,0 @@ "main": "./dist/index.js", |
Sorry, the diff of this file is too big to display
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 11 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 3 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 11 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 3 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
1752962
1.48%152
2.7%43865
1.5%53
1.92%