11import Cron from './cron' ;
2- import {
3- CronOptions ,
4- IBaker ,
5- IBakerOptions ,
6- ICron ,
7- Status ,
8- ExecutionHistory ,
2+ import {
3+ CronOptions ,
4+ IBaker ,
5+ IBakerOptions ,
6+ ICron ,
7+ Status ,
8+ ExecutionHistory ,
99 JobMetrics ,
1010 SchedulerConfig ,
11- PersistenceOptions
11+ PersistenceOptions ,
1212} from './types' ;
13- import * as fs from 'fs ' ;
14- import * as path from 'path ' ;
13+ import { FilePersistenceProvider } from './persistence/file ' ;
14+ import type { PersistenceProvider } from './persistence/types ' ;
1515
1616/**
1717 * A class that implements the `IBaker` interface and provides methods to manage cron jobs.
@@ -20,6 +20,7 @@ class Baker implements IBaker {
2020 private crons : Map < string , ICron > = new Map ( ) ;
2121 private config : SchedulerConfig ;
2222 private persistence : PersistenceOptions ;
23+ private persistenceProvider ?: PersistenceProvider ;
2324 private enableMetrics : boolean ;
2425 private onError ?: ( error : Error , jobName : string ) => void ;
2526
@@ -34,7 +35,20 @@ class Baker implements IBaker {
3435 enabled : options . persistence ?. enabled ?? false ,
3536 filePath : options . persistence ?. filePath ?? './cronbake-state.json' ,
3637 autoRestore : options . persistence ?. autoRestore ?? true ,
38+ strategy : options . persistence ?. strategy ?? 'file' ,
39+ provider : options . persistence ?. provider ,
40+ redis : options . persistence ?. redis ,
3741 } ;
42+
43+ if ( this . persistence . enabled ) {
44+ if ( this . persistence . provider ) {
45+ this . persistenceProvider = this . persistence . provider ;
46+ } else if ( this . persistence . strategy === 'file' ) {
47+ this . persistenceProvider = new FilePersistenceProvider ( this . persistence . filePath ! ) ;
48+ } else if ( this . persistence . strategy === 'redis' ) {
49+ throw new Error ( 'Redis persistence selected but no provider supplied. Pass persistence.provider or use FilePersistenceProvider.' ) ;
50+ }
51+ }
3852
3953 this . enableMetrics = options . enableMetrics ?? true ;
4054 this . onError = options . onError ;
@@ -225,58 +239,40 @@ class Baker implements IBaker {
225239 }
226240
227241 async saveState ( ) : Promise < void > {
228- if ( ! this . persistence . enabled ) return ;
229-
242+ if ( ! this . persistence . enabled || ! this . persistenceProvider ) return ;
230243 try {
231244 const state = {
245+ version : 1 ,
232246 timestamp : new Date ( ) . toISOString ( ) ,
233247 jobs : Array . from ( this . crons . entries ( ) ) . map ( ( [ name , cron ] ) => ( {
234248 name,
235- cron : cron . cron ,
249+ cron : String ( cron . cron ) ,
236250 status : cron . getStatus ( ) ,
237251 priority : cron . priority ,
238252 metrics : this . enableMetrics ? cron . getMetrics ( ) : undefined ,
239253 history : this . enableMetrics ? cron . getHistory ( ) : undefined ,
240254 } ) ) ,
241255 config : this . config ,
242- } ;
243-
244- const filePath = path . resolve ( this . persistence . filePath ! ) ;
245- const dir = path . dirname ( filePath ) ;
246-
247- if ( ! fs . existsSync ( dir ) ) {
248- fs . mkdirSync ( dir , { recursive : true } ) ;
249- }
250-
251- await fs . promises . writeFile ( filePath , JSON . stringify ( state , null , 2 ) , 'utf8' ) ;
256+ } as const ;
257+ await this . persistenceProvider . save ( state ) ;
252258 } catch ( error ) {
253259 throw new Error ( `Failed to save state: ${ error } ` ) ;
254260 }
255261 }
256262
257263 async restoreState ( ) : Promise < void > {
258- if ( ! this . persistence . enabled ) return ;
259-
264+ if ( ! this . persistence . enabled || ! this . persistenceProvider ) return ;
260265 try {
261- const filePath = path . resolve ( this . persistence . filePath ! ) ;
262-
263- if ( ! fs . existsSync ( filePath ) ) {
264- return ;
265- }
266-
267- const data = await fs . promises . readFile ( filePath , 'utf8' ) ;
268- const state = JSON . parse ( data ) ;
269-
266+ const state = await this . persistenceProvider . load ( ) ;
267+ if ( ! state ) return ;
270268 if ( ! state . jobs || ! Array . isArray ( state . jobs ) ) {
271- throw new Error ( 'Invalid state file format' ) ;
269+ throw new Error ( 'Invalid state format' ) ;
272270 }
273-
274271 for ( const jobData of state . jobs ) {
275272 if ( ! jobData . name || ! jobData . cron ) {
276273 console . warn ( 'Skipping invalid job data:' , jobData ) ;
277274 continue ;
278275 }
279-
280276 try {
281277 const options : CronOptions = {
282278 name : jobData . name ,
@@ -287,13 +283,11 @@ class Baker implements IBaker {
287283 priority : jobData . priority ,
288284 start : jobData . status === 'running' ,
289285 } ;
290-
291286 this . add ( options ) ;
292287 } catch ( error ) {
293288 console . warn ( `Failed to restore job '${ jobData . name } ':` , error ) ;
294289 }
295290 }
296-
297291 console . log ( `Restored ${ state . jobs . length } cron jobs from persistence` ) ;
298292 } catch ( error ) {
299293 throw new Error ( `Failed to restore state: ${ error } ` ) ;
0 commit comments