@azlib/scheduler 0.2.0 → 1.0.1

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/README.md CHANGED
@@ -1,108 +1,89 @@
1
1
  # @azlib/scheduler
2
2
 
3
- Cron-style job scheduling for Node.js workloads, designed to run standalone or embedded in host runtimes (for example Express).
4
-
5
- ## Install
6
-
7
- ```bash
8
- pnpm add @azlib/scheduler @azlib/queue @azlib/cache @azlib/logger
9
- ```
10
-
11
- ## Status
12
-
13
- This package is ready for phased implementation usage with:
14
-
15
- - schedule parsing and timezone normalization
16
- - job registration and lifecycle controls
17
- - standalone runtime start/stop orchestration
18
- - host lifecycle binding for embedded runtimes
19
- - queue-backed execution dispatch through `@azlib/queue`
20
- - cache integration through `@azlib/cache`
21
- - structured logs through `@azlib/logger`
22
-
23
- ## Basic Usage
3
+ Cron-style job scheduler for Node.js, supporting timezone normalization, overlap prevention policies, missed run executions, and database persistence.
4
+
5
+ ## Capabilities
6
+
7
+ - Schedule parsing and timezone normalization
8
+ - Job registration and lifecycle control (start, stop)
9
+ - Standalone runner execution or embedded hosting (e.g. Express)
10
+ - Host lifecycle bindings for graceful setups
11
+ - Queue-backed execution dispatch through `@azlib/queue`
12
+ - Cache coordination using `@azlib/cache`
13
+ - Structured database state persistence (SQL persistence)
14
+
15
+ ## AI Agent Quick Reference
16
+
17
+ ### Core Exports
18
+
19
+ | Export | Type | Description |
20
+ | --- | --- | --- |
21
+ | `createSchedulerService(options: SchedulerServiceOptions)` | Function | Instantiates a SchedulerService instance and a Handler Registry. |
22
+ | `createCronExpression(): CronExpressionBuilder` | Function | Fluent helper to construct standard 5-field cron strings. |
23
+ | `bindSchedulerToHost(service: SchedulerService, adapter: SchedulerHostAdapter)` | Function | Automatically starts/stops the scheduler based on custom server bindings. |
24
+ | `CronWeekday` | Enum | Monday through Sunday utility enum values. |
25
+
26
+ ### Core Types & Signatures
27
+
28
+ - `SchedulerService`:
29
+ - `start(): Promise<void>`
30
+ - `stop(): Promise<void>`
31
+ - `registerJob(job: SchedulerJobDefinition): Promise<void>`
32
+ - `unregisterJob(jobName: string): Promise<void>`
33
+ - `SchedulerHandlerRegistry`:
34
+ - `register(key: string, handler: (config?: any) => Promise<void>): void`
35
+ - `SchedulerJobDefinition`:
36
+ - `name: string`
37
+ - `handlerKey: string`
38
+ - `schedule: SchedulerScheduleConfig`
39
+ - `config?: any` (JSON-serializable config injected into handler)
40
+ - `SchedulerScheduleConfig`:
41
+ - `scheduleType: "cron"`
42
+ - `expression: string`
43
+ - `timezone?: string` (e.g. `"UTC"`, `"Asia/Saigon"`)
44
+ - `overlapPolicy: "allow" | "skip" | "enqueue"`
45
+ - `missedRunPolicy: "run-immediately" | "skip"`
46
+
47
+ ### Basic Usage
24
48
 
