@objectstack/service-job 4.0.2 → 4.0.4

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/service-job@4.0.2 build /home/runner/work/framework/framework/packages/services/service-job
2
+ > @objectstack/service-job@4.0.4 build /home/runner/work/framework/framework/packages/services/service-job
3
3
  > tsup --config ../../../tsup.config.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -10,13 +10,13 @@
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- CJS dist/index.cjs 5.09 KB
14
- CJS dist/index.cjs.map 11.42 KB
15
- CJS ⚡️ Build success in 74ms
16
13
  ESM dist/index.js 3.99 KB
17
14
  ESM dist/index.js.map 10.88 KB
18
- ESM ⚡️ Build success in 74ms
15
+ ESM ⚡️ Build success in 82ms
16
+ CJS dist/index.cjs 5.09 KB
17
+ CJS dist/index.cjs.map 11.42 KB
18
+ CJS ⚡️ Build success in 84ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 14335ms
20
+ DTS ⚡️ Build success in 15151ms
21
21
  DTS dist/index.d.ts 3.60 KB
22
22
  DTS dist/index.d.cts 3.60 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @objectstack/service-job
2
2
 
3
+ ## 4.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [326b66b]
8
+ - @objectstack/spec@4.0.4
9
+ - @objectstack/core@4.0.4
10
+
11
+ ## 4.0.3
12
+
13
+ ### Patch Changes
14
+
15
+ - @objectstack/spec@4.0.3
16
+ - @objectstack/core@4.0.3
17
+
3
18
  ## 4.0.2
4
19
 
5
20
  ### Patch Changes
