@vercel/fs-detectors 5.18.1 → 5.18.3

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,11 @@ 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:
29
+ * - Worker and queue-triggered job services:
30
30
  * Internal queue callback routes under `/_svc/{serviceName}/workers/{entry}/{handler}`
31
31
  * that rewrite to `/_svc/{serviceName}/index`.
32
32
  *
33
- * - Cron services:
33
+ * - Schedule-triggered job services:
34
34
  * Internal cron callback routes under `/_svc/{serviceName}/crons/{entry}/{handler}`
35
35
  * that rewrite to `/_svc/{serviceName}/index`.
36
36
  */
@@ -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,7 +256,7 @@ function generateServicesRoutes(services) {
255
256
  }
256
257
  }
257
258
  }
258
- const workerServices = services.filter((s) => s.type === "worker");
259
+ const workerServices = services.filter(import_build_utils.isQueueTriggeredService);
259
260
  for (const service of workerServices) {
260
261
  const workerEntrypoint = service.entrypoint || service.builder.src || "index";
261
262
  const workerPath = (0, import_utils.getInternalServiceWorkerPath)(
@@ -269,7 +270,7 @@ function generateServicesRoutes(services) {
269
270
  check: true
270
271
  });
271
272
  }
272
- const cronServices = services.filter((s) => s.type === "cron");
273
+ const cronServices = services.filter(import_build_utils.isScheduleTriggeredService);
273
274
  for (const service of cronServices) {
274
275
  const cronPrefix = (0, import_utils.getInternalServiceCronPathPrefix)(service.name);
275
276
  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,7 +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;
522
+ const topics = type === "worker" ? (0, import_build_utils.getServiceQueueTopics)({ type, topics: config.topics }) : trigger === "queue" ? config.topics : void 0;
488
523
  const consumer = type === "worker" ? config.consumer || "default" : config.consumer;
489
524
  let builderUse;
490
525
  let builderSrc;
@@ -550,6 +585,7 @@ async function resolveConfiguredService(options) {
550
585
  return {
551
586
  name,
552
587
  type,
588
+ trigger,
553
589
  group,
554
590
  workspace,
555
591
  entrypoint: resolvedEntrypointFile,
@@ -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.18.3",
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
- "@vercel/error-utils": "2.0.3",
23
+ "@vercel/frameworks": "3.24.1",
25
24
  "@vercel/routing-utils": "6.1.1",
26
- "@vercel/frameworks": "3.24.1"
25
+ "@vercel/build-utils": "13.18.0",
26
+ "@vercel/error-utils": "2.0.3"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/glob": "7.2.0",