@objectstack/service-job 4.0.5 → 4.1.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.
package/dist/index.d.cts CHANGED
@@ -33,40 +33,101 @@ declare class IntervalJobAdapter implements IJobService {
33
33
  private executeJob;
34
34
  }
35
35
 
36
+ interface JobEngineLike {
37
+ find(object: string, options?: any): Promise<any[]>;
38
+ insert(object: string, data: any, options?: any): Promise<any>;
39
+ update(object: string, idOrData: any, dataOrOptions?: any, options?: any): Promise<any>;
40
+ delete?(object: string, options?: any): Promise<any>;
41
+ }
42
+ interface JobLoggerLike {
43
+ info(msg: string, meta?: unknown): void;
44
+ warn(msg: string, meta?: unknown): void;
45
+ error?(msg: string, meta?: unknown): void;
46
+ }
47
+ interface DbJobAdapterOptions {
48
+ /** Maximum executions kept in memory per job (default 100) */
49
+ maxExecutions?: number;
50
+ /** Soft cap on sys_job_run rows recorded per job (defaults to none — handled by retention jobs) */
51
+ recordRuns?: boolean;
52
+ }
53
+ /**
54
+ * DbJobAdapter — IJobService that persists job registry and execution
55
+ * history to ObjectQL while delegating timer mechanics to
56
+ * `IntervalJobAdapter`. Cron is delegated to `CronJobAdapter` callers
57
+ * supplied via {@link withCron}.
58
+ *
59
+ * Persisted side effects:
60
+ * - `schedule(name, …)` upserts a `sys_job` row (active=true)
61
+ * - `cancel(name)` marks the row inactive
62
+ * - every execution writes a `sys_job_run` row
63
+ * - every execution updates `sys_job.last_run_at / last_status / run_count / failure_count`
64
+ *
65
+ * The persistence is best-effort: a DB failure is logged but does not
66
+ * break job execution. This keeps a healthy job system resilient to
67
+ * transient storage hiccups.
68
+ */
69
+ declare class DbJobAdapter implements IJobService {
70
+ private readonly inner;
71
+ private readonly cron?;
72
+ private readonly engine;
73
+ private readonly logger?;
74
+ private readonly recordRuns;
75
+ constructor(args: {
76
+ engine: JobEngineLike;
77
+ logger?: JobLoggerLike;
78
+ options?: DbJobAdapterOptions;
79
+ cron?: IJobService;
80
+ });
81
+ schedule(name: string, schedule: JobSchedule, handler: JobHandler): Promise<void>;
82
+ cancel(name: string): Promise<void>;
83
+ trigger(name: string, data?: unknown): Promise<void>;
84
+ getExecutions(name: string, limit?: number): Promise<JobExecution[]>;
85
+ listJobs(): Promise<string[]>;
86
+ replay(name: string, data?: unknown): Promise<void>;
87
+ listExecutionsByStatus(status: JobExecution['status'], limit?: number): Promise<JobExecution[]>;
88
+ destroy(): Promise<void>;
89
+ private wrap;
90
+ private startRun;
91
+ private finishRun;
92
+ private upsertJobRow;
93
+ private setActive;
94
+ private bumpJob;
95
+ }
96
+
36
97
  /**
37
98
  * Configuration options for the JobServicePlugin.
38
99
  */
39
100
  interface JobServicePluginOptions {
40
- /** Job adapter type (default: 'interval') */
41
- adapter?: 'interval' | 'cron';
101
+ /**
102
+ * Job adapter type.
103
+ * - 'auto' (default): use DbJobAdapter when objectql engine available, else IntervalJobAdapter
104
+ * - 'db': require objectql; persists schedules and runs to sys_job/sys_job_run
105
+ * - 'interval': in-memory IntervalJobAdapter (legacy, non-durable)
106
+ * - 'cron': in-memory CronJobAdapter using `croner`
107
+ */
108
+ adapter?: 'auto' | 'db' | 'interval' | 'cron';
42
109
  /** Options for the interval job adapter */
43
110
  interval?: IntervalJobAdapterOptions;
111
+ /** Options for the DB adapter */
112
+ db?: DbJobAdapterOptions;
113
+ /** Whether to also wire CronJobAdapter for cron schedules (default: true when available) */
114
+ enableCron?: boolean;
44
115
  }
