@vercel/fs-detectors 5.18.1 → 5.19.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.
@@ -80,7 +80,8 @@ async function detectRailwayServices(options) {
80
80
  const schedule = cf.config.deploy.cronSchedule;
81
81
  const runtime = frameworks.length === 1 ? (0, import_utils.inferRuntimeFromFramework)(frameworks[0].slug) : void 0;
82
82
  const hint = {
83
- type: "cron",
83
+ type: "job",
84
+ trigger: "schedule",
84
85
  schedule,
85
86
  entrypoint: "<path-to-handler>"
86
87
  };
@@ -89,7 +90,7 @@ async function detectRailwayServices(options) {
89
90
  }
90
91
  warnings.push({
91
92
  code: "RAILWAY_CRON_HINT",
92
- message: `Found Railway cron in ${dirLabel}/ (schedule: "${schedule}"). Vercel crons work with a file entrypoint. You can add the following to define this cron service:
93
+ message: `Found Railway cron in ${dirLabel}/ (schedule: "${schedule}"). Vercel crons work with a file entrypoint. You can add the following to define this scheduled job service:
93
94
  "${deriveServiceName(cf.dirPath)}": ${JSON.stringify(hint, null, 2)}`
94
95
  });
95
96
  continue;
@@ -26,11 +26,10 @@ export declare function detectServices(options: DetectServicesOptions): Promise<
26
26
  * Builders that provide their own routing (`@vercel/next`, `@vercel/backends`,
27
27
  * Build Output API builders, etc.) are not given synthetic routes here.
28
28
  *
29
- * - Worker services:
30
- * Internal queue callback routes under `/_svc/{serviceName}/workers/{entry}/{handler}`
31
- * that rewrite to `/_svc/{serviceName}/index`.
29
+ * - Worker and queue-triggered job services:
30
+ * Use private path routing. The generated function is not publicly accessible.
32
31
  *
33
- * - Cron services:
32
+ * - Schedule-triggered job services:
34
33
  * Internal cron callback routes under `/_svc/{serviceName}/crons/{entry}/{handler}`
35
34
  * that rewrite to `/_svc/{serviceName}/index`.
36
35
  */
@@ -22,6 +22,7 @@ __export(detect_services_exports, {
22
22
  generateServicesRoutes: () => generateServicesRoutes
23
23
  });
24
24
  module.exports = __toCommonJS(detect_services_exports);
25
+ var import_build_utils = require("@vercel/build-utils");
25
26
  var import_routing_utils = require("@vercel/routing-utils");
26
27
  var import_utils = require("./utils");
27
28
  var import_resolve = require("./resolve");
@@ -255,21 +256,7 @@ function generateServicesRoutes(services) {
255
256
  }
256
257
  }
257
258
  }
258
- const workerServices = services.filter((s) => s.type === "worker");
259
- for (const service of workerServices) {
260
- const workerEntrypoint = service.entrypoint || service.builder.src || "index";
261
- const workerPath = (0, import_utils.getInternalServiceWorkerPath)(
262
- service.name,
263
- workerEntrypoint
264
- );
265
- const functionPath = (0, import_utils.getInternalServiceFunctionPath)(service.name);
266
- workers.push({
267
- src: `^${escapeRegex(workerPath)}$`,
268
- dest: functionPath,
269
- check: true
270
- });
271
- }
272
- const cronServices = services.filter((s) => s.type === "cron");
259
+ const cronServices = services.filter(import_build_utils.isScheduleTriggeredService);
273
260
  for (const service of cronServices) {
274
261
  const cronPrefix = (0, import_utils.getInternalServiceCronPathPrefix)(service.name);
275
262
  const functionPath = (0, import_utils.getInternalServiceFunctionPath)(service.name);
@@ -281,6 +281,12 @@ function validateServiceConfig(name, config) {
281
281
  };
282
282
  }
283
283
  const serviceType = config.type || "web";
284
+ const isJobService = serviceType === "job" || serviceType === "cron";
285
+ const isScheduleJobService = serviceType === "cron" || serviceType === "job" && config.trigger === "schedule";
286
+ const isQueueJobService = serviceType === "job" && config.trigger === "queue";
287
+ const isWorkflowService = serviceType === "job" && config.trigger === "workflow";
288
+ const isNonWebService = serviceType === "worker" || isJobService;
289
+ const serviceTypeLabel = isJobService ? "Job" : serviceType === "worker" ? "Worker" : "Web";
284
290
  const routingResult = resolveServiceRoutingConfig(name, config);
285
291
  if (routingResult.error) {
286
292
  return routingResult.error;
@@ -310,24 +316,52 @@ function validateServiceConfig(name, config) {
310
316
  serviceName: name
311
317
  };
312
318
  }
313
- if ((serviceType === "worker" || serviceType === "cron") && configuredRoutePrefix) {
319
+ if (isNonWebService && configuredRoutePrefix) {
314
320
  return {
315
321
  code: "INVALID_ROUTE_PREFIX",
316
- message: `${serviceType === "worker" ? "Worker" : "Cron"} service "${name}" cannot have "routePrefix" or "mount". Only web services should specify path-based routing.`,
322
+ message: `${serviceTypeLabel} service "${name}" cannot have "routePrefix" or "mount". Only web services should specify path-based routing.`,
317
323
  serviceName: name
318
324
  };
319
325
  }
320
- if ((serviceType === "worker" || serviceType === "cron") && hasSubdomain) {
326
+ if (isNonWebService && hasSubdomain) {
321
327
  return {
322
328
  code: "INVALID_HOST_ROUTING_CONFIG",
323
- message: `${serviceType === "worker" ? "Worker" : "Cron"} service "${name}" cannot have "subdomain" or "mount.subdomain". Only web services should specify subdomain routing.`,
329
+ message: `${serviceTypeLabel} service "${name}" cannot have "subdomain" or "mount.subdomain". Only web services should specify subdomain routing.`,
324
330
  serviceName: name
325
331
  };
326
332
  }
327
- if (serviceType === "cron" && !config.schedule) {
333
+ if (serviceType === "job" && config.trigger === void 0) {
328
334
  return {
329
- code: "MISSING_CRON_SCHEDULE",
330
- message: `Cron service "${name}" is missing required "schedule" field.`,
335
+ code: "MISSING_JOB_TRIGGER",
336
+ message: `Job service "${name}" is missing required "trigger" field.`,
337
+ serviceName: name
338
+ };
339
+ }
340
+ if (serviceType === "job" && config.trigger && !import_build_utils.JOB_TRIGGERS.includes(config.trigger)) {
341
+ return {
342
+ code: "INVALID_JOB_TRIGGER",
343
+ message: `Job service "${name}" has invalid trigger "${config.trigger}". Expected ${import_build_utils.JOB_TRIGGERS.map((t) => `"${t}"`).join(", ")}.`,
344
+ serviceName: name
345
+ };
346
+ }
347
+ if (isScheduleJobService && !config.schedule) {
348
+ return {
349
+ code: serviceType === "cron" ? "MISSING_CRON_SCHEDULE" : "MISSING_JOB_SCHEDULE",
350
+ message: `${serviceTypeLabel} service "${name}" is missing required "schedule" field.`,
351
+ serviceName: name
352
+ };
353
+ }
354
+ if (isQueueJobService && (!Array.isArray(config.topics) || config.topics.length === 0)) {
355
+ return {
356
+ code: "MISSING_QUEUE_TOPICS",
357
+ message: `${serviceTypeLabel} service "${name}" is missing required "topics" field.`,
358
+ serviceName: name
359
+ };
360
+ }
361
+ if (isWorkflowService && typeof config.entrypoint !== "string") {
362
+ return {
363
+ code: "MISSING_ENTRYPOINT",
364
+ message: `Job service "${name}" with "workflow" trigger must specify "entrypoint".`,
331
365
  serviceName: name
332
366
  };
333
367
  }
@@ -428,6 +462,7 @@ async function resolveConfiguredService(options) {
428
462
  routePrefixSource = "configured"
429
463
  } = options;
430
464
  const type = config.type || "web";
465
+ const trigger = type === "cron" ? "schedule" : type === "job" ? config.trigger : void 0;
431
466
  const rawEntrypoint = config.entrypoint;
432
467
  const moduleAttrParsed = typeof rawEntrypoint === "string" ? parsePyModuleAttrEntrypoint(rawEntrypoint) : null;
433
468
  const routingResult = resolveServiceRoutingConfig(name, config);
@@ -484,8 +519,7 @@ async function resolveConfiguredService(options) {
484
519
  workspace = workspace === "." ? normalizedRoot : import_path.posix.join(normalizedRoot, workspace);
485
520
  }
486
521
  }
487
- const topics = type === "worker" ? (0, import_build_utils.getWorkerTopics)(config) : config.topics;
488
- const consumer = type === "worker" ? config.consumer || "default" : config.consumer;
522
+ const topics = type === "worker" ? (0, import_build_utils.getServiceQueueTopics)({ type, topics: config.topics }) : trigger === "queue" ? config.topics : void 0;
489
523
  let builderUse;
490
524
  let builderSrc;
491
525
  const frameworkDefinition = config.framework ? frameworksBySlug.get(config.framework) : void 0;
@@ -550,6 +584,7 @@ async function resolveConfiguredService(options) {
550
584
  return {
551
585
  name,
552
586
  type,
587
+ trigger,
553
588
  group,
554
589
  workspace,
555
590
  entrypoint: resolvedEntrypointFile,
@@ -568,7 +603,6 @@ async function resolveConfiguredService(options) {
568
603
  schedule: config.schedule,
569
604
  handlerFunction: moduleAttrParsed?.attrName,
570
605
  topics,
571
- consumer,
572
606
  envPrefix: config.envPrefix
573
607
  };
574
608
  }
@@ -22,8 +22,8 @@ export interface ServicesRoutes {
22
22
  /** Default routes (catch-all for root web service) */
23
23
  defaults: Route[];
24
24
  /**
25
- * Internal routes for cron services.
26
- * These route `/_svc/{serviceName}/crons/{entry}/{handler}` to the cron function.
25
+ * Internal routes for schedule-triggered job services.
26
+ * These route `/_svc/{serviceName}/crons/{entry}/{handler}` to the scheduled job function.
27
27
  */
28
28
  crons: Route[];
29
29
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/fs-detectors",
3
- "version": "5.18.1",
3
+ "version": "5.19.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/build-utils": "13.17.1",
24
23
  "@vercel/error-utils": "2.0.3",
25
- "@vercel/routing-utils": "6.1.1",
26
- "@vercel/frameworks": "3.24.1"
24
+ "@vercel/build-utils": "13.19.0",
25
+ "@vercel/frameworks": "3.24.1",
26
+ "@vercel/routing-utils": "6.1.1"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/glob": "7.2.0",