@vercel/fs-detectors 5.16.0 → 5.18.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.
@@ -311,7 +311,7 @@ async function maybeGetApiBuilder(fileName, apiMatches, options) {
311
311
  }
312
312
  }
313
313
  const nodeExtensions = [".js", ".mjs", ".ts", ".tsx"];
314
- if (process.env.VERCEL_NODE_FILTER_ENTRYPOINTS === "1" && nodeExtensions.some((ext) => fileName.endsWith(ext)) && options.workPath) {
314
+ if (fileName.startsWith("api/") && process.env.VERCEL_NODE_FILTER_ENTRYPOINTS === "1" && nodeExtensions.some((ext) => fileName.endsWith(ext)) && options.workPath) {
315
315
  const fsPath = (0, import_path.join)(options.workPath, fileName);
316
316
  const isEntrypoint = await (0, import_build_utils.isNodeEntrypoint)({ fsPath });
317
317
  if (!isEntrypoint) {
@@ -271,15 +271,10 @@ function generateServicesRoutes(services) {
271
271
  }
272
272
  const cronServices = services.filter((s) => s.type === "cron");
273
273
  for (const service of cronServices) {
274
- const cronEntrypoint = service.entrypoint || service.builder.src || "index";
275
- const cronPath = (0, import_utils.getInternalServiceCronPath)(
276
- service.name,
277
- cronEntrypoint,
278
- service.handlerFunction || "cron"
279
- );
274
+ const cronPrefix = (0, import_utils.getInternalServiceCronPathPrefix)(service.name);
280
275
  const functionPath = (0, import_utils.getInternalServiceFunctionPath)(service.name);
281
276
  crons.push({
282
- src: `^${escapeRegex(cronPath)}$`,
277
+ src: `^${escapeRegex(cronPrefix)}/.*$`,
283
278
  dest: functionPath,
284
279
  check: true
285
280
  });
@@ -8,7 +8,9 @@ type RoutePrefixSource = 'configured' | 'generated';
8
8
  interface ResolveConfiguredServiceOptions {
9
9
  name: string;
10
10
  config: ExperimentalServiceConfig;
11
- fs: DetectorFilesystem;
11
+ /** Filesystem scoped to the service root (via chdir) when root is set, otherwise the project-level fs. */
12
+ serviceFs: DetectorFilesystem;
13
+ root?: string;
12
14
  group?: string;
13
15
  resolvedEntrypoint?: ResolvedEntrypointPath;
14
16
  routePrefixSource?: RoutePrefixSource;
@@ -46,6 +46,33 @@ function parsePyModuleAttrEntrypoint(entrypoint) {
46
46
  const SERVICE_NAME_REGEX = /^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;
47
47
  const DNS_LABEL_RE = /^(?!-)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
48
48
  const ENV_PREFIX_RE = /^[A-Z][A-Z0-9_]*_$/;
49
+ async function getServiceFs(fs, serviceName, root) {
50
+ if (!root) {
51
+ return { fs };
52
+ }
53
+ const normalizedRoot = import_path.posix.normalize(root);
54
+ if (!await fs.hasPath(normalizedRoot)) {
55
+ return {
56
+ fs,
57
+ error: {
58
+ code: "ROOT_NOT_FOUND",
59
+ message: `Service "${serviceName}" has root "${root}" but that directory does not exist.`,
60
+ serviceName
61
+ }
62
+ };
63
+ }
64
+ if (await fs.isFile(normalizedRoot)) {
65
+ return {
66
+ fs,
67
+ error: {
68
+ code: "ROOT_NOT_DIRECTORY",
69
+ message: `Service "${serviceName}" has root "${root}" but that path is a file, not a directory.`,
70
+ serviceName
71
+ }
72
+ };
73
+ }
74
+ return { fs: fs.chdir(normalizedRoot) };
75
+ }
49
76
  function normalizeServiceEntrypoint(entrypoint) {
50
77
  const normalized = import_path.posix.normalize(entrypoint);
51
78
  return normalized === "" ? "." : normalized;
@@ -304,6 +331,23 @@ function validateServiceConfig(name, config) {
304
331
  serviceName: name
305
332
  };
306
333
  }
334
+ if (config.root !== void 0) {
335
+ const normalizedRoot = import_path.posix.normalize(config.root);
336
+ if (normalizedRoot.startsWith("/")) {
337
+ return {
338
+ code: "INVALID_ROOT",
339
+ message: `Service "${name}" has invalid "root" "${config.root}". Must be a relative path.`,
340
+ serviceName: name
341
+ };
342
+ }
343
+ if (normalizedRoot === ".." || normalizedRoot.startsWith("../")) {
344
+ return {
345
+ code: "INVALID_ROOT",
346
+ message: `Service "${name}" has invalid "root" "${config.root}". Must not escape the project root.`,
347
+ serviceName: name
348
+ };
349
+ }
350
+ }
307
351
  if (config.envPrefix !== void 0) {
308
352
  if (!ENV_PREFIX_RE.test(config.envPrefix)) {
309
353
  return {
@@ -377,7 +421,8 @@ async function resolveConfiguredService(options) {
377
421
  const {
378
422
  name,
379
423
  config,
380
- fs,
424
+ serviceFs,
425
+ root,
381
426
  group,
382
427
  resolvedEntrypoint,
383
428
  routePrefixSource = "configured"
@@ -396,7 +441,7 @@ async function resolveConfiguredService(options) {
396
441
  if (!resolvedEntrypointPath && typeof rawEntrypoint === "string") {
397
442
  const entrypointToResolve = moduleAttrParsed ? moduleAttrParsed.filePath : rawEntrypoint;
398
443
  const resolved = await resolveEntrypointPath({
399
- fs,
444
+ fs: serviceFs,
400
445
  serviceName: name,
401
446
  entrypoint: entrypointToResolve
402
447
  });
@@ -419,7 +464,7 @@ async function resolveConfiguredService(options) {
419
464
  workspace = normalizedEntrypoint;
420
465
  } else {
421
466
  const inferredWorkspace = await inferWorkspaceFromNearestManifest({
422
- fs,
467
+ fs: serviceFs,
423
468
  entrypoint: resolvedEntrypointFile,
424
469
  runtime: inferredRuntime
425
470
  });
@@ -433,6 +478,12 @@ async function resolveConfiguredService(options) {
433
478
  }
434
479
  }
435
480
  }
481
+ if (root) {
482
+ const normalizedRoot = import_path.posix.normalize(root);
483
+ if (normalizedRoot !== ".") {
484
+ workspace = workspace === "." ? normalizedRoot : import_path.posix.join(normalizedRoot, workspace);
485
+ }
486
+ }
436
487
  const topics = type === "worker" ? (0, import_build_utils.getWorkerTopics)(config) : config.topics;
437
488
  const consumer = type === "worker" ? config.consumer || "default" : config.consumer;
438
489
  let builderUse;
@@ -532,12 +583,19 @@ async function resolveAllConfiguredServices(services, fs, routePrefixSource = "c
532
583
  errors.push(validationError);
533
584
  continue;
534
585
  }
586
+ const root = serviceConfig.root;
587
+ const serviceFsResult = await getServiceFs(fs, name, root);
588
+ if (serviceFsResult.error) {
589
+ errors.push(serviceFsResult.error);
590
+ continue;
591
+ }
592
+ const serviceFs = serviceFsResult.fs;
535
593
  let resolvedEntrypoint;
536
594
  if (typeof serviceConfig.entrypoint === "string") {
537
595
  const moduleAttr = parsePyModuleAttrEntrypoint(serviceConfig.entrypoint);
538
596
  const entrypointToResolve = moduleAttr?.filePath ?? serviceConfig.entrypoint;
539
597
  const resolvedPath = await resolveEntrypointPath({
540
- fs,
598
+ fs: serviceFs,
541
599
  serviceName: name,
542
600
  entrypoint: entrypointToResolve
543
601
  });
@@ -566,7 +624,7 @@ async function resolveAllConfiguredServices(services, fs, routePrefixSource = "c
566
624
  });
567
625
  const workspace = resolvedEntrypoint.normalized;
568
626
  const { framework, error } = await detectFrameworkFromWorkspace({
569
- fs,
627
+ fs: serviceFs,
570
628
  workspace,
571
629
  runtime: inferredRuntime,
572
630
  serviceName: name
@@ -594,13 +652,13 @@ async function resolveAllConfiguredServices(services, fs, routePrefixSource = "c
594
652
  });
595
653
  if (inferredRuntime) {
596
654
  const inferredWorkspace = await inferWorkspaceFromNearestManifest({
597
- fs,
655
+ fs: serviceFs,
598
656
  entrypoint: resolvedEntrypoint.normalized,
599
657
  runtime: inferredRuntime
600
658
  });
601
659
  const workspace = inferredWorkspace ?? import_path.posix.dirname(resolvedEntrypoint.normalized);
602
660
  const detection = await detectFrameworkFromWorkspace({
603
- fs,
661
+ fs: serviceFs,
604
662
  workspace,
605
663
  serviceName: name,
606
664
  runtime: inferredRuntime
@@ -617,7 +675,8 @@ async function resolveAllConfiguredServices(services, fs, routePrefixSource = "c
617
675
  const service = await resolveConfiguredService({
618
676
  name,
619
677
  config: resolvedConfig,
620
- fs,
678
+ serviceFs,
679
+ root,
621
680
  resolvedEntrypoint,
622
681
  routePrefixSource
623
682
  });
@@ -1,19 +1,14 @@
1
+ import { INTERNAL_SERVICE_PREFIX, getInternalServiceFunctionPath, getInternalServiceCronPathPrefix, getInternalServiceCronPath } from '@vercel/build-utils';
1
2
  import type { DetectorFilesystem } from '../detectors/filesystem';
2
3
  import type { ServiceRuntime, ExperimentalServices, ServiceDetectionError, ResolvedService } from './types';
4
+ export { INTERNAL_SERVICE_PREFIX, getInternalServiceFunctionPath, getInternalServiceCronPathPrefix, getInternalServiceCronPath, };
3
5
  export declare function hasFile(fs: DetectorFilesystem, filePath: string): Promise<boolean>;
4
- /**
5
- * Reserved internal namespace used by services routing/runtime plumbing.
6
- */
7
- export declare const INTERNAL_SERVICE_PREFIX = "/_svc";
8
6
  /**
9
7
  * Reserved internal namespace used by the dev queue proxy.
10
8
  */
11
9
  export declare const INTERNAL_QUEUES_PREFIX = "/_svc/_queues";
12
- export declare function getInternalServiceFunctionPath(serviceName: string): string;
13
10
  export declare function getInternalServiceWorkerPathPrefix(serviceName: string): string;
14
- export declare function getInternalServiceCronPathPrefix(serviceName: string): string;
15
11
  export declare function getInternalServiceWorkerPath(serviceName: string, entrypoint: string, handler?: string): string;
16
- export declare function getInternalServiceCronPath(serviceName: string, entrypoint: string, handler?: string): string;
17
12
  export declare function getBuilderForRuntime(runtime: ServiceRuntime): string;
18
13
  export declare function isStaticBuild(service: ResolvedService): boolean;
19
14
  /**
@@ -29,12 +29,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  var utils_exports = {};
30
30
  __export(utils_exports, {
31
31
  INTERNAL_QUEUES_PREFIX: () => INTERNAL_QUEUES_PREFIX,
32
- INTERNAL_SERVICE_PREFIX: () => INTERNAL_SERVICE_PREFIX,
32
+ INTERNAL_SERVICE_PREFIX: () => import_build_utils.INTERNAL_SERVICE_PREFIX,
33
33
  filterFrameworksByRuntime: () => filterFrameworksByRuntime,
34
34
  getBuilderForRuntime: () => getBuilderForRuntime,
35
- getInternalServiceCronPath: () => getInternalServiceCronPath,
36
- getInternalServiceCronPathPrefix: () => getInternalServiceCronPathPrefix,
37
- getInternalServiceFunctionPath: () => getInternalServiceFunctionPath,
35
+ getInternalServiceCronPath: () => import_build_utils.getInternalServiceCronPath,
36
+ getInternalServiceCronPathPrefix: () => import_build_utils.getInternalServiceCronPathPrefix,
37
+ getInternalServiceFunctionPath: () => import_build_utils.getInternalServiceFunctionPath,
38
38
  getInternalServiceWorkerPath: () => getInternalServiceWorkerPath,
39
39
  getInternalServiceWorkerPathPrefix: () => getInternalServiceWorkerPathPrefix,
40
40
  hasFile: () => hasFile,
@@ -47,6 +47,7 @@ __export(utils_exports, {
47
47
  });
48
48
  module.exports = __toCommonJS(utils_exports);
49
49
  var import_framework_helpers = require("@vercel/build-utils/dist/framework-helpers");
50
+ var import_build_utils = require("@vercel/build-utils");
50
51
  var import_types = require("./types");
51
52
  async function hasFile(fs, filePath) {
52
53
  try {
@@ -55,29 +56,18 @@ async function hasFile(fs, filePath) {
55
56
  return false;
56
57
  }
57
58
  }
58
- const INTERNAL_SERVICE_PREFIX = "/_svc";
59
59
  const INTERNAL_QUEUES_PREFIX = "/_svc/_queues";
60
- function getInternalServiceFunctionPath(serviceName) {
61
- return `${INTERNAL_SERVICE_PREFIX}/${serviceName}/index`;
62
- }
63
60
  function normalizeInternalServiceEntrypoint(entrypoint) {
64
61
  const normalized = entrypoint.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\.[^/.]+$/, "");
65
62
  return normalized || "index";
66
63
  }
67
64
  function getInternalServiceWorkerPathPrefix(serviceName) {
68
- return `${INTERNAL_SERVICE_PREFIX}/${serviceName}/workers`;
69
- }
70
- function getInternalServiceCronPathPrefix(serviceName) {
71
- return `${INTERNAL_SERVICE_PREFIX}/${serviceName}/crons`;
65
+ return `${import_build_utils.INTERNAL_SERVICE_PREFIX}/${serviceName}/workers`;
72
66
  }
73
67
  function getInternalServiceWorkerPath(serviceName, entrypoint, handler = "worker") {
74
68
  const normalizedEntrypoint = normalizeInternalServiceEntrypoint(entrypoint);
75
69
  return `${getInternalServiceWorkerPathPrefix(serviceName)}/${normalizedEntrypoint}/${handler}`;
76
70
  }
77
- function getInternalServiceCronPath(serviceName, entrypoint, handler = "cron") {
78
- const normalizedEntrypoint = normalizeInternalServiceEntrypoint(entrypoint);
79
- return `${getInternalServiceCronPathPrefix(serviceName)}/${normalizedEntrypoint}/${handler}`;
80
- }
81
71
  function getBuilderForRuntime(runtime) {
82
72
  const builder = import_types.RUNTIME_BUILDERS[runtime];
83
73
  if (!builder) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/fs-detectors",
3
- "version": "5.16.0",
3
+ "version": "5.18.0",
4
4
  "description": "Vercel filesystem detectors",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -20,9 +20,9 @@
20
20
  "minimatch": "3.1.2",
21
21
  "semver": "6.3.1",
22
22
  "smol-toml": "1.5.2",
23
- "@vercel/build-utils": "13.15.0",
24
23
  "@vercel/error-utils": "2.0.3",
25
- "@vercel/frameworks": "3.24.0",
24
+ "@vercel/frameworks": "3.24.1",
25
+ "@vercel/build-utils": "13.17.0",
26
26
  "@vercel/routing-utils": "6.1.1"
27
27
  },
28
28
  "devDependencies": {