45
116
  /**
46
117
  * JobServicePlugin — Production IJobService implementation.
47
118
  *
48
- * Registers a job scheduler with the kernel during the init phase.
49
- * Supports setInterval-based and cron-based adapters.
50
- *
51
- * @example
52
- * ```ts
53
- * import { ObjectKernel } from '@objectstack/core';
54
- * import { JobServicePlugin } from '@objectstack/service-job';
55
- *
56
- * const kernel = new ObjectKernel();
57
- * kernel.use(new JobServicePlugin({ adapter: 'interval' }));
58
- * await kernel.bootstrap();
59
- *
60
- * const job = kernel.getService('job');
61
- * await job.schedule('cleanup', { type: 'interval', intervalMs: 60000 }, handler);
62
- * ```
119
+ * Default behaviour: registers a `DbJobAdapter` when the ObjectQL engine is
120
+ * available (persisting registry + execution history to `sys_job` and
121
+ * `sys_job_run`), falling back to in-memory `IntervalJobAdapter` otherwise.
122
+ * Cron schedules are routed to `CronJobAdapter` (croner-backed).
63
123
  */
64
124
  declare class JobServicePlugin implements Plugin {
65
125
  name: string;
66
126
  version: string;
67
127
  type: string;
68
128
  private readonly options;
69
- private adapter?;
129
+ private dbAdapter?;
130
+ private intervalAdapter?;
70
131
  constructor(options?: JobServicePluginOptions);
71
132
  init(ctx: PluginContext): Promise<void>;
72
133
  destroy(): Promise<void>;
@@ -78,27 +139,29 @@ declare class JobServicePlugin implements Plugin {
78
139
  interface CronJobAdapterOptions {
79
140
  /** Timezone for cron expressions (default: 'UTC') */
80
141
  timezone?: string;
142
+ /** Maximum execution history per job (default: 100) */
143
+ maxExecutions?: number;
81
144
  }
82
145
  /**
83
- * Cron-based job adapter skeleton implementing IJobService.
84
- *
85
- * This is a placeholder for future cron integration (e.g., `node-cron` or `croner`).
86
- * Concrete implementation will parse cron expressions and schedule jobs accordingly.
87
- *
88
- * @example
89
- * ```ts
90
- * const scheduler = new CronJobAdapter({ timezone: 'America/New_York' });
91
- * await scheduler.schedule('nightly-cleanup', { type: 'cron', expression: '0 0 * * *' }, handler);
92
- * ```
146
+ * Cron-based job adapter implementing IJobService using the `croner`
147
+ * library. Honours per-job timezones, supports the standard 5-field cron
148
+ * syntax, and falls back to setInterval / setTimeout for `interval` and
149
+ * `once` schedule types (so a single CronJobAdapter can serve as the
150
+ * "real" production job runner).
93
151
  */
94
152
  declare class CronJobAdapter implements IJobService {
95
- private readonly timezone;
153
+ private readonly defaultTimezone;
154
+ private readonly maxExecutions;
155
+ private readonly jobs;
96
156
  constructor(options?: CronJobAdapterOptions);
97
- schedule(_name: string, _schedule: JobSchedule, _handler: JobHandler): Promise<void>;
98
- cancel(_name: string): Promise<void>;
99
- trigger(_name: string, _data?: unknown): Promise<void>;
100
- getExecutions(_name: string, _limit?: number): Promise<JobExecution[]>;
157
+ schedule(name: string, schedule: JobSchedule, handler: JobHandler): Promise<void>;
158
+ cancel(name: string): Promise<void>;
159
+ trigger(name: string, data?: unknown): Promise<void>;
160
+ getExecutions(name: string, limit?: number): Promise<JobExecution[]>;
101
161
  listJobs(): Promise<string[]>;
162
+ /** Stop all timers — call from plugin destroy. */
163
+ destroy(): Promise<void>;
164
+ private execute;
102
165
  }
103
166
 
104
- export { CronJobAdapter, type CronJobAdapterOptions, IntervalJobAdapter, type IntervalJobAdapterOptions, JobServicePlugin, type JobServicePluginOptions };
167
+ export { CronJobAdapter, type CronJobAdapterOptions, DbJobAdapter, type DbJobAdapterOptions, IntervalJobAdapter, type IntervalJobAdapterOptions, type JobEngineLike, type JobLoggerLike, JobServicePlugin, type JobServicePluginOptions };
package/dist/index.d.ts CHANGED
@@ -33,40 +33,101 @@ declare class IntervalJobAdapter implements IJobService {
33
33
  private executeJob;
34
34
  }
35
35
 
36
+ interface JobEngineLike {
37
+ find(object: string, options?: any): Promise<any[]>;
38
+ insert(object: string, data: any, options?: any): Promise<any>;
39
+ update(object: string, idOrData: any, dataOrOptions?: any, options?: any): Promise<any>;
40
+ delete?(object: string, options?: any): Promise<any>;
41
+ }
42
+ interface JobLoggerLike {
43
+ info(msg: string, meta?: unknown): void;
44
+ warn(msg: string, meta?: unknown): void;
45
+ error?(msg: string, meta?: unknown): void;
46
+ }
47
+ interface DbJobAdapterOptions {
48
+ /** Maximum executions kept in memory per job (default 100) */
49
+ maxExecutions?: number;
50
+ /** Soft cap on sys_job_run rows recorded per job (defaults to none — handled by retention jobs) */
51
+ recordRuns?: boolean;
52
+ }
53
+ /**
54
+ * DbJobAdapter — IJobService that persists job registry and execution
55
+ * history to ObjectQL while delegating timer mechanics to
56
+ * `IntervalJobAdapter`. Cron is delegated to `CronJobAdapter` callers
57
+ * supplied via {@link withCron}.
58
+ *
59
+ * Persisted side effects:
60
+ * - `schedule(name, …)` upserts a `sys_job` row (active=true)
61
+ * - `cancel(name)` marks the row inactive
62
+ * - every execution writes a `sys_job_run` row
63
+ * - every execution updates `sys_job.last_run_at / last_status / run_count / failure_count`
64
+ *
65
+ * The persistence is best-effort: a DB failure is logged but does not
66
+ * break job execution. This keeps a healthy job system resilient to
67
+ * transient storage hiccups.
68
+ */
69
+ declare class DbJobAdapter implements IJobService {
70
+ private readonly inner;
71
+ private readonly cron?;
72
+ private readonly engine;
73
+ private readonly logger?;
74
+ private readonly recordRuns;
75
+ constructor(args: {
76
+ engine: JobEngineLike;
77
+ logger?: JobLoggerLike;
78
+ options?: DbJobAdapterOptions;
79
+ cron?: IJobService;
80
+ });
81
+ schedule(name: string, schedule: JobSchedule, handler: JobHandler): Promise<void>;
82
+ cancel(name: string): Promise<void>;
83
+ trigger(name: string, data?: unknown): Promise<void>;
84
+ getExecutions(name: string, limit?: number): Promise<JobExecution[]>;
85
+ listJobs(): Promise<string[]>;
86
+ replay(name: string, data?: unknown): Promise<void>;
87
+ listExecutionsByStatus(status: JobExecution['status'], limit?: number): Promise<JobExecution[]>;
88
+ destroy(): Promise<void>;
89
+ private wrap;
90
+ private startRun;
91
+ private finishRun;
92
+ private upsertJobRow;
93
+ private setActive;
94
+ private bumpJob;
95
+ }
96
+
36
97
  /**
37
98
  * Configuration options for the JobServicePlugin.
38
99
  */
39
100
  interface JobServicePluginOptions {
40
- /** Job adapter type (default: 'interval') */
41
- adapter?: 'interval' | 'cron';
101
+ /**
102
+ * Job adapter type.
103
+ * - 'auto' (default): use DbJobAdapter when objectql engine available, else IntervalJobAdapter
104
+ * - 'db': require objectql; persists schedules and runs to sys_job/sys_job_run
105
+ * - 'interval': in-memory IntervalJobAdapter (legacy, non-durable)
106
+ * - 'cron': in-memory CronJobAdapter using `croner`
107
+ */
108
+ adapter?: 'auto' | 'db' | 'interval' | 'cron';
42
109
  /** Options for the interval job adapter */
43
110
  interval?: IntervalJobAdapterOptions;
111
+ /** Options for the DB adapter */
112
+ db?: DbJobAdapterOptions;
113
+ /** Whether to also wire CronJobAdapter for cron schedules (default: true when available) */
114
+ enableCron?: boolean;
44
115
  }
45
116
  /**
46
117
  * JobServicePlugin — Production IJobService implementation.
47
118
  *
48
- * Registers a job scheduler with the kernel during the init phase.
49
- * Supports setInterval-based and cron-based adapters.
50
- *
51
- * @example
52
- * ```ts
53
- * import { ObjectKernel } from '@objectstack/core';
54
- * import { JobServicePlugin } from '@objectstack/service-job';
55
- *
56
- * const kernel = new ObjectKernel();
57
- * kernel.use(new JobServicePlugin({ adapter: 'interval' }));
58
- * await kernel.bootstrap();
59
- *
60
- * const job = kernel.getService('job');
61
- * await job.schedule('cleanup', { type: 'interval', intervalMs: 60000 }, handler);
62
- * ```
119
+ * Default behaviour: registers a `DbJobAdapter` when the ObjectQL engine is
120
+ * available (persisting registry + execution history to `sys_job` and
121
+ * `sys_job_run`), falling back to in-memory `IntervalJobAdapter` otherwise.
122
+ * Cron schedules are routed to `CronJobAdapter` (croner-backed).
63
123
  */
64
124
  declare class JobServicePlugin implements Plugin {
65
125
  name: string;
66
126
  version: string;
67
127
  type: string;
68
128
  private readonly options;
69
- private adapter?;
129
+ private dbAdapter?;
130
+ private intervalAdapter?;
70
131
  constructor(options?: JobServicePluginOptions);
71
132
  init(ctx: PluginContext): Promise<void>;
72
133
  destroy(): Promise<void>;
@@ -78,27 +139,29 @@ declare class JobServicePlugin implements Plugin {
78
139
  interface CronJobAdapterOptions {
79
140
  /** Timezone for cron expressions (default: 'UTC') */
80
141
  timezone?: string;
142
+ /** Maximum execution history per job (default: 100) */
143
+ maxExecutions?: number;
81
144
  }
82
145
  /**
83
- * Cron-based job adapter skeleton implementing IJobService.
84
- *
85
- * This is a placeholder for future cron integration (e.g., `node-cron` or `croner`).
86
- * Concrete implementation will parse cron expressions and schedule jobs accordingly.
87
- *
88
- * @example
89
- * ```ts
90
- * const scheduler = new CronJobAdapter({ timezone: 'America/New_York' });
91
- * await scheduler.schedule('nightly-cleanup', { type: 'cron', expression: '0 0 * * *' }, handler);
92
- * ```
146
+ * Cron-based job adapter implementing IJobService using the `croner`
147
+ * library. Honours per-job timezones, supports the standard 5-field cron
148
+ * syntax, and falls back to setInterval / setTimeout for `interval` and
149
+ * `once` schedule types (so a single CronJobAdapter can serve as the
150
+ * "real" production job runner).
93
151
  */
94
152
  declare class CronJobAdapter implements IJobService {
95
- private readonly timezone;
153
+ private readonly defaultTimezone;
154
+ private readonly maxExecutions;
155
+ private readonly jobs;
96
156
  constructor(options?: CronJobAdapterOptions);
97
- schedule(_name: string, _schedule: JobSchedule, _handler: JobHandler): Promise<void>;
98
- cancel(_name: string): Promise<void>;
99
- trigger(_name: string, _data?: unknown): Promise<void>;
100
- getExecutions(_name: string, _limit?: number): Promise<JobExecution[]>;
157
+ schedule(name: string, schedule: JobSchedule, handler: JobHandler): Promise<void>;
158
+ cancel(name: string): Promise<void>;
159
+ trigger(name: string, data?: unknown): Promise<void>;
160
+ getExecutions(name: string, limit?: number): Promise<JobExecution[]>;
101
161
  listJobs(): Promise<string[]>;
162
+ /** Stop all timers — call from plugin destroy. */
163
+ destroy(): Promise<void>;
164
+ private execute;
102
165
  }
103
166
 
104
- export { CronJobAdapter, type CronJobAdapterOptions, IntervalJobAdapter, type IntervalJobAdapterOptions, JobServicePlugin, type JobServicePluginOptions };
167
+ export { CronJobAdapter, type CronJobAdapterOptions, DbJobAdapter, type DbJobAdapterOptions, IntervalJobAdapter, type IntervalJobAdapterOptions, type JobEngineLike, type JobLoggerLike, JobServicePlugin, type JobServicePluginOptions };