25
49
  ```ts
26
50
  import { createQueueService } from "@azlib/queue";
27
- import { createProviderFromEnv } from "@azlib/queue/providers";
28
- import {
29
- createCronExpression,
30
- createSchedulerService,
31
- } from "@azlib/scheduler";
32
-
33
- const queue = createQueueService({
34
- provider: createProviderFromEnv({ QUEUE_PROVIDER: "memory" }),
35
- defaultQueue: "scheduled-jobs",
36
- retryPolicy: {
37
- maxAttempts: 5,
38
- backoffType: "exponential-jitter",
39
- baseDelayMs: 1000,
40
- maxDelayMs: 60000,
41
- },
42
- });
51
+ import { createSchedulerService, createCronExpression } from "@azlib/scheduler";
52
+
53
+ const queue = createQueueService({ /* ... */ });
43
54
 
44
55
  const { service, handlers } = createSchedulerService({
45
- mode: "standalone",
46
- queueService: queue,
56
+ mode: "standalone",
57
+ queueService: queue,
47
58
  });
48
59
 
49
- handlers.register("daily-report", async (config: { template: string }) => {
50
- await sendReport(config.template);
60
+ // 1. Register executor logic
61
+ handlers.register("purge-logs", async (config: { thresholdDays: number }) => {
62
+ await db.logs.deleteOlderThan(config.thresholdDays);
51
63
  });
52
64
 
65
+ // 2. Register scheduled trigger
53
66
  await service.registerJob({
54
- name: "report-job",
55
- handlerKey: "daily-report",
56
- schedule: {
57
- scheduleType: "cron",
58
- expression: createCronExpression().dailyAt(8, 0).build(),
59
- timezone: "UTC",
60
- overlapPolicy: "skip",
61
- missedRunPolicy: "run-immediately",
62
- },
63
- config: { template: "daily-summary" },
67
+ name: "nightly-purge",
68
+ handlerKey: "purge-logs",
69
+ schedule: {
70
+ scheduleType: "cron",
71
+ expression: createCronExpression().dailyAt(2, 30).build(), // 02:30 AM
72
+ timezone: "UTC",
73
+ overlapPolicy: "skip",
74
+ missedRunPolicy: "run-immediately",
75
+ },
76
+ config: { thresholdDays: 30 },
64
77
  });
65
78
 
79
+ // 3. Boot scheduler
66
80
  await service.start();
67
81
  ```
68
82
 
69
- ## Cron Builder
70
-
71
- Use the fluent builder to avoid writing raw cron syntax directly:
72
-
73
- ```ts
74
- import { CronWeekday, createCronExpression } from "@azlib/scheduler";
75
-
76
- const everyFiveMinutes = createCronExpression().everyNMinutes(5).build();
77
- const everyTwoHours = createCronExpression().atMinute(0).everyNHours(2).build();
78
- const weekdayMorning = createCronExpression()
79
- .weeklyOn(CronWeekday.Monday, 9, 0)
80
- .build();
81
- ```
82
-
83
- ## Embedded Runtime Binding
84
-
85
- ```ts
86
- import { bindSchedulerToHost } from "@azlib/scheduler";
87
-
88
- bindSchedulerToHost(service, {
89
- onStart(listener) {
90
- app.listen(3000, () => {
91
- void listener();
92
- });
93
- },
94
- onStop(listener) {
95
- process.on("SIGINT", () => {
96
- void listener();
97
- });
98
- },
99
- });
100
- ```
101
-
102
- ## Validation
103
-
104
- ```bash
105
- pnpm --filter @azlib/scheduler lint
106
- pnpm --filter @azlib/scheduler test
107
- pnpm --filter @azlib/scheduler build
108
- ```
83
+ ### Behavioral Gotchas
84
+ - **Timezone Safety**: Always specify a `timezone` in job configs to prevent local developer clocks from altering trigger cycles.
85
+ - **Overlap Policies**:
86
+ - `skip`: If a previous job execution is still running, the new scheduled trigger is skipped.
87
+ - `enqueue`: Queues the new run to execute immediately after the active run completes.
88
+ - `allow`: Runs execution concurrent to existing instances (warning: potential race conditions).
89
+ - **Execution Engine**: Jobs are not run inside the scheduler thread directly. Instead, they are dispatched as tasks to the configured `@azlib/queue` provider to preserve single-thread event loop safety.