workflow-cron-sleep 1.0.1 → 1.0.2

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.
Files changed (2) hide show
  1. package/README.md +39 -31
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -88,14 +88,14 @@ Throws an error if the cron expression is invalid or has no future occurrences.
88
88
 
89
89
  Since `cronSleep` only sleeps until the **next** occurrence, you need to design your workflow architecture to handle recurring schedules.
90
90
 
91
- ### Pattern: Separate Scheduler and Processor Workflows
91
+ ### Pattern: While Loop Scheduler
92
92
 
93
- Separate your scheduling logic from your business logic by keeping workflows and steps in different files:
93
+ The simplest approach is a single workflow with a `while (true)` loop that sleeps and triggers your business logic workflow repeatedly:
94
94
 
95
95
  **`workflows/report-scheduler.ts`** - The scheduler workflow
96
96
  ```typescript
97
97
  import { cronSleep } from "workflow-cron-sleep"
98
- import { startReportProcessor } from "./steps"
98
+ import { triggerReportProcessor } from "./steps"
99
99
 
100
100
  export interface ReportSchedulerInput {
101
101
  reportId: string
@@ -107,57 +107,65 @@ export async function reportScheduler(input: ReportSchedulerInput) {
107
107
 
108
108
  const { reportId, cronExpression } = input
109
109
 
110
- // Wait for the next scheduled time
111
- await cronSleep(cronExpression, { timezone: "America/New_York" })
112
-
113
- // Trigger the processor workflow
114
- await startReportProcessor(reportId, cronExpression)
110
+ while (true) {
111
+ // Wait for the next scheduled time
112
+ await cronSleep(cronExpression, { timezone: "America/New_York" })
113
+
114
+ // Trigger the processor workflow
115
+ await triggerReportProcessor(reportId)
116
+ }
115
117
  }
116
118
  ```
117
119
 
118
- **`workflows/report-processor.ts`** - The processor workflow (business logic)
120
+ **`workflows/steps.ts`** - Step function to start the processor
119
121
  ```typescript
120
- import { restartScheduler } from "./steps"
122
+ import { start } from "workflow/api"
123
+ import { reportProcessorWorkflow } from "./report-processor"
124
+
125
+ export async function triggerReportProcessor(reportId: string) {
126
+ "use step"
127
+ await start(reportProcessorWorkflow, [{ reportId }])
128
+ }
129
+ ```
121
130
 
131
+ **`workflows/report-processor.ts`** - The processor workflow (business logic)
132
+ ```typescript
122
133
  export interface ReportProcessorInput {
123
134
  reportId: string
124
- cronExpression: string
125
135
  }
126
136
 
127
137
  export async function reportProcessorWorkflow(input: ReportProcessorInput) {
128
138
  "use workflow"
129
139
 
130
- const { reportId, cronExpression } = input
140
+ const { reportId } = input
131
141
 
132
- // Your business logic here
142
+ // Your business logic here - completely agnostic to scheduling
133
143
  await generateReport(reportId)
134
144
  await sendNotifications(reportId)
135
-
136
- // Restart the scheduler for the next occurrence
137
- await restartScheduler(reportId, cronExpression)
138
145
  }
139
146
  ```
140
147
 
141
- **`workflows/steps.ts`** - Step functions in a separate file
148
+ ### Starting and Canceling
149
+
142
150
  ```typescript
143
- import { start } from "workflow"
144
- import { reportProcessorWorkflow } from "./report-processor"
145
- import { reportScheduler } from "./report-scheduler"
151
+ import { start, getRun } from "workflow/api"
152
+ import { reportScheduler } from "./workflows/report-scheduler"
146
153
 
147
- export async function startReportProcessor(reportId: string, cronExpression: string) {
148
- "use step"
149
- await start(reportProcessorWorkflow, [{ reportId, cronExpression }])
150
- }
154
+ // Start the cron scheduler
155
+ const run = await start(reportScheduler, [{
156
+ reportId: "daily-report",
157
+ cronExpression: "0 9 * * *"
158
+ }])
151
159
 
152
- export async function restartScheduler(reportId: string, cronExpression: string) {
153
- "use step"
154
- await start(reportScheduler, [{ reportId, cronExpression }])
155
- }
156
- ```
160
+ // Save the run ID to cancel later
161
+ await db.cronJobs.create({ runId: run.id })
157
162
 
158
- This pattern keeps your scheduling and business logic decoupled, and creates a recurring loop: scheduler → processor → scheduler → ...
163
+ // To cancel the cron (stops the while loop)
164
+ const currentRun = getRun(run.id)
165
+ await currentRun.cancel()
166
+ ```
159
167
 
160
- For this pattern, it is recommended to return and persist your `runId` for each scheduler workflow run, in case you need to cancel a run. If the cron expression changes, you can cancel the current scheduler run and create a new one with the new cron expression.
168
+ This pattern keeps your scheduling and business logic decoupled. The scheduler runs forever, triggering your processor workflow on schedule. Save the `runId` so you can cancel if the cron expression changes or the job is no longer needed.
161
169
 
162
170
  ## Requirements
163
171
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workflow-cron-sleep",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Sleep until the next cron expression occurrence in Vercel Workflows",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",