@vercel/fs-detectors 7.1.7 → 7.2.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.
@@ -0,0 +1,424 @@
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 detect_services_exports = {};
20
+ __export(detect_services_exports, {
21
+ detectServices: () => detectServices,
22
+ generateServiceRewrites: () => generateServiceRewrites,
23
+ generateServicesRoutes: () => generateServicesRoutes
24
+ });
25
+ module.exports = __toCommonJS(detect_services_exports);
26
+ var import_build_utils = require("@vercel/build-utils");
27
+ var import_routing_utils = require("@vercel/routing-utils");
28
+ var import_framework = require("./runtimes/framework");
29
+ var import_utils = require("./utils");
30
+ var import_resolve = require("./resolve");
31
+ var import_resolve_v2 = require("./resolve-v2");
32
+ var import_auto_detect = require("./auto-detect");
33
+ var import_detect_railway = require("./detect-railway");
34
+ var import_detect_render = require("./detect-render");
35
+ var import_detect_procfile = require("./detect-procfile");
36
+ const PREVIEW_DOMAIN_MISSING = [
37
+ { type: "host", value: { suf: ".vercel.app" } },
38
+ { type: "host", value: { suf: ".vercel.dev" } }
39
+ ];
40
+ function emptyRoutes() {
41
+ return {
42
+ hostRewrites: [],
43
+ rewrites: [],
44
+ defaults: [],
45
+ fallbacks: [],
46
+ crons: [],
47
+ workers: []
48
+ };
49
+ }
50
+ function withResolvedResult(resolved, inferred = null) {
51
+ return {
52
+ services: resolved.services,
53
+ source: resolved.source,
54
+ useImplicitEnvInjection: resolved.useImplicitEnvInjection,
55
+ routes: resolved.routes,
56
+ rewrites: resolved.rewrites,
57
+ errors: resolved.errors,
58
+ warnings: resolved.warnings,
59
+ resolved,
60
+ inferred
61
+ };
62
+ }
63
+ function toInferredLayoutConfig(services) {
64
+ const inferredConfig = {};
65
+ for (const [name, service] of Object.entries(services)) {
66
+ const serviceConfig = {
67
+ root: service.root
68
+ };
69
+ if (service.type) {
70
+ serviceConfig.type = service.type;
71
+ }
72
+ if (typeof service.entrypoint === "string") {
73
+ serviceConfig.entrypoint = service.entrypoint;
74
+ }
75
+ if (typeof service.mountPath === "string") {
76
+ serviceConfig.mountPath = service.mountPath;
77
+ }
78
+ if ((0, import_framework.isFrontendFramework)(service.framework)) {
79
+ serviceConfig.framework = service.framework;
80
+ }
81
+ if (typeof service.buildCommand === "string") {
82
+ serviceConfig.buildCommand = service.buildCommand;
83
+ }
84
+ if (typeof service.runtime === "string") {
85
+ serviceConfig.runtime = service.runtime;
86
+ }
87
+ inferredConfig[name] = serviceConfig;
88
+ }
89
+ return inferredConfig;
90
+ }
91
+ async function detectServices(options) {
92
+ const {
93
+ fs,
94
+ workPath,
95
+ detectEntrypoint,
96
+ configuredServices: providedConfiguredServices,
97
+ configuredServicesType: providedConfiguredServicesType
98
+ } = options;
99
+ const scopedFs = workPath ? fs.chdir(workPath) : fs;
100
+ const { config: vercelConfig, error: configError } = await (0, import_utils.readVercelConfig)(scopedFs);
101
+ if (configError) {
102
+ return withResolvedResult({
103
+ services: [],
104
+ source: "configured",
105
+ useImplicitEnvInjection: true,
106
+ routes: emptyRoutes(),
107
+ rewrites: [],
108
+ errors: [configError],
109
+ warnings: []
110
+ });
111
+ }
112
+ if (vercelConfig?.services != null && vercelConfig.experimentalServicesV2 != null) {
113
+ return withResolvedResult({
114
+ services: [],
115
+ source: "configured",
116
+ useImplicitEnvInjection: false,
117
+ routes: emptyRoutes(),
118
+ rewrites: [],
119
+ errors: [
120
+ {
121
+ code: "SERVICES_AND_EXPERIMENTAL_SERVICES_V2",
122
+ message: "The `services` property cannot be used in conjunction with its deprecated alias `experimentalServicesV2`. Please use only `services`."
123
+ }
124
+ ],
125
+ warnings: []
126
+ });
127
+ }
128
+ const hasProvidedConfiguredServices = providedConfiguredServices && Object.keys(providedConfiguredServices).length > 0;
129
+ const experimentalServicesV2 = hasProvidedConfiguredServices && (providedConfiguredServicesType === "services" || providedConfiguredServicesType === "experimentalServicesV2") ? providedConfiguredServices : hasProvidedConfiguredServices ? void 0 : vercelConfig?.services ?? vercelConfig?.experimentalServicesV2;
130
+ if (experimentalServicesV2 && Object.keys(experimentalServicesV2).length > 0) {
131
+ const result = await (0, import_resolve_v2.resolveAllConfiguredServicesV2)(
132
+ experimentalServicesV2,
133
+ scopedFs
134
+ );
135
+ return withResolvedResult({
136
+ services: result.services,
137
+ source: "configured",
138
+ // V2 uses explicit `bindings`, so no implicit `{NAME}_URL` injection.
139
+ useImplicitEnvInjection: false,
140
+ // V2 routes are explicitly carried per-service to output them separately.
141
+ routes: emptyRoutes(),
142
+ rewrites: [],
143
+ errors: result.errors,
144
+ warnings: []
145
+ });
146
+ }
147
+ const experimentalServicesV1 = hasProvidedConfiguredServices ? providedConfiguredServices : vercelConfig?.experimentalServices;
148
+ const hasExperimentalServicesV1 = experimentalServicesV1 && Object.keys(experimentalServicesV1).length > 0;
149
+ if (hasExperimentalServicesV1) {
150
+ const result = await (0, import_resolve.resolveAllConfiguredServices)(
151
+ experimentalServicesV1,
152
+ scopedFs,
153
+ "configured"
154
+ );
155
+ const routes = generateServicesRoutes(result.services);
156
+ return withResolvedResult({
157
+ services: result.services,
158
+ source: "configured",
159
+ // experimentalServices uses the legacy `{NAME}_URL` injection.
160
+ useImplicitEnvInjection: true,
161
+ routes,
162
+ rewrites: [],
163
+ errors: result.errors,
164
+ warnings: []
165
+ });
166
+ }
167
+ const detectors = [
168
+ { detect: import_detect_railway.detectRailwayServices, source: "railway" },
169
+ { detect: import_detect_render.detectRenderServices, source: "render" },
170
+ { detect: import_detect_procfile.detectProcfileServices, source: "procfile" },
171
+ { detect: import_auto_detect.autoDetectServices, source: "layout" }
172
+ ];
173
+ for (const { detect, source } of detectors) {
174
+ const detectResult = await detect({ fs: scopedFs, detectEntrypoint });
175
+ const match = await tryResolveInferred(detectResult, source, scopedFs);
176
+ if (match)
177
+ return match;
178
+ }
179
+ return withResolvedResult({
180
+ services: [],
181
+ source: "auto-detected",
182
+ useImplicitEnvInjection: true,
183
+ routes: emptyRoutes(),
184
+ rewrites: [],
185
+ errors: [
186
+ {
187
+ code: "NO_SERVICES_CONFIGURED",
188
+ message: "No services configured. Add `services` to vercel.json."
189
+ }
190
+ ],
191
+ warnings: []
192
+ });
193
+ }
194
+ async function tryResolveInferred(detectResult, source, scopedFs) {
195
+ if (detectResult.errors.length > 0) {
196
+ return withResolvedResult({
197
+ services: [],
198
+ source: "auto-detected",
199
+ useImplicitEnvInjection: source !== "layout",
200
+ routes: emptyRoutes(),
201
+ rewrites: [],
202
+ errors: detectResult.errors,
203
+ warnings: detectResult.warnings
204
+ });
205
+ }
206
+ if (!detectResult.services) {
207
+ return null;
208
+ }
209
+ if (source === "layout") {
210
+ const v2Services = {};
211
+ for (const [name, svc] of Object.entries(detectResult.services)) {
212
+ v2Services[name] = {
213
+ root: svc.root,
214
+ ...svc.framework ? { framework: svc.framework } : {},
215
+ ...svc.entrypoint ? { entrypoint: svc.entrypoint } : {}
216
+ };
217
+ }
218
+ const result2 = await (0, import_resolve_v2.resolveAllConfiguredServicesV2)(v2Services, scopedFs);
219
+ const rootServices = Object.values(detectResult.services).filter(
220
+ (svc) => svc.mountPath === "/" && typeof svc.framework === "string"
221
+ );
222
+ const shouldInfer2 = result2.errors.length === 0 && rootServices.length === 1 && result2.services.length > 1;
223
+ const inferred2 = shouldInfer2 ? {
224
+ source,
225
+ config: toInferredLayoutConfig(detectResult.services),
226
+ services: result2.services,
227
+ warnings: detectResult.warnings
228
+ } : null;
229
+ return withResolvedResult(
230
+ {
231
+ services: shouldInfer2 ? result2.services : [],
232
+ source: "auto-detected",
233
+ useImplicitEnvInjection: false,
234
+ routes: emptyRoutes(),
235
+ rewrites: shouldInfer2 ? generateServiceRewrites(detectResult.services) : [],
236
+ experimentalServicesV2: shouldInfer2 ? v2Services : void 0,
237
+ errors: result2.errors,
238
+ warnings: detectResult.warnings
239
+ },
240
+ inferred2
241
+ );
242
+ }
243
+ const v1Services = {};
244
+ for (const [name, svc] of Object.entries(detectResult.services)) {
245
+ v1Services[name] = {
246
+ root: svc.root === "." ? void 0 : svc.root,
247
+ ...svc.framework ? { framework: svc.framework } : {},
248
+ ...svc.entrypoint ? { entrypoint: svc.entrypoint } : {},
249
+ ...svc.type ? { type: svc.type } : {},
250
+ ...svc.buildCommand ? { buildCommand: svc.buildCommand } : {},
251
+ ...svc.preDeployCommand ? { preDeployCommand: svc.preDeployCommand } : {},
252
+ ...svc.mountPath ? { routePrefix: svc.mountPath } : {}
253
+ };
254
+ }
255
+ const result = await (0, import_resolve.resolveAllConfiguredServices)(
256
+ v1Services,
257
+ scopedFs,
258
+ "generated"
259
+ );
260
+ const shouldInfer = result.errors.length === 0 && result.services.length > 0;
261
+ const inferred = shouldInfer ? {
262
+ source,
263
+ config: toInferredLayoutConfig(detectResult.services),
264
+ services: result.services,
265
+ warnings: detectResult.warnings
266
+ } : null;
267
+ return withResolvedResult(
268
+ {
269
+ services: [],
270
+ source: "auto-detected",
271
+ useImplicitEnvInjection: true,
272
+ routes: emptyRoutes(),
273
+ rewrites: [],
274
+ errors: result.errors,
275
+ warnings: detectResult.warnings
276
+ },
277
+ inferred
278
+ );
279
+ }
280
+ function generateServiceRewrites(services) {
281
+ const entries = Object.entries(services).filter(
282
+ ([, svc]) => typeof svc.mountPath === "string" && (!svc.type || svc.type === "web")
283
+ ).sort(([, a], [, b]) => b.mountPath.length - a.mountPath.length);
284
+ return entries.map(([name, svc]) => {
285
+ const mountPath = svc.mountPath;
286
+ if (mountPath === "/") {
287
+ return {
288
+ source: "/(.*)",
289
+ destination: { type: "service", service: name }
290
+ };
291
+ }
292
+ const prefix = mountPath.startsWith("/") ? mountPath.slice(1) : mountPath;
293
+ return {
294
+ source: `/${prefix}(/.*)?`,
295
+ destination: { type: "service", service: name }
296
+ };
297
+ });
298
+ }
299
+ function generateServicesRoutes(allServices) {
300
+ const services = allServices.filter(import_build_utils.isExperimentalService);
301
+ const hostRewrites = [];
302
+ const rewrites = [];
303
+ const defaults = [];
304
+ const fallbacks = [];
305
+ const crons = [];
306
+ const workers = [];
307
+ const sortedWebServices = services.filter(
308
+ (s) => s.type === "web" && typeof s.routePrefix === "string"
309
+ ).sort((a, b) => b.routePrefix.length - a.routePrefix.length);
310
+ const allWebPrefixes = getWebRoutePrefixes(sortedWebServices);
311
+ const explicitHostPrefixGuard = getExplicitHostPrefixNegativeLookahead(allWebPrefixes);
312
+ for (const service of sortedWebServices) {
313
+ const { routePrefix } = service;
314
+ const normalizedPrefix = routePrefix.slice(1);
315
+ const ownershipGuard = (0, import_routing_utils.getOwnershipGuard)(routePrefix, allWebPrefixes);
316
+ const hostCondition = getHostCondition(service);
317
+ if (hostCondition && routePrefix !== "/") {
318
+ const normalizedRoutePrefix = (0, import_routing_utils.normalizeRoutePrefix)(routePrefix);
319
+ hostRewrites.push({
320
+ src: "^/$",
321
+ dest: normalizedRoutePrefix,
322
+ has: hostCondition,
323
+ missing: PREVIEW_DOMAIN_MISSING,
324
+ check: true
325
+ });
326
+ hostRewrites.push({
327
+ // Preserve explicit service prefixes so canonical paths like /_/api
328
+ // keep routing to their target service even on another service's host.
329
+ src: `^/${explicitHostPrefixGuard}(.*)$`,
330
+ dest: `${normalizedRoutePrefix}/$1`,
331
+ has: hostCondition,
332
+ missing: PREVIEW_DOMAIN_MISSING,
333
+ check: true
334
+ });
335
+ }
336
+ if ((0, import_utils.isRouteOwningBuilder)(service)) {
337
+ continue;
338
+ }
339
+ if ((0, import_utils.isStaticBuild)(service)) {
340
+ if (routePrefix === "/") {
341
+ fallbacks.push({
342
+ src: (0, import_routing_utils.scopeRouteSourceToOwnership)("/(.*)", ownershipGuard),
343
+ dest: "/index.html"
344
+ });
345
+ } else {
346
+ fallbacks.push({
347
+ src: (0, import_routing_utils.scopeRouteSourceToOwnership)(
348
+ `^/${normalizedPrefix}(?:/.*)?$`,
349
+ ownershipGuard
350
+ ),
351
+ dest: `/${normalizedPrefix}/index.html`
352
+ });
353
+ }
354
+ } else if (service.runtime) {
355
+ const functionPath = (0, import_utils.getInternalServiceFunctionPath)(service.name);
356
+ const check = service.runtime === "container" ? void 0 : true;
357
+ if (routePrefix === "/") {
358
+ defaults.push({
359
+ src: (0, import_routing_utils.scopeRouteSourceToOwnership)("^/(.*)$", ownershipGuard),
360
+ dest: functionPath,
361
+ ...check ? { check } : {}
362
+ });
363
+ } else {
364
+ rewrites.push({
365
+ src: (0, import_routing_utils.scopeRouteSourceToOwnership)(
366
+ `^/${normalizedPrefix}(?:/.*)?$`,
367
+ ownershipGuard
368
+ ),
369
+ dest: functionPath,
370
+ ...check ? { check } : {}
371
+ });
372
+ }
373
+ }
374
+ }
375
+ const cronServices = services.filter(import_build_utils.isScheduleTriggeredService);
376
+ for (const service of cronServices) {
377
+ const cronPrefix = (0, import_utils.getInternalServiceCronPathPrefix)(service.name);
378
+ const functionPath = (0, import_utils.getInternalServiceFunctionPath)(service.name);
379
+ crons.push({
380
+ src: `^${escapeRegex(cronPrefix)}/.*$`,
381
+ dest: functionPath,
382
+ check: true
383
+ });
384
+ }
385
+ return { hostRewrites, rewrites, defaults, fallbacks, crons, workers };
386
+ }
387
+ function escapeRegex(str) {
388
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
389
+ }
390
+ function getWebRoutePrefixes(services) {
391
+ const unique = /* @__PURE__ */ new Set();
392
+ for (const service of services) {
393
+ if (service.type !== "web" || typeof service.routePrefix !== "string") {
394
+ continue;
395
+ }
396
+ unique.add((0, import_routing_utils.normalizeRoutePrefix)(service.routePrefix));
397
+ }
398
+ return Array.from(unique);
399
+ }
400
+ function getExplicitHostPrefixNegativeLookahead(routePrefixes) {
401
+ const explicitPrefixes = routePrefixes.map(import_routing_utils.normalizeRoutePrefix).filter((prefix) => prefix !== "/").sort((a, b) => b.length - a.length).map((prefix) => escapeRegex(prefix.slice(1)));
402
+ if (explicitPrefixes.length === 0) {
403
+ return "";
404
+ }
405
+ if (explicitPrefixes.length === 1) {
406
+ return `(?!${explicitPrefixes[0]}(?:/|$))`;
407
+ }
408
+ return `(?!(?:${explicitPrefixes.join("|")})(?:/|$))`;
409
+ }
410
+ function getHostCondition(service) {
411
+ if (service.type !== "web") {
412
+ return void 0;
413
+ }
414
+ if (typeof service.subdomain === "string" && service.subdomain.length > 0) {
415
+ return [{ type: "host", value: { pre: `${service.subdomain}.` } }];
416
+ }
417
+ return void 0;
418
+ }
419
+ // Annotate the CommonJS export names for ESM import in node:
420
+ 0 && (module.exports = {
421
+ detectServices,
422
+ generateServiceRewrites,
423
+ generateServicesRoutes
424
+ });
@@ -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
+ }>;