@vercel/fs-detectors 5.9.0 → 5.10.1
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 +1 -0
- package/dist/detect-builders.js +2 -2
- package/dist/services/detect-services.d.ts +3 -3
- package/dist/services/detect-services.js +55 -4
- package/dist/services/get-services-builders.d.ts +1 -0
- package/dist/services/get-services-builders.js +4 -0
- package/dist/services/resolve.js +25 -4
- package/dist/services/types.d.ts +2 -0
- package/package.json +4 -4
|
@@ -37,6 +37,7 @@ export declare function detectBuilders(files: string[], pkg?: PackageJson | unde
|
|
|
37
37
|
builders: Builder[] | null;
|
|
38
38
|
errors: ErrorResponse[] | null;
|
|
39
39
|
warnings: ErrorResponse[];
|
|
40
|
+
hostRewriteRoutes?: Route[] | null;
|
|
40
41
|
defaultRoutes: Route[] | null;
|
|
41
42
|
redirectRoutes: Route[] | null;
|
|
42
43
|
rewriteRoutes: Route[] | null;
|
package/dist/detect-builders.js
CHANGED
|
@@ -460,10 +460,10 @@ function validateFunctions({ functions = {} }) {
|
|
|
460
460
|
message: "Function must contain at least one property."
|
|
461
461
|
};
|
|
462
462
|
}
|
|
463
|
-
if (func.maxDuration !== void 0 && (func.maxDuration < 1 || func.maxDuration > 900 || !Number.isInteger(func.maxDuration))) {
|
|
463
|
+
if (func.maxDuration !== void 0 && func.maxDuration !== "max" && (func.maxDuration < 1 || func.maxDuration > 900 || !Number.isInteger(func.maxDuration))) {
|
|
464
464
|
return {
|
|
465
465
|
code: "invalid_function_duration",
|
|
466
|
-
message:
|
|
466
|
+
message: 'Functions must have a maxDuration between 1 and 900, or "max".'
|
|
467
467
|
};
|
|
468
468
|
}
|
|
469
469
|
if (func.memory !== void 0 && (func.memory < 128 || func.memory > 10240)) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type DetectServicesOptions, type DetectServicesResult, type
|
|
1
|
+
import { type DetectServicesOptions, type DetectServicesResult, type Service, type ServicesRoutes } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Detect and resolve services within a project.
|
|
4
4
|
*
|
|
5
|
-
* Reads vercel.json and resolves `experimentalServices` into
|
|
5
|
+
* Reads vercel.json and resolves `experimentalServices` into Service objects.
|
|
6
6
|
* Returns an error if no services are configured.
|
|
7
7
|
*/
|
|
8
8
|
export declare function detectServices(options: DetectServicesOptions): Promise<DetectServicesResult>;
|
|
@@ -34,4 +34,4 @@ export declare function detectServices(options: DetectServicesOptions): Promise<
|
|
|
34
34
|
* Internal cron callback routes under `/_svc/{serviceName}/crons/{entry}/{handler}`
|
|
35
35
|
* that rewrite to `/_svc/{serviceName}/index`.
|
|
36
36
|
*/
|
|
37
|
-
export declare function generateServicesRoutes(services:
|
|
37
|
+
export declare function generateServicesRoutes(services: Service[]): ServicesRoutes;
|
|
@@ -26,6 +26,10 @@ var import_routing_utils = require("@vercel/routing-utils");
|
|
|
26
26
|
var import_utils = require("./utils");
|
|
27
27
|
var import_resolve = require("./resolve");
|
|
28
28
|
var import_auto_detect = require("./auto-detect");
|
|
29
|
+
const PREVIEW_DOMAIN_MISSING = [
|
|
30
|
+
{ type: "host", value: { suf: ".vercel.app" } },
|
|
31
|
+
{ type: "host", value: { suf: ".vercel.dev" } }
|
|
32
|
+
];
|
|
29
33
|
async function detectServices(options) {
|
|
30
34
|
const { fs, workPath } = options;
|
|
31
35
|
const scopedFs = workPath ? fs.chdir(workPath) : fs;
|
|
@@ -34,7 +38,13 @@ async function detectServices(options) {
|
|
|
34
38
|
return {
|
|
35
39
|
services: [],
|
|
36
40
|
source: "configured",
|
|
37
|
-
routes: {
|
|
41
|
+
routes: {
|
|
42
|
+
hostRewrites: [],
|
|
43
|
+
rewrites: [],
|
|
44
|
+
defaults: [],
|
|
45
|
+
crons: [],
|
|
46
|
+
workers: []
|
|
47
|
+
},
|
|
38
48
|
errors: [configError],
|
|
39
49
|
warnings: []
|
|
40
50
|
};
|
|
@@ -47,7 +57,13 @@ async function detectServices(options) {
|
|
|
47
57
|
return {
|
|
48
58
|
services: [],
|
|
49
59
|
source: "auto-detected",
|
|
50
|
-
routes: {
|
|
60
|
+
routes: {
|
|
61
|
+
hostRewrites: [],
|
|
62
|
+
rewrites: [],
|
|
63
|
+
defaults: [],
|
|
64
|
+
crons: [],
|
|
65
|
+
workers: []
|
|
66
|
+
},
|
|
51
67
|
errors: autoResult.errors,
|
|
52
68
|
warnings: []
|
|
53
69
|
};
|
|
@@ -70,7 +86,13 @@ async function detectServices(options) {
|
|
|
70
86
|
return {
|
|
71
87
|
services: [],
|
|
72
88
|
source: "auto-detected",
|
|
73
|
-
routes: {
|
|
89
|
+
routes: {
|
|
90
|
+
hostRewrites: [],
|
|
91
|
+
rewrites: [],
|
|
92
|
+
defaults: [],
|
|
93
|
+
crons: [],
|
|
94
|
+
workers: []
|
|
95
|
+
},
|
|
74
96
|
errors: [
|
|
75
97
|
{
|
|
76
98
|
code: "NO_SERVICES_CONFIGURED",
|
|
@@ -95,6 +117,7 @@ async function detectServices(options) {
|
|
|
95
117
|
};
|
|
96
118
|
}
|
|
97
119
|
function generateServicesRoutes(services) {
|
|
120
|
+
const hostRewrites = [];
|
|
98
121
|
const rewrites = [];
|
|
99
122
|
const defaults = [];
|
|
100
123
|
const crons = [];
|
|
@@ -107,6 +130,25 @@ function generateServicesRoutes(services) {
|
|
|
107
130
|
const { routePrefix } = service;
|
|
108
131
|
const normalizedPrefix = routePrefix.slice(1);
|
|
109
132
|
const ownershipGuard = (0, import_routing_utils.getOwnershipGuard)(routePrefix, allWebPrefixes);
|
|
133
|
+
const hostCondition = getHostCondition(service);
|
|
134
|
+
if (hostCondition && routePrefix !== "/") {
|
|
135
|
+
const normalizedRoutePrefix = (0, import_routing_utils.normalizeRoutePrefix)(routePrefix);
|
|
136
|
+
const escapedPrefix = escapeRegex(normalizedRoutePrefix.slice(1));
|
|
137
|
+
hostRewrites.push({
|
|
138
|
+
src: "^/$",
|
|
139
|
+
dest: normalizedRoutePrefix,
|
|
140
|
+
has: hostCondition,
|
|
141
|
+
missing: PREVIEW_DOMAIN_MISSING,
|
|
142
|
+
check: true
|
|
143
|
+
});
|
|
144
|
+
hostRewrites.push({
|
|
145
|
+
src: `^/(?!${escapedPrefix}(?:/|$))(.*)$`,
|
|
146
|
+
dest: `${normalizedRoutePrefix}/$1`,
|
|
147
|
+
has: hostCondition,
|
|
148
|
+
missing: PREVIEW_DOMAIN_MISSING,
|
|
149
|
+
check: true
|
|
150
|
+
});
|
|
151
|
+
}
|
|
110
152
|
if ((0, import_utils.isRouteOwningBuilder)(service)) {
|
|
111
153
|
continue;
|
|
112
154
|
}
|
|
@@ -176,7 +218,7 @@ function generateServicesRoutes(services) {
|
|
|
176
218
|
check: true
|
|
177
219
|
});
|
|
178
220
|
}
|
|
179
|
-
return { rewrites, defaults, crons, workers };
|
|
221
|
+
return { hostRewrites, rewrites, defaults, crons, workers };
|
|
180
222
|
}
|
|
181
223
|
function escapeRegex(str) {
|
|
182
224
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -191,6 +233,15 @@ function getWebRoutePrefixes(services) {
|
|
|
191
233
|
}
|
|
192
234
|
return Array.from(unique);
|
|
193
235
|
}
|
|
236
|
+
function getHostCondition(service) {
|
|
237
|
+
if (service.type !== "web") {
|
|
238
|
+
return void 0;
|
|
239
|
+
}
|
|
240
|
+
if (typeof service.subdomain === "string" && service.subdomain.length > 0) {
|
|
241
|
+
return [{ type: "host", value: { pre: `${service.subdomain}.` } }];
|
|
242
|
+
}
|
|
243
|
+
return void 0;
|
|
244
|
+
}
|
|
194
245
|
// Annotate the CommonJS export names for ESM import in node:
|
|
195
246
|
0 && (module.exports = {
|
|
196
247
|
detectServices,
|
|
@@ -14,6 +14,7 @@ export interface ServicesBuildersResult {
|
|
|
14
14
|
builders: Builder[] | null;
|
|
15
15
|
errors: ErrorResponse[] | null;
|
|
16
16
|
warnings: ErrorResponse[];
|
|
17
|
+
hostRewriteRoutes: Route[] | null;
|
|
17
18
|
defaultRoutes: Route[] | null;
|
|
18
19
|
redirectRoutes: Route[] | null;
|
|
19
20
|
rewriteRoutes: Route[] | null;
|
|
@@ -35,6 +35,7 @@ async function getServicesBuilders(options) {
|
|
|
35
35
|
}
|
|
36
36
|
],
|
|
37
37
|
warnings: [],
|
|
38
|
+
hostRewriteRoutes: null,
|
|
38
39
|
defaultRoutes: null,
|
|
39
40
|
redirectRoutes: null,
|
|
40
41
|
rewriteRoutes: null,
|
|
@@ -55,6 +56,7 @@ async function getServicesBuilders(options) {
|
|
|
55
56
|
message: e.message
|
|
56
57
|
})),
|
|
57
58
|
warnings: warningResponses,
|
|
59
|
+
hostRewriteRoutes: null,
|
|
58
60
|
defaultRoutes: null,
|
|
59
61
|
redirectRoutes: null,
|
|
60
62
|
rewriteRoutes: null,
|
|
@@ -71,6 +73,7 @@ async function getServicesBuilders(options) {
|
|
|
71
73
|
}
|
|
72
74
|
],
|
|
73
75
|
warnings: warningResponses,
|
|
76
|
+
hostRewriteRoutes: null,
|
|
74
77
|
defaultRoutes: null,
|
|
75
78
|
redirectRoutes: null,
|
|
76
79
|
rewriteRoutes: null,
|
|
@@ -82,6 +85,7 @@ async function getServicesBuilders(options) {
|
|
|
82
85
|
builders: builders.length > 0 ? builders : null,
|
|
83
86
|
errors: null,
|
|
84
87
|
warnings: warningResponses,
|
|
88
|
+
hostRewriteRoutes: result.routes.hostRewrites.length > 0 ? result.routes.hostRewrites : null,
|
|
85
89
|
defaultRoutes: result.routes.defaults.length > 0 ? result.routes.defaults : null,
|
|
86
90
|
redirectRoutes: [],
|
|
87
91
|
rewriteRoutes: result.routes.rewrites.length > 0 || result.routes.workers.length > 0 || result.routes.crons.length > 0 ? [
|
package/dist/services/resolve.js
CHANGED
|
@@ -52,6 +52,7 @@ function parsePyModuleAttrEntrypoint(entrypoint) {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
const SERVICE_NAME_REGEX = /^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;
|
|
55
|
+
const DNS_LABEL_RE = /^(?!-)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
|
|
55
56
|
function normalizeServiceEntrypoint(entrypoint) {
|
|
56
57
|
const normalized = import_path.posix.normalize(entrypoint);
|
|
57
58
|
return normalized === "" ? "." : normalized;
|
|
@@ -182,10 +183,19 @@ function validateServiceConfig(name, config) {
|
|
|
182
183
|
};
|
|
183
184
|
}
|
|
184
185
|
const serviceType = config.type || "web";
|
|
185
|
-
|
|
186
|
+
const hasRoutePrefix = typeof config.routePrefix === "string";
|
|
187
|
+
const hasSubdomain = typeof config.subdomain === "string";
|
|
188
|
+
if (hasSubdomain && !DNS_LABEL_RE.test(config.subdomain)) {
|
|
189
|
+
return {
|
|
190
|
+
code: "INVALID_SUBDOMAIN",
|
|
191
|
+
message: `Web service "${name}" has invalid subdomain "${config.subdomain}". Use a single DNS label such as "api".`,
|
|
192
|
+
serviceName: name
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (serviceType === "web" && !hasRoutePrefix && !hasSubdomain) {
|
|
186
196
|
return {
|
|
187
197
|
code: "MISSING_ROUTE_PREFIX",
|
|
188
|
-
message: `Web service "${name}" must specify "routePrefix".`,
|
|
198
|
+
message: `Web service "${name}" must specify at least one of "routePrefix" or "subdomain".`,
|
|
189
199
|
serviceName: name
|
|
190
200
|
};
|
|
191
201
|
}
|
|
@@ -203,6 +213,13 @@ function validateServiceConfig(name, config) {
|
|
|
203
213
|
serviceName: name
|
|
204
214
|
};
|
|
205
215
|
}
|
|
216
|
+
if ((serviceType === "worker" || serviceType === "cron") && hasSubdomain) {
|
|
217
|
+
return {
|
|
218
|
+
code: "INVALID_HOST_ROUTING_CONFIG",
|
|
219
|
+
message: `${serviceType === "worker" ? "Worker" : "Cron"} service "${name}" cannot have "subdomain". Only web services should specify subdomain routing.`,
|
|
220
|
+
serviceName: name
|
|
221
|
+
};
|
|
222
|
+
}
|
|
206
223
|
if (serviceType === "cron" && !config.schedule) {
|
|
207
224
|
return {
|
|
208
225
|
code: "MISSING_CRON_SCHEDULE",
|
|
@@ -337,7 +354,10 @@ async function resolveConfiguredService(options) {
|
|
|
337
354
|
builderUse = (0, import_utils.getBuilderForRuntime)(inferredRuntime);
|
|
338
355
|
builderSrc = resolvedEntrypointFile;
|
|
339
356
|
}
|
|
340
|
-
const
|
|
357
|
+
const normalizedSubdomain = type === "web" && typeof config.subdomain === "string" ? config.subdomain.toLowerCase() : void 0;
|
|
358
|
+
const defaultRoutePrefix = type === "web" && normalizedSubdomain ? `/_/${name}` : void 0;
|
|
359
|
+
const routePrefix = type === "web" && (config.routePrefix || defaultRoutePrefix) ? (config.routePrefix || defaultRoutePrefix).startsWith("/") ? config.routePrefix || defaultRoutePrefix : `/${config.routePrefix || defaultRoutePrefix}` : void 0;
|
|
360
|
+
const resolvedRoutePrefixSource = type === "web" && typeof routePrefix === "string" ? config.routePrefix ? routePrefixSource : "generated" : void 0;
|
|
341
361
|
const isRoot = workspace === ".";
|
|
342
362
|
if (!isRoot) {
|
|
343
363
|
builderSrc = import_path.posix.join(workspace, builderSrc);
|
|
@@ -373,7 +393,8 @@ async function resolveConfiguredService(options) {
|
|
|
373
393
|
workspace,
|
|
374
394
|
entrypoint: resolvedEntrypointFile,
|
|
375
395
|
routePrefix,
|
|
376
|
-
routePrefixSource:
|
|
396
|
+
routePrefixSource: resolvedRoutePrefixSource,
|
|
397
|
+
subdomain: normalizedSubdomain,
|
|
377
398
|
framework: config.framework,
|
|
378
399
|
builder: {
|
|
379
400
|
src: builderSrc,
|
package/dist/services/types.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export interface DetectServicesOptions {
|
|
|
15
15
|
workPath?: string;
|
|
16
16
|
}
|
|
17
17
|
export interface ServicesRoutes {
|
|
18
|
+
/** Host-based rewrite routes for subdomain-mounted web services */
|
|
19
|
+
hostRewrites: Route[];
|
|
18
20
|
/** Rewrite routes for non-root web services (prefix-based) */
|
|
19
21
|
rewrites: Route[];
|
|
20
22
|
/** Default routes (catch-all for root web service) */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/fs-detectors",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.10.1",
|
|
4
4
|
"description": "Vercel filesystem detectors",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"json5": "2.2.2",
|
|
20
20
|
"minimatch": "3.1.2",
|
|
21
21
|
"semver": "6.3.1",
|
|
22
|
-
"@vercel/frameworks": "3.20.0",
|
|
23
22
|
"@vercel/error-utils": "2.0.3",
|
|
24
|
-
"@vercel/routing-utils": "6.0.2"
|
|
23
|
+
"@vercel/routing-utils": "6.0.2",
|
|
24
|
+
"@vercel/frameworks": "3.21.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/glob": "7.2.0",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@types/semver": "7.3.10",
|
|
33
33
|
"jest-junit": "16.0.0",
|
|
34
34
|
"typescript": "4.9.5",
|
|
35
|
-
"@vercel/build-utils": "13.
|
|
35
|
+
"@vercel/build-utils": "13.8.0"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "node ../../utils/build.mjs",
|