Files
2026-05-27 23:29:53 +01:00

2.7 KiB

jobsSchema

Purpose

Background-job block: cron schedules and queued workers. Handlers live in the code half of the manifest; this block is the declarative surface the host pre-registers so cron triggers and queue topics exist before plugin code runs.

Features

  • Cron expression validated against a permissive character regex
  • Optional timezone per cron entry (host applies a default if absent)
  • Optional perTenant flag for tenant-scoped cron
  • Queued jobs carry an optional maxAttempts retry cap

Basic Example

import { jobsSchema } from "@suaveplan/api-manifest/schema";
jobsSchema.parse({ cron: [{ name: "daily", expression: "0 0 * * *" }] });

Advanced Example

import { jobsSchema } from "@suaveplan/api-manifest/schema";
const jobs = jobsSchema.parse({
  cron: [
    { name: "reconcile", expression: "0 3 * * *", tz: "UTC", perTenant: true },
  ],
  queued: [{ name: "send-receipt", maxAttempts: 5 }],
});

API Reference

  • jobsSchema: z.ZodObject<{ cron?, queued? }>
  • cronJobSchema, queuedJobSchema — the building blocks.
  • Types: Jobs, CronJob, QueuedJob.

Implementation Notes

The cron expression regex permits the digits, asterisks, commas, slashes, and dashes that cover both 5-field and 6-field crontabs; the host's @suaveplan/cron parser is the source of truth for actual scheduling semantics, including L/W/# extensions. The schema's regex is a fast-fail surface, not the canonical parser. perTenant: true instructs the host's tenant fan-out to run one logical cron per tenant rather than a single host-wide cron.

Cross-package Notes

This module is part of the @suaveplan/api-manifest Stage-1 contract package. It sits beneath every Stage-2 consumer (api-host, api-loader, the admin CLI, the IDE plugin) and above the foundation packages (@suaveplan/error, @suaveplan/types). The strict-mode top-level rejection of unknown keys means upgrading from manifest v1 to v2 is an OpenSpec-driven event: a new key landing in a consumer manifest is a parse failure rather than a silent drift, which is exactly the contract this stage exists to guarantee.

When the host reloads a plugin without a full restart, every manifest field this module validates is compared against the install-time manifestHash. A mismatch triggers a hard rollback via the RollbackJournal because the host's invariants (route mounts, registered capabilities, declared scopes) are derived from these exact bytes. Treat the schemas and validators here as the contract surface, not just an input filter — downstream code reads manifest.foo.bar and relies on those keys being present, well-typed, and non-undefined in the documented optionality regions.