@vercel/backends 0.0.46 → 0.0.48
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/dist/index.mjs +226 -8
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { delimiter, dirname, extname, join } from "path";
|
|
|
3
3
|
import { FileBlob, FileFsRef, NodejsLambda, Span, debug, defaultCachePathGlob, download, execCommand, getEnvForPackageManager, getLambdaOptionsFromFunction, getNodeBinPaths, getNodeVersion, glob, isBackendFramework, isBunVersion, isExperimentalBackendsWithoutIntrospectionEnabled, runNpmInstall, runPackageJsonScript, scanParentDirs } from "@vercel/build-utils";
|
|
4
4
|
import { createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { lstat, readFile, rm, stat } from "node:fs/promises";
|
|
6
|
-
import { dirname as dirname$1, extname as extname$1, isAbsolute, join as join$1, relative } from "node:path";
|
|
6
|
+
import { basename, dirname as dirname$1, extname as extname$1, isAbsolute, join as join$1, relative } from "node:path";
|
|
7
7
|
import { build as build$2 } from "rolldown";
|
|
8
8
|
import { exports } from "resolve.exports";
|
|
9
9
|
import { isNativeError } from "node:util/types";
|
|
@@ -673,6 +673,167 @@ const resolveEntrypointAndFormat = async (args) => {
|
|
|
673
673
|
};
|
|
674
674
|
};
|
|
675
675
|
|
|
676
|
+
//#endregion
|
|
677
|
+
//#region src/service-vc-init.ts
|
|
678
|
+
async function applyServiceVcInit(args) {
|
|
679
|
+
const { format, extension } = await resolveShimFormat(args);
|
|
680
|
+
const handlerDir = dirname$1(args.handler);
|
|
681
|
+
const vcInitName = `${basename(args.handler, extname$1(args.handler))}.__vc_service_vc_init${extension}`;
|
|
682
|
+
const vcInitHandler = handlerDir === "." ? vcInitName : join$1(handlerDir, vcInitName);
|
|
683
|
+
const handlerImportPath = `./${basename(args.handler)}`;
|
|
684
|
+
const vcInitSource = format === "esm" ? createEsmServiceVcInit(handlerImportPath) : createCjsServiceVcInit(handlerImportPath);
|
|
685
|
+
return {
|
|
686
|
+
handler: vcInitHandler,
|
|
687
|
+
files: {
|
|
688
|
+
...args.files,
|
|
689
|
+
[vcInitHandler]: new FileBlob({
|
|
690
|
+
data: vcInitSource,
|
|
691
|
+
mode: 420
|
|
692
|
+
})
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
async function resolveShimFormat(args) {
|
|
697
|
+
const { format } = await resolveEntrypointAndFormat({
|
|
698
|
+
entrypoint: args.handler,
|
|
699
|
+
workPath: args.workPath
|
|
700
|
+
});
|
|
701
|
+
return {
|
|
702
|
+
format,
|
|
703
|
+
extension: extname$1(args.handler) || (format === "esm" ? ".mjs" : ".cjs")
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
const sharedShimPrelude = String.raw`
|
|
707
|
+
const PATCH_SYMBOL = Symbol.for('vc.service.route-prefix-strip.patch')
|
|
708
|
+
|
|
709
|
+
function normalizeServiceRoutePrefix(rawPrefix) {
|
|
710
|
+
if (!rawPrefix) {
|
|
711
|
+
return ''
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
let prefix = String(rawPrefix).trim()
|
|
715
|
+
if (!prefix) {
|
|
716
|
+
return ''
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (!prefix.startsWith('/')) {
|
|
720
|
+
prefix = '/' + prefix
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (prefix !== '/') {
|
|
724
|
+
prefix = prefix.replace(/\/+$/, '')
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return prefix === '/' ? '' : prefix
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function getServiceRoutePrefix() {
|
|
731
|
+
const enabled = String(
|
|
732
|
+
process.env.VERCEL_SERVICE_ROUTE_PREFIX_STRIP || ''
|
|
733
|
+
).toLowerCase()
|
|
734
|
+
if (enabled !== '1' && enabled !== 'true') {
|
|
735
|
+
return ''
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return normalizeServiceRoutePrefix(process.env.VERCEL_SERVICE_ROUTE_PREFIX || '')
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function stripServiceRoutePrefix(requestUrl, prefix) {
|
|
742
|
+
if (typeof requestUrl !== 'string' || requestUrl === '*') {
|
|
743
|
+
return requestUrl
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const queryIndex = requestUrl.indexOf('?')
|
|
747
|
+
const rawPath =
|
|
748
|
+
queryIndex === -1 ? requestUrl : requestUrl.slice(0, queryIndex)
|
|
749
|
+
const query = queryIndex === -1 ? '' : requestUrl.slice(queryIndex)
|
|
750
|
+
|
|
751
|
+
let path = rawPath || '/'
|
|
752
|
+
if (!path.startsWith('/')) {
|
|
753
|
+
path = '/' + path
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (!prefix) {
|
|
757
|
+
return path + query
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
if (path === prefix) {
|
|
761
|
+
return '/' + query
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (path.startsWith(prefix + '/')) {
|
|
765
|
+
return path.slice(prefix.length) + query
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
return path + query
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function patchServerRequestUrl(ServerCtor) {
|
|
772
|
+
const prefix = getServiceRoutePrefix()
|
|
773
|
+
if (!prefix || globalThis[PATCH_SYMBOL]) {
|
|
774
|
+
return
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
globalThis[PATCH_SYMBOL] = true
|
|
778
|
+
|
|
779
|
+
const originalEmit = ServerCtor.prototype.emit
|
|
780
|
+
ServerCtor.prototype.emit = function patchedEmit(event, request, ...args) {
|
|
781
|
+
if (event === 'request' && request && typeof request.url === 'string') {
|
|
782
|
+
request.url = stripServiceRoutePrefix(request.url, prefix)
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
return originalEmit.call(this, event, request, ...args)
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
`;
|
|
789
|
+
function createEsmServiceVcInit(handlerImportPath) {
|
|
790
|
+
return `
|
|
791
|
+
import { Server } from 'node:http'
|
|
792
|
+
|
|
793
|
+
${sharedShimPrelude}
|
|
794
|
+
|
|
795
|
+
// Patch the HTTP server before loading user code so apps that attach request
|
|
796
|
+
// listeners during module evaluation see the stripped service-relative URL.
|
|
797
|
+
patchServerRequestUrl(Server)
|
|
798
|
+
|
|
799
|
+
const originalModule = await import(${JSON.stringify(handlerImportPath)})
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Match the Node serverless loader behavior: TS/CJS/ESM interop can leave us
|
|
803
|
+
* with nested \`.default\` wrappers, so peel off a few layers to recover the
|
|
804
|
+
* actual user entrypoint shape.
|
|
805
|
+
*/
|
|
806
|
+
function unwrapDefaultExport(value) {
|
|
807
|
+
let current = value
|
|
808
|
+
for (let i = 0; i < 5; i++) {
|
|
809
|
+
if (current && typeof current === 'object' && 'default' in current && current.default) {
|
|
810
|
+
current = current.default
|
|
811
|
+
} else {
|
|
812
|
+
break
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return current
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
const entrypoint = unwrapDefaultExport(originalModule)
|
|
819
|
+
|
|
820
|
+
// Re-export the resolved entrypoint so the surrounding runtime still sees the
|
|
821
|
+
// same handler shape after this service bootstrap runs.
|
|
822
|
+
export default typeof entrypoint === 'undefined' ? originalModule : entrypoint
|
|
823
|
+
`.trimStart();
|
|
824
|
+
}
|
|
825
|
+
function createCjsServiceVcInit(handlerImportPath) {
|
|
826
|
+
return `
|
|
827
|
+
const { Server } = require('node:http')
|
|
828
|
+
|
|
829
|
+
${sharedShimPrelude}
|
|
830
|
+
|
|
831
|
+
patchServerRequestUrl(Server)
|
|
832
|
+
|
|
833
|
+
module.exports = require(${JSON.stringify(handlerImportPath)})
|
|
834
|
+
`.trimStart();
|
|
835
|
+
}
|
|
836
|
+
|
|
676
837
|
//#endregion
|
|
677
838
|
//#region src/rolldown/nft.ts
|
|
678
839
|
const nft = async (args) => {
|
|
@@ -1475,10 +1636,23 @@ const build = async (args) => {
|
|
|
1475
1636
|
sourceFile: entrypoint,
|
|
1476
1637
|
config: args.config
|
|
1477
1638
|
});
|
|
1639
|
+
const serviceRoutePrefix = normalizeServiceRoutePrefix(args.config?.routePrefix ?? args.service?.routePrefix);
|
|
1640
|
+
const shouldStripServiceRoutePrefix = !!serviceRoutePrefix && (typeof args.config?.serviceName === "string" || !!args.service);
|
|
1641
|
+
let lambdaFiles = files;
|
|
1642
|
+
let lambdaHandler = handler;
|
|
1643
|
+
if (shouldStripServiceRoutePrefix) {
|
|
1644
|
+
const shimmedLambda = await applyServiceVcInit({
|
|
1645
|
+
files,
|
|
1646
|
+
handler,
|
|
1647
|
+
workPath: nftWorkPath
|
|
1648
|
+
});
|
|
1649
|
+
lambdaFiles = shimmedLambda.files;
|
|
1650
|
+
lambdaHandler = shimmedLambda.handler;
|
|
1651
|
+
}
|
|
1478
1652
|
const lambda = new NodejsLambda({
|
|
1479
1653
|
runtime: nodeVersion.runtime,
|
|
1480
|
-
handler,
|
|
1481
|
-
files,
|
|
1654
|
+
handler: lambdaHandler,
|
|
1655
|
+
files: lambdaFiles,
|
|
1482
1656
|
framework: rolldownResult.framework,
|
|
1483
1657
|
shouldAddHelpers: false,
|
|
1484
1658
|
shouldAddSourcemapSupport: true,
|
|
@@ -1486,18 +1660,35 @@ const build = async (args) => {
|
|
|
1486
1660
|
...functionConfigOverrides,
|
|
1487
1661
|
shouldDisableAutomaticFetchInstrumentation: process.env.VERCEL_TRACING_DISABLE_AUTOMATIC_FETCH_INSTRUMENTATION === "1"
|
|
1488
1662
|
});
|
|
1663
|
+
if (shouldStripServiceRoutePrefix && serviceRoutePrefix) lambda.environment = {
|
|
1664
|
+
...lambda.environment,
|
|
1665
|
+
VERCEL_SERVICE_ROUTE_PREFIX: serviceRoutePrefix,
|
|
1666
|
+
VERCEL_SERVICE_ROUTE_PREFIX_STRIP: "1"
|
|
1667
|
+
};
|
|
1668
|
+
const serviceName = typeof args.config?.serviceName === "string" && args.config.serviceName !== "" ? args.config.serviceName : void 0;
|
|
1669
|
+
const internalServiceFunctionPath = typeof serviceName === "string" && serviceName !== "" ? `/_svc/${serviceName}/index` : void 0;
|
|
1670
|
+
const internalServiceOutputPath = internalServiceFunctionPath?.slice(1);
|
|
1671
|
+
const remapRouteDestination = (route) => {
|
|
1672
|
+
const prefixedRoute = maybePrefixServiceRouteSource(route, serviceRoutePrefix);
|
|
1673
|
+
if (!internalServiceFunctionPath || !route.dest) return prefixedRoute;
|
|
1674
|
+
return {
|
|
1675
|
+
...prefixedRoute,
|
|
1676
|
+
dest: internalServiceFunctionPath
|
|
1677
|
+
};
|
|
1678
|
+
};
|
|
1489
1679
|
const routes = [
|
|
1490
1680
|
{ handle: "filesystem" },
|
|
1491
|
-
...introspectionResult.routes,
|
|
1681
|
+
...introspectionResult.routes.map(remapRouteDestination),
|
|
1492
1682
|
{
|
|
1493
|
-
src:
|
|
1494
|
-
dest: "/"
|
|
1683
|
+
src: getServiceCatchallSource(serviceRoutePrefix),
|
|
1684
|
+
dest: internalServiceFunctionPath ?? "/"
|
|
1495
1685
|
}
|
|
1496
1686
|
];
|
|
1497
|
-
const output = { index: lambda };
|
|
1687
|
+
const output = internalServiceOutputPath ? { [internalServiceOutputPath]: lambda } : { index: lambda };
|
|
1498
1688
|
for (const route of routes) if (route.dest) {
|
|
1499
1689
|
if (route.dest === "/") continue;
|
|
1500
|
-
|
|
1690
|
+
const outputPath = route.dest === internalServiceFunctionPath && internalServiceOutputPath ? internalServiceOutputPath : route.dest;
|
|
1691
|
+
output[outputPath] = lambda;
|
|
1501
1692
|
}
|
|
1502
1693
|
return {
|
|
1503
1694
|
routes,
|
|
@@ -1509,6 +1700,33 @@ const prepareCache = ({ repoRootPath, workPath }) => {
|
|
|
1509
1700
|
return glob(defaultCachePathGlob, repoRootPath || workPath);
|
|
1510
1701
|
};
|
|
1511
1702
|
const normalizeArray = (value) => Array.isArray(value) ? value : value ? [value] : [];
|
|
1703
|
+
const normalizeServiceRoutePrefix = (routePrefix) => {
|
|
1704
|
+
if (typeof routePrefix !== "string" || routePrefix === "" || routePrefix === ".") return;
|
|
1705
|
+
let normalized = routePrefix.startsWith("/") ? routePrefix : `/${routePrefix}`;
|
|
1706
|
+
if (normalized !== "/" && normalized.endsWith("/")) normalized = normalized.slice(0, -1);
|
|
1707
|
+
return normalized === "/" ? void 0 : normalized;
|
|
1708
|
+
};
|
|
1709
|
+
const maybePrefixServiceRouteSource = (route, routePrefix) => {
|
|
1710
|
+
if (!routePrefix || typeof route.dest !== "string" || !route.dest.startsWith("/")) return route;
|
|
1711
|
+
return {
|
|
1712
|
+
...route,
|
|
1713
|
+
src: getPrefixedRouteSource(route.src, route.dest, routePrefix)
|
|
1714
|
+
};
|
|
1715
|
+
};
|
|
1716
|
+
const getPrefixedRouteSource = (routeSource, routePath, routePrefix) => {
|
|
1717
|
+
if (!routeSource) return routeSource;
|
|
1718
|
+
if (routePath === routePrefix || routePath.startsWith(`${routePrefix}/`)) return routeSource;
|
|
1719
|
+
const escapedRoutePrefix = toRegexSource(routePrefix);
|
|
1720
|
+
if (routeSource.startsWith("^(?:")) return `^(?:${escapedRoutePrefix}${routeSource.slice(4)}`;
|
|
1721
|
+
if (routeSource.startsWith("^")) return `^${escapedRoutePrefix}${routeSource.slice(1)}`;
|
|
1722
|
+
return `${escapedRoutePrefix}${routeSource}`;
|
|
1723
|
+
};
|
|
1724
|
+
const getServiceCatchallSource = (routePrefix) => {
|
|
1725
|
+
if (!routePrefix) return "/(.*)";
|
|
1726
|
+
return `^${escapeForRegex(routePrefix)}(?:/(.*))?$`;
|
|
1727
|
+
};
|
|
1728
|
+
const escapeForRegex = (value) => value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
|
|
1729
|
+
const toRegexSource = (value) => escapeForRegex(value).replaceAll("/", "\\/");
|
|
1512
1730
|
|
|
1513
1731
|
//#endregion
|
|
1514
1732
|
export { build, build$1 as cervelBuild, serve as cervelServe, findEntrypoint, findEntrypointOrThrow, getBuildSummary, introspectApp, nodeFileTrace, prepareCache, srvxOptions, version };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/backends",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.48",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./dist/index.mjs",
|
|
6
6
|
"homepage": "https://vercel.com/docs",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"srvx": "0.8.9",
|
|
35
35
|
"tsx": "4.21.0",
|
|
36
36
|
"zod": "3.22.4",
|
|
37
|
-
"@vercel/build-utils": "13.8.
|
|
37
|
+
"@vercel/build-utils": "13.8.2"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"typescript": "^4.0.0 || ^5.0.0"
|