package/README.md ADDED
@@ -0,0 +1,371 @@
1
+ # @objectstack/service-job
2
+
3
+ Job Service for ObjectStack — implements `IJobService` with setInterval and cron scheduling.
4
+
5
+ ## Features
6
+
7
+ - **Cron Scheduling**: Schedule jobs with cron expressions
8
+ - **Interval Scheduling**: Run jobs at fixed intervals
9
+ - **Job Queue**: Manage job execution queue
10
+ - **Retry Logic**: Automatic retry on failure with exponential backoff
11
+ - **Job History**: Track execution history and status
12
+ - **Concurrency Control**: Limit concurrent job execution
13
+ - **Timezone Support**: Schedule jobs in specific timezones
14
+ - **Type-Safe**: Full TypeScript support
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pnpm add @objectstack/service-job
20
+ ```
21
+
22
+ ## Basic Usage
23
+
24
+ ```typescript
25
+ import { defineStack } from '@objectstack/spec';
26
+ import { ServiceJob } from '@objectstack/service-job';
27
+
28
+ const stack = defineStack({
29
+ services: [
30
+ ServiceJob.configure({
31
+ timezone: 'America/New_York',
32
+ maxConcurrent: 5,
33
+ }),
34
+ ],
35
+ });
36
+ ```
37
+
38
+ ## Configuration
39
+
40
+ ```typescript
41
+ interface JobServiceConfig {
42
+ /** Default timezone for cron jobs (default: 'UTC') */
43
+ timezone?: string;
44
+
45
+ /** Maximum concurrent job executions (default: 10) */
46
+ maxConcurrent?: number;
47
+
48
+ /** Enable job history tracking (default: true) */
49
+ enableHistory?: boolean;
50
+
51
+ /** Maximum history entries per job (default: 100) */
52
+ maxHistorySize?: number;
53
+ }
54
+ ```
55
+
56
+ ## Service API
57
+
58
+ ```typescript
59
+ // Get job service
60
+ const jobs = kernel.getService<IJobService>('job');
61
+ ```
62
+
63
+ ### Cron Jobs
64
+
65
+ ```typescript
66
+ // Schedule a job with cron expression
67
+ const job = await jobs.schedule({
68
+ name: 'daily_report',
69
+ schedule: '0 9 * * *', // Every day at 9 AM
70
+ handler: async (context) => {
71
+ console.log('Generating daily report...');
72
+ // Your job logic here
73
+ },
74
+ timezone: 'America/New_York',
75
+ });
76
+
77
+ // Common cron patterns:
78
+ // '*/5 * * * *' - Every 5 minutes
79
+ // '0 */2 * * *' - Every 2 hours
80
+ // '0 9 * * 1-5' - Weekdays at 9 AM
81
+ // '0 0 1 * *' - First day of every month at midnight
82
+ // '0 0 * * 0' - Every Sunday at midnight
83
+ ```
84
+
85
+ ### Interval Jobs
86
+
87
+ ```typescript
88
+ // Run every 30 seconds
89
+ const job = await jobs.scheduleInterval({
90
+ name: 'health_check',
91
+ interval: 30000, // milliseconds
92
+ handler: async (context) => {
93
+ console.log('Running health check...');
94
+ },
95
+ });
96
+
97
+ // Run every 5 minutes
98
+ const job = await jobs.scheduleInterval({
99
+ name: 'sync_data',
100
+ interval: 5 * 60 * 1000, // 5 minutes
101
+ handler: async (context) => {
102
+ // Sync data
103
+ },
104
+ });
105
+ ```
106
+
107
+ ### One-Time Jobs
108
+
109
+ ```typescript
110
+ // Schedule a one-time job
111
+ const job = await jobs.scheduleOnce({
112
+ name: 'send_reminder',
113
+ runAt: new Date('2024-12-25T09:00:00Z'),
114
+ handler: async (context) => {
115
+ console.log('Sending holiday reminder...');
116
+ },
117
+ });
118
+
119
+ // Schedule to run after a delay
120
+ const job = await jobs.scheduleOnce({
121
+ name: 'delayed_task',
122
+ delay: 3600000, // 1 hour from now
123
+ handler: async (context) => {
124
+ console.log('Executing delayed task...');
125
+ },
126
+ });
127
+ ```
128
+
129
+ ### Job Management
130
+
131
+ ```typescript
132
+ // List all jobs
133
+ const allJobs = await jobs.listJobs();
134
+
135
+ // Get job details
136
+ const job = await jobs.getJob('daily_report');
137
+
138
+ // Stop a job
139
+ await jobs.stopJob('daily_report');
140
+
141
+ // Resume a stopped job
142
+ await jobs.resumeJob('daily_report');
143
+
144
+ // Delete a job
145
+ await jobs.deleteJob('daily_report');
146
+
147
+ // Run a job immediately (ignoring schedule)
148
+ await jobs.runNow('daily_report');
149
+ ```
150
+
151
+ ## Advanced Features
152
+
153
+ ### Job Context
154
+
155
+ ```typescript
156
+ const job = await jobs.schedule({
157
+ name: 'process_orders',
158
+ schedule: '*/10 * * * *',
159
+ handler: async (context) => {
160
+ console.log('Job name:', context.jobName);
161
+ console.log('Execution ID:', context.executionId);
162
+ console.log('Scheduled time:', context.scheduledTime);
163
+ console.log('Execution count:', context.executionCount);
164
+
165
+ // Access services
166
+ const db = context.kernel.getService('database');
167
+ const orders = await db.find({ object: 'order', status: 'pending' });
168
+
169
+ // Process orders...
170
+ },
171
+ });
172
+ ```
173
+
174
+ ### Retry Configuration
175
+
176
+ ```typescript
177
+ const job = await jobs.schedule({
178
+ name: 'api_sync',
179
+ schedule: '0 * * * *', // Every hour
180
+ retry: {
181
+ maxAttempts: 3,
182
+ backoff: 'exponential', // 'linear' or 'exponential'
183
+ initialDelay: 1000, // 1 second
184
+ maxDelay: 60000, // 1 minute
185
+ },
186
+ handler: async (context) => {
187
+ // May fail and retry
188
+ await syncWithExternalAPI();
189
+ },
190
+ });
191
+ ```
192
+
193
+ ### Concurrency Control
194
+
195
+ ```typescript
196
+ const job = await jobs.schedule({
197
+ name: 'heavy_processing',
198
+ schedule: '*/5 * * * *',
199
+ concurrency: 1, // Only one instance can run at a time
200
+ handler: async (context) => {
201
+ // Long-running process
202
+ },
203
+ });
204
+ ```
205
+
206
+ ### Job History
207
+
208
+ ```typescript
209
+ // Get execution history for a job
210
+ const history = await jobs.getJobHistory('daily_report', {
211
+ limit: 50,
212
+ status: 'success', // 'success', 'failed', 'running'
213
+ });
214
+
215
+ // Example history entry:
216
+ // {
217
+ // executionId: 'exec:abc123',
218
+ // jobName: 'daily_report',
219
+ // status: 'success',
220
+ // startedAt: '2024-01-15T09:00:00Z',
221
+ // completedAt: '2024-01-15T09:05:23Z',
222
+ // duration: 323000, // milliseconds
223
+ // error: null,
224
+ // result: { records: 1250 }
225
+ // }
226
+
227
+ // Clear history for a job
228
+ await jobs.clearHistory('daily_report');
229
+ ```
230
+
231
+ ### Job Data & Results
232
+
233
+ ```typescript
234
+ const job = await jobs.schedule({
235
+ name: 'data_export',
236
+ schedule: '0 0 * * *',
237
+ handler: async (context) => {
238
+ const records = await exportData();
239
+
240
+ // Return result data
241
+ return {
242
+ recordCount: records.length,
243
+ fileSize: calculateSize(records),
244
+ exportedAt: new Date(),
245
+ };
246
+ },
247
+ });
248
+
249
+ // Get last execution result
250
+ const lastRun = await jobs.getLastExecution('data_export');
251
+ console.log('Last export:', lastRun.result);
252
+ ```
253
+
254
+ ## Common Patterns
255
+
256
+ ### Database Cleanup Job
257
+
258
+ ```typescript
259
+ jobs.schedule({
260
+ name: 'cleanup_old_records',
261
+ schedule: '0 2 * * *', // 2 AM daily
262
+ handler: async (context) => {
263
+ const db = context.kernel.getService('database');
264
+
265
+ // Delete records older than 90 days
266
+ const cutoff = new Date();
267
+ cutoff.setDate(cutoff.getDate() - 90);
268
+
269
+ await db.delete({
270
+ object: 'audit_log',
271
+ filters: [{ field: 'created_at', operator: 'lt', value: cutoff }],
272
+ });
273
+ },
274
+ });
275
+ ```
276
+
277
+ ### Report Generation Job
278
+
279
+ ```typescript
280
+ jobs.schedule({
281
+ name: 'weekly_sales_report',
282
+ schedule: '0 8 * * 1', // Mondays at 8 AM
283
+ handler: async (context) => {
284
+ const analytics = context.kernel.getService('analytics');
285
+
286
+ const data = await analytics.query({
287
+ object: 'order',
288
+ aggregations: [{ function: 'sum', field: 'amount' }],
289
+ groupBy: ['sales_rep'],
290
+ filters: [{ field: 'created_at', operator: 'last_week' }],
291
+ });
292
+
293
+ // Generate and email report
294
+ await sendReport(data);
295
+ },
296
+ });
297
+ ```
298
+
299
+ ### Cache Warming Job
300
+
301
+ ```typescript
302
+ jobs.scheduleInterval({
303
+ name: 'warm_cache',
304
+ interval: 15 * 60 * 1000, // Every 15 minutes
305
+ handler: async (context) => {
306
+ const cache = context.kernel.getService('cache');
307
+
308
+ // Pre-load frequently accessed data
309
+ const popularProducts = await getPopularProducts();
310
+ await cache.set('popular_products', popularProducts, { ttl: 900 });
311
+ },
312
+ });
313
+ ```
314
+
315
+ ## REST API Endpoints
316
+
317
+ ```
318
+ GET /api/v1/jobs # List all jobs
319
+ GET /api/v1/jobs/:name # Get job details
320
+ POST /api/v1/jobs/:name/run # Run job immediately
321
+ POST /api/v1/jobs/:name/stop # Stop job
322
+ POST /api/v1/jobs/:name/resume # Resume job
323
+ DELETE /api/v1/jobs/:name # Delete job
324
+ GET /api/v1/jobs/:name/history # Get execution history
325
+ ```
326
+
327
+ ## Best Practices
328
+
329
+ 1. **Idempotent Handlers**: Job handlers should be idempotent (safe to run multiple times)
330
+ 2. **Error Handling**: Always handle errors gracefully and log failures
331
+ 3. **Timeout Limits**: Set reasonable timeout limits for long-running jobs
332
+ 4. **Resource Limits**: Limit concurrent executions to avoid overloading the system
333
+ 5. **Monitoring**: Monitor job execution times and failure rates
334
+ 6. **Timezone Awareness**: Always specify timezone for cron jobs to avoid ambiguity
335
+ 7. **Cleanup**: Periodically delete old job history to save storage
336
+
337
+ ## Performance Considerations
338
+
339
+ - **Concurrency**: Limit concurrent jobs based on system resources
340
+ - **Job Duration**: Keep job execution time reasonable (< 5 minutes ideal)
341
+ - **History Size**: Limit history entries to prevent memory bloat
342
+ - **Batch Processing**: Process records in batches for large datasets
343
+
344
+ ## Contract Implementation
345
+
346
+ Implements `IJobService` from `@objectstack/spec/contracts`:
347
+
348
+ ```typescript
349
+ interface IJobService {
350
+ schedule(options: ScheduleOptions): Promise<Job>;
351
+ scheduleInterval(options: IntervalOptions): Promise<Job>;
352
+ scheduleOnce(options: OnceOptions): Promise<Job>;
353
+ getJob(name: string): Promise<Job>;
354
+ listJobs(filter?: JobFilter): Promise<Job[]>;
355
+ stopJob(name: string): Promise<void>;
356
+ resumeJob(name: string): Promise<void>;
357
+ deleteJob(name: string): Promise<void>;
358
+ runNow(name: string): Promise<JobExecution>;
359
+ getJobHistory(name: string, options?: HistoryOptions): Promise<JobExecution[]>;
360
+ }
361
+ ```
362
+
363
+ ## License
364
+
365
+ Apache-2.0
366
+
367
+ ## See Also
368
+
369
+ - [Cron Expression Generator](https://crontab.guru/)
370
+ - [@objectstack/spec/contracts](../../spec/src/contracts/)
371
+ - [Job Scheduling Guide](/content/docs/guides/jobs/)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/service-job",
3
- "version": "4.0.2",
3
+ "version": "4.0.4",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Job Service for ObjectStack — implements IJobService with setInterval and cron scheduling",
6
6
  "type": "module",
@@ -14,13 +14,13 @@
14
14
  }
15
15
  },
16
16
  "dependencies": {
17
- "@objectstack/core": "4.0.2",
18
- "@objectstack/spec": "4.0.2"
17
+ "@objectstack/core": "4.0.4",
18
+ "@objectstack/spec": "4.0.4"
19
19
  },
20
20
  "devDependencies": {
21
- "@types/node": "^25.5.2",
21
+ "@types/node": "^25.6.0",
22
22
  "typescript": "^6.0.2",
23
- "vitest": "^4.1.2"
23
+ "vitest": "^4.1.4"
24
24
  },
25
25
  "scripts": {
26
26
  "build": "tsup --config ../../../tsup.config.ts",