@vercel/fs-detectors 6.7.7 → 6.8.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/dist/detect-builders.d.ts +2 -1
- package/dist/detect-builders.js +17 -6
- package/dist/index.d.ts +3 -1
- package/dist/index.js +12 -0
- package/dist/services/detect-services.d.ts +1 -1
- package/dist/services/detect-services.js +26 -6
- package/dist/services/get-services-builders.d.ts +7 -3
- package/dist/services/get-services-builders.js +21 -2
- package/dist/services/resolve-v2.d.ts +11 -0
- package/dist/services/resolve-v2.js +264 -0
- package/dist/services/resolve.d.ts +33 -3
- package/dist/services/resolve.js +11 -0
- package/dist/services/types.d.ts +7 -6
- package/dist/services/utils.d.ts +2 -1
- package/package.json +5 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Route } from '@vercel/routing-utils';
|
|
2
|
-
import type { PackageJson, Builder, BuilderFunctions, ExperimentalServices, ProjectSettings, Service } from '@vercel/build-utils';
|
|
2
|
+
import type { PackageJson, Builder, BuilderFunctions, ExperimentalServices, ExperimentalServicesV2, ProjectSettings, Service } from '@vercel/build-utils';
|
|
3
3
|
/**
|
|
4
4
|
* Pattern for finding all supported middleware files.
|
|
5
5
|
*/
|
|
@@ -23,6 +23,7 @@ export interface Options {
|
|
|
23
23
|
tag?: string;
|
|
24
24
|
functions?: BuilderFunctions;
|
|
25
25
|
experimentalServices?: ExperimentalServices;
|
|
26
|
+
experimentalServicesV2?: ExperimentalServicesV2;
|
|
26
27
|
ignoreBuildScript?: boolean;
|
|
27
28
|
projectSettings?: ProjectSettings;
|
|
28
29
|
cleanUrls?: boolean;
|
package/dist/detect-builders.js
CHANGED
|
@@ -92,18 +92,28 @@ function detectOutputDirectory(builders) {
|
|
|
92
92
|
return publicBuilder ? publicBuilder.src.replace("/**/*", "") : null;
|
|
93
93
|
}
|
|
94
94
|
async function detectBuilders(files, pkg, options = {}) {
|
|
95
|
-
const {
|
|
95
|
+
const {
|
|
96
|
+
experimentalServices: experimentalServicesV1,
|
|
97
|
+
experimentalServicesV2,
|
|
98
|
+
projectSettings = {}
|
|
99
|
+
} = options;
|
|
96
100
|
const { framework } = projectSettings;
|
|
97
|
-
const configuredServices =
|
|
98
|
-
const configuredServicesType = "experimentalServices";
|
|
101
|
+
const configuredServices = experimentalServicesV2 ?? experimentalServicesV1;
|
|
102
|
+
const configuredServicesType = experimentalServicesV2 ? "experimentalServicesV2" : "experimentalServices";
|
|
99
103
|
const hasServicesConfig = configuredServices != null && typeof configuredServices === "object";
|
|
100
104
|
if (hasServicesConfig || framework === "services") {
|
|
101
|
-
|
|
105
|
+
const result = await (0, import_get_services_builders.getServicesBuilders)({
|
|
102
106
|
workPath: options.workPath,
|
|
103
107
|
configuredServices,
|
|
104
108
|
configuredServicesType,
|
|
105
109
|
projectFramework: framework
|
|
106
110
|
});
|
|
111
|
+
if (configuredServices != null) {
|
|
112
|
+
result.warnings.push(
|
|
113
|
+
...(0, import_get_services_builders.warnIgnoredDirectories)(files, configuredServices)
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
107
117
|
}
|
|
108
118
|
const errors = [];
|
|
109
119
|
const warnings = [];
|
|
@@ -486,10 +496,11 @@ function validateFunctions({ functions = {} }) {
|
|
|
486
496
|
message: "Function must contain at least one property."
|
|
487
497
|
};
|
|
488
498
|
}
|
|
489
|
-
|
|
499
|
+
const maxDurationLimit = (0, import_build_utils.getMaxDurationLimit)();
|
|
500
|
+
if (func.maxDuration !== void 0 && func.maxDuration !== "max" && (func.maxDuration < 1 || maxDurationLimit !== void 0 && func.maxDuration > maxDurationLimit || !Number.isInteger(func.maxDuration))) {
|
|
490
501
|
return {
|
|
491
502
|
code: "invalid_function_duration",
|
|
492
|
-
message:
|
|
503
|
+
message: maxDurationLimit !== void 0 ? `Functions must have a maxDuration between 1 and ${maxDurationLimit}, or "max".` : 'Functions must have a positive integer maxDuration, or "max".'
|
|
493
504
|
};
|
|
494
505
|
}
|
|
495
506
|
if (func.memory !== void 0 && (func.memory < 128 || func.memory > 10240)) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export { detectBuilders, detectOutputDirectory, detectApiDirectory, detectApiExtensions, type Options as DetectBuildersOptions, } from './detect-builders';
|
|
2
2
|
export { detectServices, generateServicesRoutes, } from './services/detect-services';
|
|
3
|
+
export { resolveAllConfiguredServicesV2, resolveConfiguredServiceV2, validateServiceConfigV2, } from './services/resolve-v2';
|
|
4
|
+
export { isExperimentalService, isExperimentalServiceV2, } from '@vercel/build-utils';
|
|
3
5
|
export { autoDetectServices } from './services/auto-detect';
|
|
4
6
|
export type { AutoDetectOptions, AutoDetectResult, } from './services/auto-detect';
|
|
5
7
|
export { isStaticBuild, isRouteOwningBuilder, INTERNAL_SERVICE_PREFIX, getInternalServiceFunctionPath, getInternalServiceCronPath, getInternalServiceCronPathPrefix, getInternalServiceWorkerPath, getInternalServiceWorkerPathPrefix, } from './services/utils';
|
|
6
8
|
export { getServicesBuilders } from './services/get-services-builders';
|
|
7
|
-
export type { DetectServicesOptions, DetectServicesResult, DetectServicesSource, InferredServicesConfig, ResolvedServicesResult, InferredServicesResult, ResolvedService, Service, ServicesRoutes, ServiceDetectionError, } from './services/types';
|
|
9
|
+
export type { DetectServicesOptions, DetectServicesResult, DetectServicesSource, InferredServicesConfig, ResolvedServicesResult, InferredServicesResult, ResolvedService, Service, ExperimentalService, ExperimentalServiceV2, ExperimentalServiceV2Config, ExperimentalServicesV2, ExperimentalServiceV2Binding, ServicesRoutes, ServiceDetectionError, } from './services/types';
|
|
8
10
|
export { detectFileSystemAPI } from './detect-file-system-api';
|
|
9
11
|
export { detectFramework, detectFrameworks, detectFrameworkRecord, detectFrameworkVersion, } from './detect-framework';
|
|
10
12
|
export { getProjectPaths } from './get-project-paths';
|
package/dist/index.js
CHANGED
|
@@ -49,17 +49,24 @@ __export(src_exports, {
|
|
|
49
49
|
getServicesBuilders: () => import_get_services_builders.getServicesBuilders,
|
|
50
50
|
getWorkspacePackagePaths: () => import_get_workspace_package_paths.getWorkspacePackagePaths,
|
|
51
51
|
getWorkspaces: () => import_get_workspaces.getWorkspaces,
|
|
52
|
+
isExperimentalService: () => import_build_utils.isExperimentalService,
|
|
53
|
+
isExperimentalServiceV2: () => import_build_utils.isExperimentalServiceV2,
|
|
52
54
|
isOfficialRuntime: () => import_is_official_runtime.isOfficialRuntime,
|
|
53
55
|
isRouteOwningBuilder: () => import_utils.isRouteOwningBuilder,
|
|
54
56
|
isStaticBuild: () => import_utils.isStaticBuild,
|
|
55
57
|
isStaticRuntime: () => import_is_official_runtime.isStaticRuntime,
|
|
56
58
|
monorepoManagers: () => import_monorepo_managers.monorepoManagers,
|
|
57
59
|
packageManagers: () => import_package_managers.packageManagers,
|
|
60
|
+
resolveAllConfiguredServicesV2: () => import_resolve_v2.resolveAllConfiguredServicesV2,
|
|
61
|
+
resolveConfiguredServiceV2: () => import_resolve_v2.resolveConfiguredServiceV2,
|
|
62
|
+
validateServiceConfigV2: () => import_resolve_v2.validateServiceConfigV2,
|
|
58
63
|
workspaceManagers: () => import_workspace_managers.workspaceManagers
|
|
59
64
|
});
|
|
60
65
|
module.exports = __toCommonJS(src_exports);
|
|
61
66
|
var import_detect_builders = require("./detect-builders");
|
|
62
67
|
var import_detect_services = require("./services/detect-services");
|
|
68
|
+
var import_resolve_v2 = require("./services/resolve-v2");
|
|
69
|
+
var import_build_utils = require("@vercel/build-utils");
|
|
63
70
|
var import_auto_detect = require("./services/auto-detect");
|
|
64
71
|
var import_utils = require("./services/utils");
|
|
65
72
|
var import_get_services_builders = require("./services/get-services-builders");
|
|
@@ -109,12 +116,17 @@ var import_detect_instrumentation = require("./detect-instrumentation");
|
|
|
109
116
|
getServicesBuilders,
|
|
110
117
|
getWorkspacePackagePaths,
|
|
111
118
|
getWorkspaces,
|
|
119
|
+
isExperimentalService,
|
|
120
|
+
isExperimentalServiceV2,
|
|
112
121
|
isOfficialRuntime,
|
|
113
122
|
isRouteOwningBuilder,
|
|
114
123
|
isStaticBuild,
|
|
115
124
|
isStaticRuntime,
|
|
116
125
|
monorepoManagers,
|
|
117
126
|
packageManagers,
|
|
127
|
+
resolveAllConfiguredServicesV2,
|
|
128
|
+
resolveConfiguredServiceV2,
|
|
129
|
+
validateServiceConfigV2,
|
|
118
130
|
workspaceManagers,
|
|
119
131
|
...require("./monorepos/get-monorepo-default-settings")
|
|
120
132
|
});
|
|
@@ -33,4 +33,4 @@ export declare function detectServices(options: DetectServicesOptions): Promise<
|
|
|
33
33
|
* Internal cron callback routes under `/_svc/{serviceName}/crons/{entry}/{handler}`
|
|
34
34
|
* that rewrite to `/_svc/{serviceName}/index`.
|
|
35
35
|
*/
|
|
36
|
-
export declare function generateServicesRoutes(
|
|
36
|
+
export declare function generateServicesRoutes(allServices: Service[]): ServicesRoutes;
|
|
@@ -26,6 +26,7 @@ var import_build_utils = require("@vercel/build-utils");
|
|
|
26
26
|
var import_routing_utils = require("@vercel/routing-utils");
|
|
27
27
|
var import_utils = require("./utils");
|
|
28
28
|
var import_resolve = require("./resolve");
|
|
29
|
+
var import_resolve_v2 = require("./resolve-v2");
|
|
29
30
|
var import_auto_detect = require("./auto-detect");
|
|
30
31
|
var import_detect_railway = require("./detect-railway");
|
|
31
32
|
var import_detect_render = require("./detect-render");
|
|
@@ -90,7 +91,8 @@ async function detectServices(options) {
|
|
|
90
91
|
fs,
|
|
91
92
|
workPath,
|
|
92
93
|
detectEntrypoint,
|
|
93
|
-
configuredServices: providedConfiguredServices
|
|
94
|
+
configuredServices: providedConfiguredServices,
|
|
95
|
+
configuredServicesType: providedConfiguredServicesType
|
|
94
96
|
} = options;
|
|
95
97
|
const scopedFs = workPath ? fs.chdir(workPath) : fs;
|
|
96
98
|
const { config: vercelConfig, error: configError } = await (0, import_utils.readVercelConfig)(scopedFs);
|
|
@@ -105,9 +107,26 @@ async function detectServices(options) {
|
|
|
105
107
|
});
|
|
106
108
|
}
|
|
107
109
|
const hasProvidedConfiguredServices = providedConfiguredServices && Object.keys(providedConfiguredServices).length > 0;
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
const experimentalServicesV2 = hasProvidedConfiguredServices && providedConfiguredServicesType === "experimentalServicesV2" ? providedConfiguredServices : hasProvidedConfiguredServices ? void 0 : vercelConfig?.experimentalServicesV2;
|
|
111
|
+
if (experimentalServicesV2 && Object.keys(experimentalServicesV2).length > 0) {
|
|
112
|
+
const result2 = await (0, import_resolve_v2.resolveAllConfiguredServicesV2)(
|
|
113
|
+
experimentalServicesV2,
|
|
114
|
+
scopedFs
|
|
115
|
+
);
|
|
116
|
+
return withResolvedResult({
|
|
117
|
+
services: result2.services,
|
|
118
|
+
source: "configured",
|
|
119
|
+
// V2 uses explicit `bindings`, so no implicit `{NAME}_URL` injection.
|
|
120
|
+
useImplicitEnvInjection: false,
|
|
121
|
+
// V2 routes are explicitly carried per-service to output them separately.
|
|
122
|
+
routes: emptyRoutes(),
|
|
123
|
+
errors: result2.errors,
|
|
124
|
+
warnings: []
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const experimentalServicesV1 = hasProvidedConfiguredServices ? providedConfiguredServices : vercelConfig?.experimentalServices;
|
|
128
|
+
const hasExperimentalServicesV1 = experimentalServicesV1 && Object.keys(experimentalServicesV1).length > 0;
|
|
129
|
+
if (!hasExperimentalServicesV1) {
|
|
111
130
|
const detectors = [
|
|
112
131
|
{ detect: import_detect_railway.detectRailwayServices, source: "railway" },
|
|
113
132
|
{ detect: import_detect_render.detectRenderServices, source: "render" },
|
|
@@ -135,7 +154,7 @@ async function detectServices(options) {
|
|
|
135
154
|
});
|
|
136
155
|
}
|
|
137
156
|
const result = await (0, import_resolve.resolveAllConfiguredServices)(
|
|
138
|
-
|
|
157
|
+
experimentalServicesV1,
|
|
139
158
|
scopedFs,
|
|
140
159
|
"configured"
|
|
141
160
|
);
|
|
@@ -210,7 +229,8 @@ async function tryResolveInferred(detectResult, source, scopedFs) {
|
|
|
210
229
|
inferred
|
|
211
230
|
);
|
|
212
231
|
}
|
|
213
|
-
function generateServicesRoutes(
|
|
232
|
+
function generateServicesRoutes(allServices) {
|
|
233
|
+
const services = allServices.filter(import_build_utils.isExperimentalService);
|
|
214
234
|
const hostRewrites = [];
|
|
215
235
|
const rewrites = [];
|
|
216
236
|
const defaults = [];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Route } from '@vercel/routing-utils';
|
|
2
2
|
import type { Builder } from '@vercel/build-utils';
|
|
3
|
-
import type { ConfiguredServices,
|
|
3
|
+
import type { ConfiguredServices, ConfiguredServicesType, Service } from './types';
|
|
4
4
|
export interface ErrorResponse {
|
|
5
5
|
code: string;
|
|
6
6
|
message: string;
|
|
@@ -10,7 +10,7 @@ export interface ErrorResponse {
|
|
|
10
10
|
export interface GetServicesBuildersOptions {
|
|
11
11
|
workPath?: string;
|
|
12
12
|
configuredServices?: ConfiguredServices;
|
|
13
|
-
configuredServicesType?:
|
|
13
|
+
configuredServicesType?: ConfiguredServicesType;
|
|
14
14
|
projectFramework?: string | null;
|
|
15
15
|
}
|
|
16
16
|
export interface ServicesBuildersResult {
|
|
@@ -23,7 +23,7 @@ export interface ServicesBuildersResult {
|
|
|
23
23
|
redirectRoutes: Route[] | null;
|
|
24
24
|
rewriteRoutes: Route[] | null;
|
|
25
25
|
errorRoutes: Route[] | null;
|
|
26
|
-
services?:
|
|
26
|
+
services?: Service[];
|
|
27
27
|
useImplicitEnvInjection?: boolean;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
@@ -33,3 +33,7 @@ export interface ServicesBuildersResult {
|
|
|
33
33
|
* the shape expected by `detectBuilders` when `framework === 'services'`.
|
|
34
34
|
*/
|
|
35
35
|
export declare function getServicesBuilders(options: GetServicesBuildersOptions): Promise<ServicesBuildersResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Returns warnings for ignored directories that are not covered by services
|
|
38
|
+
*/
|
|
39
|
+
export declare function warnIgnoredDirectories(files: string[], configuredServices: ConfiguredServices): ErrorResponse[];
|
|
@@ -18,7 +18,8 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var get_services_builders_exports = {};
|
|
20
20
|
__export(get_services_builders_exports, {
|
|
21
|
-
getServicesBuilders: () => getServicesBuilders
|
|
21
|
+
getServicesBuilders: () => getServicesBuilders,
|
|
22
|
+
warnIgnoredDirectories: () => warnIgnoredDirectories
|
|
22
23
|
});
|
|
23
24
|
module.exports = __toCommonJS(get_services_builders_exports);
|
|
24
25
|
var import_detect_services = require("./detect-services");
|
|
@@ -134,7 +135,25 @@ async function getServicesBuilders(options) {
|
|
|
134
135
|
useImplicitEnvInjection: result.useImplicitEnvInjection
|
|
135
136
|
};
|
|
136
137
|
}
|
|
138
|
+
function warnIgnoredDirectories(files, configuredServices) {
|
|
139
|
+
const warnings = [];
|
|
140
|
+
if (files.some((f) => f.startsWith("api/"))) {
|
|
141
|
+
const serviceCoversApi = Object.values(configuredServices).some((service) => {
|
|
142
|
+
const root = service.root ?? ".";
|
|
143
|
+
const entrypoint = service.entrypoint ?? "";
|
|
144
|
+
return root === "api" || root.startsWith("api/") || root === "." && entrypoint.startsWith("api/");
|
|
145
|
+
});
|
|
146
|
+
if (!serviceCoversApi) {
|
|
147
|
+
warnings.push({
|
|
148
|
+
code: "api_dir_ignored",
|
|
149
|
+
message: "The `api/` directory will not be built because `experimentalServices` is configured. To serve these files, declare them as a service in your `vercel.json`."
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return warnings;
|
|
154
|
+
}
|
|
137
155
|
// Annotate the CommonJS export names for ESM import in node:
|
|
138
156
|
0 && (module.exports = {
|
|
139
|
-
getServicesBuilders
|
|
157
|
+
getServicesBuilders,
|
|
158
|
+
warnIgnoredDirectories
|
|
140
159
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ExperimentalServiceV2, ExperimentalServiceV2Config, ExperimentalServicesV2, ServiceDetectionError } from './types';
|
|
2
|
+
import type { DetectorFilesystem } from '../detectors/filesystem';
|
|
3
|
+
export declare function validateServiceConfigV2(name: string, config: ExperimentalServiceV2Config): ServiceDetectionError | null;
|
|
4
|
+
export declare function resolveConfiguredServiceV2(name: string, config: ExperimentalServiceV2Config, fs: DetectorFilesystem): Promise<{
|
|
5
|
+
service?: ExperimentalServiceV2;
|
|
6
|
+
error?: ServiceDetectionError;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function resolveAllConfiguredServicesV2(services: ExperimentalServicesV2, fs: DetectorFilesystem): Promise<{
|
|
9
|
+
services: ExperimentalServiceV2[];
|
|
10
|
+
errors: ServiceDetectionError[];
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,264 @@
|
|
|
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 resolve_v2_exports = {};
|
|
20
|
+
__export(resolve_v2_exports, {
|
|
21
|
+
resolveAllConfiguredServicesV2: () => resolveAllConfiguredServicesV2,
|
|
22
|
+
resolveConfiguredServiceV2: () => resolveConfiguredServiceV2,
|
|
23
|
+
validateServiceConfigV2: () => validateServiceConfigV2
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(resolve_v2_exports);
|
|
26
|
+
var import_path = require("path");
|
|
27
|
+
var import_build_utils = require("@vercel/build-utils");
|
|
28
|
+
var import_frameworks = require("@vercel/frameworks");
|
|
29
|
+
var import_types = require("./types");
|
|
30
|
+
var import_resolve = require("./resolve");
|
|
31
|
+
var import_utils = require("./utils");
|
|
32
|
+
const frameworksBySlug = new Map(import_frameworks.frameworkList.map((f) => [f.slug, f]));
|
|
33
|
+
const SERVICE_NAME_REGEX = /^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;
|
|
34
|
+
function validateServiceConfigV2(name, config) {
|
|
35
|
+
if (!SERVICE_NAME_REGEX.test(name)) {
|
|
36
|
+
return {
|
|
37
|
+
code: "INVALID_SERVICE_NAME",
|
|
38
|
+
message: `Service name "${name}" is invalid. Names must start with a letter, end with an alphanumeric character, and contain only alphanumeric characters, hyphens, and underscores.`,
|
|
39
|
+
serviceName: name
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (!config || typeof config !== "object") {
|
|
43
|
+
return {
|
|
44
|
+
code: "INVALID_SERVICE_CONFIG",
|
|
45
|
+
message: `Service "${name}" has an invalid configuration. Expected an object.`,
|
|
46
|
+
serviceName: name
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (typeof config.root !== "string" || config.root.length === 0) {
|
|
50
|
+
return {
|
|
51
|
+
code: "MISSING_ROOT",
|
|
52
|
+
message: `Service "${name}" must specify a "root".`,
|
|
53
|
+
serviceName: name
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const normalizedRoot = import_path.posix.normalize(config.root);
|
|
57
|
+
if (normalizedRoot.startsWith("/")) {
|
|
58
|
+
return {
|
|
59
|
+
code: "INVALID_ROOT",
|
|
60
|
+
message: `Service "${name}" has invalid "root" "${config.root}". Must be a relative path.`,
|
|
61
|
+
serviceName: name
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (normalizedRoot === ".." || normalizedRoot.startsWith("../")) {
|
|
65
|
+
return {
|
|
66
|
+
code: "INVALID_ROOT",
|
|
67
|
+
message: `Service "${name}" has invalid "root" "${config.root}". Must not escape the project root.`,
|
|
68
|
+
serviceName: name
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (config.runtime && !(config.runtime in import_types.RUNTIME_BUILDERS)) {
|
|
72
|
+
return {
|
|
73
|
+
code: "INVALID_RUNTIME",
|
|
74
|
+
message: `Service "${name}" has invalid runtime "${config.runtime}".`,
|
|
75
|
+
serviceName: name
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (config.framework && !frameworksBySlug.has(config.framework)) {
|
|
79
|
+
return {
|
|
80
|
+
code: "INVALID_FRAMEWORK",
|
|
81
|
+
message: `Service "${name}" has invalid framework "${config.framework}".`,
|
|
82
|
+
serviceName: name
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (config.runtime && config.framework) {
|
|
86
|
+
const frameworkRuntime = (0, import_utils.inferRuntimeFromFramework)(config.framework);
|
|
87
|
+
if (frameworkRuntime && frameworkRuntime !== config.runtime) {
|
|
88
|
+
return {
|
|
89
|
+
code: "RUNTIME_FRAMEWORK_MISMATCH",
|
|
90
|
+
message: `Service "${name}" has conflicting runtime/framework: runtime "${config.runtime}" is incompatible with framework "${config.framework}" (runtime "${frameworkRuntime}").`,
|
|
91
|
+
serviceName: name
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!config.framework && !config.entrypoint) {
|
|
96
|
+
return {
|
|
97
|
+
code: "MISSING_SERVICE_CONFIG",
|
|
98
|
+
message: `Service "${name}" must specify "framework" or "entrypoint".`,
|
|
99
|
+
serviceName: name
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
async function resolveConfiguredServiceV2(name, config, fs) {
|
|
105
|
+
const root = config.root;
|
|
106
|
+
const normalizedRoot = import_path.posix.normalize(root);
|
|
107
|
+
const serviceFsResult = normalizedRoot === "." ? { fs } : await (0, import_resolve.getServiceFs)(fs, name, root);
|
|
108
|
+
if (serviceFsResult.error) {
|
|
109
|
+
return { error: serviceFsResult.error };
|
|
110
|
+
}
|
|
111
|
+
const serviceFs = serviceFsResult.fs;
|
|
112
|
+
const rawEntrypoint = config.entrypoint;
|
|
113
|
+
const moduleAttr = typeof rawEntrypoint === "string" ? (0, import_resolve.parsePyModuleAttrEntrypoint)(rawEntrypoint) : null;
|
|
114
|
+
let normalizedEntrypoint;
|
|
115
|
+
let entrypointIsDirectory = false;
|
|
116
|
+
if (typeof rawEntrypoint === "string") {
|
|
117
|
+
const entrypointToResolve = moduleAttr ? moduleAttr.filePath : rawEntrypoint;
|
|
118
|
+
const resolved = await (0, import_resolve.resolveEntrypointPath)({
|
|
119
|
+
fs: serviceFs,
|
|
120
|
+
serviceName: name,
|
|
121
|
+
entrypoint: entrypointToResolve
|
|
122
|
+
});
|
|
123
|
+
if (resolved.error) {
|
|
124
|
+
return { error: resolved.error };
|
|
125
|
+
}
|
|
126
|
+
normalizedEntrypoint = resolved.entrypoint?.normalized;
|
|
127
|
+
entrypointIsDirectory = Boolean(resolved.entrypoint?.isDirectory);
|
|
128
|
+
}
|
|
129
|
+
const entrypointFile = entrypointIsDirectory || !normalizedEntrypoint ? void 0 : normalizedEntrypoint;
|
|
130
|
+
const inferredRuntime = (0, import_utils.inferServiceRuntime)({
|
|
131
|
+
runtime: config.runtime,
|
|
132
|
+
framework: config.framework,
|
|
133
|
+
entrypoint: entrypointFile
|
|
134
|
+
});
|
|
135
|
+
let framework = config.framework;
|
|
136
|
+
if (!framework && normalizedEntrypoint) {
|
|
137
|
+
const workspace = entrypointIsDirectory ? normalizedEntrypoint : import_path.posix.dirname(normalizedEntrypoint) || ".";
|
|
138
|
+
const detection = await (0, import_resolve.detectFrameworkFromWorkspace)({
|
|
139
|
+
fs: serviceFs,
|
|
140
|
+
workspace,
|
|
141
|
+
serviceName: name,
|
|
142
|
+
runtime: inferredRuntime
|
|
143
|
+
});
|
|
144
|
+
if (detection.error) {
|
|
145
|
+
return { error: detection.error };
|
|
146
|
+
}
|
|
147
|
+
framework = detection.framework;
|
|
148
|
+
}
|
|
149
|
+
if (entrypointIsDirectory && !framework) {
|
|
150
|
+
return {
|
|
151
|
+
error: {
|
|
152
|
+
code: "MISSING_SERVICE_FRAMEWORK",
|
|
153
|
+
message: `Service "${name}" uses directory entrypoint "${config.entrypoint}" but no framework could be detected. Specify "framework" explicitly or use a file entrypoint.`,
|
|
154
|
+
serviceName: name
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const frameworkDefinition = framework ? frameworksBySlug.get(framework) : void 0;
|
|
159
|
+
let builderUse;
|
|
160
|
+
let builderSrc;
|
|
161
|
+
if (framework) {
|
|
162
|
+
builderUse = (0, import_build_utils.isNodeBackendFramework)(framework) ? "@vercel/backends" : frameworkDefinition?.useRuntime?.use || "@vercel/static-build";
|
|
163
|
+
builderSrc = entrypointFile || frameworkDefinition?.useRuntime?.src || "package.json";
|
|
164
|
+
} else {
|
|
165
|
+
if (!inferredRuntime) {
|
|
166
|
+
return {
|
|
167
|
+
error: {
|
|
168
|
+
code: "MISSING_SERVICE_CONFIG",
|
|
169
|
+
message: `Service "${name}" must specify "framework" or a runtime-resolvable "entrypoint".`,
|
|
170
|
+
serviceName: name
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
builderUse = inferredRuntime === "node" ? "@vercel/backends" : (0, import_utils.getBuilderForRuntime)(inferredRuntime);
|
|
175
|
+
builderSrc = entrypointFile;
|
|
176
|
+
}
|
|
177
|
+
const isRoot = normalizedRoot === ".";
|
|
178
|
+
const projectRelativeSrc = isRoot ? builderSrc : import_path.posix.join(normalizedRoot, builderSrc);
|
|
179
|
+
const builderConfig = { zeroConfig: true };
|
|
180
|
+
if (builderUse === "@vercel/backends") {
|
|
181
|
+
builderConfig.serviceName = name;
|
|
182
|
+
}
|
|
183
|
+
if (framework) {
|
|
184
|
+
builderConfig.framework = framework;
|
|
185
|
+
}
|
|
186
|
+
if (!isRoot) {
|
|
187
|
+
builderConfig.workspace = normalizedRoot;
|
|
188
|
+
}
|
|
189
|
+
if (moduleAttr) {
|
|
190
|
+
builderConfig.handlerFunction = moduleAttr.attrName;
|
|
191
|
+
}
|
|
192
|
+
const runtime = import_types.STATIC_BUILDERS.has(builderUse) ? void 0 : inferredRuntime;
|
|
193
|
+
return {
|
|
194
|
+
service: {
|
|
195
|
+
schema: "experimentalServicesV2",
|
|
196
|
+
name,
|
|
197
|
+
root,
|
|
198
|
+
framework,
|
|
199
|
+
runtime,
|
|
200
|
+
entrypoint: entrypointFile,
|
|
201
|
+
builder: {
|
|
202
|
+
src: projectRelativeSrc,
|
|
203
|
+
use: builderUse,
|
|
204
|
+
config: builderConfig
|
|
205
|
+
},
|
|
206
|
+
installCommand: config.installCommand,
|
|
207
|
+
buildCommand: config.buildCommand,
|
|
208
|
+
devCommand: config.devCommand,
|
|
209
|
+
ignoreCommand: config.ignoreCommand,
|
|
210
|
+
outputDirectory: config.outputDirectory,
|
|
211
|
+
bindings: config.bindings,
|
|
212
|
+
functions: config.functions,
|
|
213
|
+
headers: config.headers,
|
|
214
|
+
redirects: config.redirects,
|
|
215
|
+
rewrites: config.rewrites,
|
|
216
|
+
routes: config.routes,
|
|
217
|
+
cleanUrls: config.cleanUrls,
|
|
218
|
+
trailingSlash: config.trailingSlash
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
async function resolveAllConfiguredServicesV2(services, fs) {
|
|
223
|
+
const resolved = [];
|
|
224
|
+
const errors = [];
|
|
225
|
+
for (const name of Object.keys(services)) {
|
|
226
|
+
const config = services[name];
|
|
227
|
+
const validationError = validateServiceConfigV2(name, config);
|
|
228
|
+
if (validationError) {
|
|
229
|
+
errors.push(validationError);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const { service, error } = await resolveConfiguredServiceV2(
|
|
233
|
+
name,
|
|
234
|
+
config,
|
|
235
|
+
fs
|
|
236
|
+
);
|
|
237
|
+
if (error) {
|
|
238
|
+
errors.push(error);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (service) {
|
|
242
|
+
resolved.push(service);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const serviceNames = new Set(Object.keys(services));
|
|
246
|
+
for (const service of resolved) {
|
|
247
|
+
for (const binding of service.bindings ?? []) {
|
|
248
|
+
if (!serviceNames.has(binding.service)) {
|
|
249
|
+
errors.push({
|
|
250
|
+
code: "UNKNOWN_SERVICE_BINDING",
|
|
251
|
+
message: `Service "${service.name}" declares a binding to unknown service "${binding.service}".`,
|
|
252
|
+
serviceName: service.name
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return { services: resolved, errors };
|
|
258
|
+
}
|
|
259
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
260
|
+
0 && (module.exports = {
|
|
261
|
+
resolveAllConfiguredServicesV2,
|
|
262
|
+
resolveConfiguredServiceV2,
|
|
263
|
+
validateServiceConfigV2
|
|
264
|
+
});
|
|
@@ -1,10 +1,26 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ExperimentalService, ConfiguredServices, ExperimentalServiceConfig, ServiceDetectionError, ServiceRuntime } from './types';
|
|
2
2
|
import type { DetectorFilesystem } from '../detectors/filesystem';
|
|
3
|
+
export declare function parsePyModuleAttrEntrypoint(entrypoint: string): {
|
|
4
|
+
attrName: string;
|
|
5
|
+
filePath: string;
|
|
6
|
+
} | null;
|
|
3
7
|
type ConfiguredServiceConfig = ExperimentalServiceConfig;
|
|
4
8
|
interface ResolvedEntrypointPath {
|
|
5
9
|
normalized: string;
|
|
6
10
|
isDirectory: boolean;
|
|
7
11
|
}
|
|
12
|
+
export declare function getServiceFs(fs: DetectorFilesystem, serviceName: string, root?: string): Promise<{
|
|
13
|
+
fs: DetectorFilesystem;
|
|
14
|
+
error?: ServiceDetectionError;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function resolveEntrypointPath({ fs, serviceName, entrypoint, }: {
|
|
17
|
+
fs: DetectorFilesystem;
|
|
18
|
+
serviceName: string;
|
|
19
|
+
entrypoint: string;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
entrypoint?: ResolvedEntrypointPath;
|
|
22
|
+
error?: ServiceDetectionError;
|
|
23
|
+
}>;
|
|
8
24
|
type RoutePrefixSource = 'configured' | 'generated';
|
|
9
25
|
interface ResolveConfiguredServiceOptions {
|
|
10
26
|
name: string;
|
|
@@ -19,6 +35,20 @@ interface ResolveConfiguredServiceOptions {
|
|
|
19
35
|
interface ResolveAllConfiguredServicesOptions {
|
|
20
36
|
requireFileEntrypointForBackendRuntimes?: boolean;
|
|
21
37
|
}
|
|
38
|
+
export declare function inferWorkspaceFromNearestManifest({ fs, entrypoint, runtime, }: {
|
|
39
|
+
fs: DetectorFilesystem;
|
|
40
|
+
entrypoint?: string;
|
|
41
|
+
runtime?: ServiceRuntime;
|
|
42
|
+
}): Promise<string | undefined>;
|
|
43
|
+
export declare function detectFrameworkFromWorkspace({ fs, workspace, serviceName, runtime, }: {
|
|
44
|
+
fs: DetectorFilesystem;
|
|
45
|
+
workspace: string;
|
|
46
|
+
serviceName: string;
|
|
47
|
+
runtime?: ServiceRuntime;
|
|
48
|
+
}): Promise<{
|
|
49
|
+
framework?: string;
|
|
50
|
+
error?: ServiceDetectionError;
|
|
51
|
+
}>;
|
|
22
52
|
/**
|
|
23
53
|
* Validate a service configuration from vercel.json services.
|
|
24
54
|
*/
|
|
@@ -27,13 +57,13 @@ export declare function validateServiceEntrypoint(name: string, config: Configur
|
|
|
27
57
|
/**
|
|
28
58
|
* Resolve a single service from user configuration.
|
|
29
59
|
*/
|
|
30
|
-
export declare function resolveConfiguredService(options: ResolveConfiguredServiceOptions): Promise<
|
|
60
|
+
export declare function resolveConfiguredService(options: ResolveConfiguredServiceOptions): Promise<ExperimentalService>;
|
|
31
61
|
/**
|
|
32
62
|
* Resolve all services from vercel.json services.
|
|
33
63
|
* Validates each service configuration.
|
|
34
64
|
*/
|
|
35
65
|
export declare function resolveAllConfiguredServices(services: ConfiguredServices, fs: DetectorFilesystem, routePrefixSource?: RoutePrefixSource, options?: ResolveAllConfiguredServicesOptions): Promise<{
|
|
36
|
-
services:
|
|
66
|
+
services: ExperimentalService[];
|
|
37
67
|
errors: ServiceDetectionError[];
|
|
38
68
|
}>;
|
|
39
69
|
export {};
|
package/dist/services/resolve.js
CHANGED
|
@@ -18,8 +18,13 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var resolve_exports = {};
|
|
20
20
|
__export(resolve_exports, {
|
|
21
|
+
detectFrameworkFromWorkspace: () => detectFrameworkFromWorkspace,
|
|
22
|
+
getServiceFs: () => getServiceFs,
|
|
23
|
+
inferWorkspaceFromNearestManifest: () => inferWorkspaceFromNearestManifest,
|
|
24
|
+
parsePyModuleAttrEntrypoint: () => parsePyModuleAttrEntrypoint,
|
|
21
25
|
resolveAllConfiguredServices: () => resolveAllConfiguredServices,
|
|
22
26
|
resolveConfiguredService: () => resolveConfiguredService,
|
|
27
|
+
resolveEntrypointPath: () => resolveEntrypointPath,
|
|
23
28
|
validateServiceConfig: () => validateServiceConfig,
|
|
24
29
|
validateServiceEntrypoint: () => validateServiceEntrypoint
|
|
25
30
|
});
|
|
@@ -666,6 +671,7 @@ async function resolveConfiguredService(options) {
|
|
|
666
671
|
builderConfig.handlerFunction = moduleAttrParsed.attrName;
|
|
667
672
|
}
|
|
668
673
|
return {
|
|
674
|
+
schema: "experimentalServices",
|
|
669
675
|
name,
|
|
670
676
|
type,
|
|
671
677
|
trigger,
|
|
@@ -870,8 +876,13 @@ function validateEnvRefs(env, serviceName, servicesByName, errors) {
|
|
|
870
876
|
}
|
|
871
877
|
// Annotate the CommonJS export names for ESM import in node:
|
|
872
878
|
0 && (module.exports = {
|
|
879
|
+
detectFrameworkFromWorkspace,
|
|
880
|
+
getServiceFs,
|
|
881
|
+
inferWorkspaceFromNearestManifest,
|
|
882
|
+
parsePyModuleAttrEntrypoint,
|
|
873
883
|
resolveAllConfiguredServices,
|
|
874
884
|
resolveConfiguredService,
|
|
885
|
+
resolveEntrypointPath,
|
|
875
886
|
validateServiceConfig,
|
|
876
887
|
validateServiceEntrypoint
|
|
877
888
|
});
|
package/dist/services/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Route } from '@vercel/routing-utils';
|
|
2
|
-
import type { DetectEntrypointFn, EnvVar, EnvVars, ExperimentalServiceConfig, ExperimentalServiceGroups, ExperimentalServices, ServiceRuntime, ServiceType, ServiceRefEnvVar, Service, Builder } from '@vercel/build-utils';
|
|
2
|
+
import type { DetectEntrypointFn, EnvVar, EnvVars, ExperimentalServiceConfig, ExperimentalServiceV2Config, ExperimentalServiceGroups, ExperimentalServices, ExperimentalServicesV2, ExperimentalServiceV2Binding, ExperimentalService, ExperimentalServiceV2, ServiceRuntime, ServiceType, ServiceRefEnvVar, Service, Builder } from '@vercel/build-utils';
|
|
3
3
|
import type { DetectorFilesystem } from '../detectors/filesystem';
|
|
4
|
-
export type { DetectEntrypointFn, EnvVar, EnvVars, ExperimentalServiceConfig, ExperimentalServiceGroups, ExperimentalServices, ServiceRuntime, ServiceType, ServiceRefEnvVar, Service, Builder, };
|
|
4
|
+
export type { DetectEntrypointFn, EnvVar, EnvVars, ExperimentalServiceConfig, ExperimentalServiceGroups, ExperimentalServices, ExperimentalServiceV2Config, ExperimentalServicesV2, ExperimentalServiceV2Binding, ExperimentalService, ExperimentalServiceV2, ServiceRuntime, ServiceType, ServiceRefEnvVar, Service, Builder, };
|
|
5
5
|
/**
|
|
6
6
|
* @deprecated Use `Service` instead
|
|
7
7
|
*/
|
|
@@ -9,7 +9,7 @@ export type ResolvedService = Service;
|
|
|
9
9
|
export interface DetectServicesOptions {
|
|
10
10
|
fs: DetectorFilesystem;
|
|
11
11
|
configuredServices?: ConfiguredServices;
|
|
12
|
-
configuredServicesType?:
|
|
12
|
+
configuredServicesType?: ConfiguredServicesType;
|
|
13
13
|
/**
|
|
14
14
|
* Working directory path (relative to fs root).
|
|
15
15
|
* If provided, vercel.json is read from this path.
|
|
@@ -42,7 +42,8 @@ export interface ServicesRoutes {
|
|
|
42
42
|
*/
|
|
43
43
|
workers: Route[];
|
|
44
44
|
}
|
|
45
|
-
export type
|
|
45
|
+
export type ConfiguredServicesType = 'experimentalServices' | 'experimentalServicesV2';
|
|
46
|
+
export type ConfiguredServices = ExperimentalServices | ExperimentalServicesV2;
|
|
46
47
|
export type InferredServicesConfig = ExperimentalServices;
|
|
47
48
|
export interface ResolvedServicesResult {
|
|
48
49
|
services: Service[];
|
|
@@ -55,13 +56,13 @@ export interface ResolvedServicesResult {
|
|
|
55
56
|
export interface InferredServicesResult {
|
|
56
57
|
source: 'layout' | 'procfile' | 'railway' | 'render';
|
|
57
58
|
config: InferredServicesConfig;
|
|
58
|
-
services:
|
|
59
|
+
services: ExperimentalService[];
|
|
59
60
|
warnings: ServiceDetectionWarning[];
|
|
60
61
|
}
|
|
61
62
|
export interface DetectServicesResult extends ResolvedServicesResult {
|
|
62
63
|
/**
|
|
63
64
|
* Source of service definitions:
|
|
64
|
-
* - `configured`: loaded from explicit project configuration (`vercel.json#experimentalServices`)
|
|
65
|
+
* - `configured`: loaded from explicit project configuration (`vercel.json#experimentalServices` or `vercel.json#experimentalServicesV2`)
|
|
65
66
|
* - `auto-detected`: inferred from project structure
|
|
66
67
|
*/
|
|
67
68
|
resolved: ResolvedServicesResult;
|
package/dist/services/utils.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { INTERNAL_SERVICE_PREFIX, getInternalServiceFunctionPath, getInternalServiceCronPathPrefix, getInternalServiceCronPath } from '@vercel/build-utils';
|
|
2
2
|
import type { Framework } from '@vercel/frameworks';
|
|
3
3
|
import type { DetectorFilesystem } from '../detectors/filesystem';
|
|
4
|
-
import type { ServiceRuntime, ExperimentalServices, ServiceDetectionError, ServiceDetectionWarning, ResolvedService } from './types';
|
|
4
|
+
import type { ServiceRuntime, ExperimentalServices, ExperimentalServicesV2, ServiceDetectionError, ServiceDetectionWarning, ResolvedService } from './types';
|
|
5
5
|
export declare const DETECTION_FRAMEWORKS: Framework[];
|
|
6
6
|
export { INTERNAL_SERVICE_PREFIX, getInternalServiceFunctionPath, getInternalServiceCronPathPrefix, getInternalServiceCronPath, };
|
|
7
7
|
export declare function hasFile(fs: DetectorFilesystem, filePath: string): Promise<boolean>;
|
|
@@ -56,6 +56,7 @@ export declare function inferServiceRuntime(config: {
|
|
|
56
56
|
export interface ReadVercelConfigResult {
|
|
57
57
|
config: {
|
|
58
58
|
experimentalServices?: ExperimentalServices;
|
|
59
|
+
experimentalServicesV2?: ExperimentalServicesV2;
|
|
59
60
|
} | null;
|
|
60
61
|
error: ServiceDetectionError | null;
|
|
61
62
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/fs-detectors",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.8.0",
|
|
4
4
|
"description": "Vercel filesystem detectors",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
"minimatch": "3.1.2",
|
|
21
21
|
"semver": "6.3.1",
|
|
22
22
|
"smol-toml": "1.5.2",
|
|
23
|
-
"@vercel/
|
|
24
|
-
"@vercel/
|
|
25
|
-
"@vercel/frameworks": "3.
|
|
26
|
-
"@vercel/
|
|
23
|
+
"@vercel/error-utils": "2.2.0",
|
|
24
|
+
"@vercel/build-utils": "13.27.2",
|
|
25
|
+
"@vercel/frameworks": "3.28.0",
|
|
26
|
+
"@vercel/routing-utils": "6.2.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/glob": "7.2.0",
|