@chidchanun/bcp 0.2.8 → 0.2.10

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,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "framework": "bcp",
4
- "version": "0.2.8",
4
+ "version": "0.2.10",
5
5
  "releaseState": "unreleased",
6
- "baseline": "background-jobs-platform",
6
+ "baseline": "durable-jobs-platform",
7
7
  "runtime": {
8
8
  "node": ">=24.11.0",
9
9
  "react": "19",
@@ -73,6 +73,23 @@
73
73
  "jobRetries": true,
74
74
  "jobWorkerConcurrency": true,
75
75
  "jobCancellation": true,
76
+ "jobSchedulingPlatform": true,
77
+ "recurringJobs": true,
78
+ "cronScheduling": true,
79
+ "intervalScheduling": true,
80
+ "jobScheduleStoreContract": true,
81
+ "schedulerLeases": true,
82
+ "scheduledRunDeduplication": true,
83
+ "durableJobsPlatform": true,
84
+ "redisJobQueueAdapter": true,
85
+ "redisJobScheduleStore": true,
86
+ "jobVisibilityTimeout": true,
87
+ "jobHeartbeats": true,
88
+ "staleJobRecovery": true,
89
+ "deadLetterQueue": true,
90
+ "deadLetterRequeue": true,
91
+ "jobRetentionCleanup": true,
92
+ "jobQueueStatistics": true,
76
93
  "databaseMigrations": true,
77
94
  "databaseAdapterContract": true,
78
95
  "databasePostgresql": true,
@@ -112,7 +129,7 @@
112
129
  "s3-compatible"
113
130
  ],
114
131
  "compatibility": {
115
- "previousBaseline": "0.2.7",
132
+ "previousBaseline": "0.2.9",
116
133
  "intentionalBreakingChangesFromPreviousBaseline": false,
117
134
  "migrationGuide": "migration-0.2.md"
118
135
  },
@@ -130,7 +147,9 @@
130
147
  "authorizationSecurity": "authorization-security.md",
131
148
  "observability": "observability.md",
132
149
  "backgroundJobs": "background-jobs.md",
150
+ "jobScheduling": "job-scheduling.md",
151
+ "durableJobs": "durable-jobs.md",
133
152
  "migrationGuide": "migration-0.2.md",
134
- "releaseNotes": "releases/0.2.8.md"
153
+ "releaseNotes": "releases/0.2.10.md"
135
154
  }
136
155
  }
@@ -0,0 +1,148 @@
1
+ # 0.2.10 — Durable Jobs Platform
2
+
3
+ **State:** unreleased development target
4
+
5
+ BCP Framework `0.2.10` extends the Background Jobs and Job Scheduling platforms with durable worker lifecycle contracts, dead-letter handling, retention/statistics APIs and Redis-compatible reference adapters.
6
+
7
+ ## Highlights
8
+
9
+ ### Durable worker leases
10
+
11
+ Workers can reserve jobs with a visibility timeout and stable worker identity.
12
+
13
+ New job record metadata:
14
+
15
+ ```text
16
+ leaseOwner
17
+ leaseUntil
18
+ heartbeatAt
19
+ recoveredAt
20
+ ```
21
+
22
+ When an adapter supports `heartbeat()`, BCP workers renew the lease while a handler is running.
23
+
24
+ When an adapter supports `recoverStale()`, expired running jobs can be returned to the queue for at-least-once recovery.
25
+
26
+ ### Dead-letter queue
27
+
28
+ Retry-exhausted jobs remain terminal `failed` jobs and can also be indexed in the adapter DLQ.
29
+
30
+ New queue APIs:
31
+
32
+ ```ts
33
+ jobs.deadLetters()
34
+ jobs.requeueDeadLetter()
35
+ ```
36
+
37
+ The built-in memory adapter and Redis-compatible adapter implement both APIs.
38
+
39
+ ### Queue maintenance
40
+
41
+ New operational APIs:
42
+
43
+ ```ts
44
+ jobs.stats()
45
+ jobs.cleanup()
46
+ jobs.recoverStale()
47
+ ```
48
+
49
+ Statistics include queue state totals and DLQ depth.
50
+
51
+ Cleanup can remove old succeeded, failed and cancelled records after an application-defined retention period.
52
+
53
+ ### Redis-compatible queue adapter
54
+
55
+ New public API:
56
+
57
+ ```ts
58
+ createRedisJobQueueAdapter()
59
+ ```
60
+
61
+ The adapter consumes a minimal application-owned Redis command client exposing:
62
+
63
+ ```ts
64
+ sendCommand(command: string[]): Promise<unknown>
65
+ ```
66
+
67
+ BCP does not add a Redis client runtime dependency.
68
+
69
+ The Redis adapter uses atomic Lua operations for:
70
+
71
+ - duplicate-safe enqueue,
72
+ - job reservation,
73
+ - complete/fail/cancel transitions,
74
+ - heartbeat lease renewal,
75
+ - stale-job recovery,
76
+ - DLQ requeue,
77
+ - retention cleanup.
78
+
79
+ The default Redis namespace is `bcp:{jobs}` so all adapter keys share one Redis Cluster hash slot.
80
+
81
+ ### Redis-compatible scheduler store
82
+
83
+ New public API:
84
+
85
+ ```ts
86
+ createRedisJobScheduleStore()
87
+ ```
88
+
89
+ The scheduler store implements the `0.2.9` `JobScheduleStore` contract on shared Redis state and uses atomic schedule leasing for multi-instance schedulers.
90
+
91
+ ### Compiled npm runtime
92
+
93
+ The published `bcp/jobs` default runtime continues to use generated `jobs.mjs` rather than requiring consumers to execute framework TypeScript directly.
94
+
95
+ The Durable Jobs package smoke imports that compiled runtime and verifies the new public APIs.
96
+
97
+ ## Public API additions
98
+
99
+ `bcp/jobs` adds:
100
+
101
+ ```text
102
+ createRedisJobQueueAdapter
103
+ createRedisJobScheduleStore
104
+ RedisCommandClient
105
+ RedisJobQueueAdapter
106
+ RedisJobScheduleStore
107
+ RedisJobsAdapterOptions
108
+
109
+ CleanupJobsOptions
110
+ DeadLetterJobRecord
111
+ HeartbeatJobOptions
112
+ JobQueueStats
113
+ ProcessNextJobOptions
114
+ RecoverStaleJobsOptions
115
+ RequeueDeadLetterOptions
116
+ ReserveJobOptions
117
+ ```
118
+
119
+ Existing queue and scheduling APIs remain available.
120
+
121
+ ## Compatibility
122
+
123
+ The release is intended to be backward compatible with `0.2.9`.
124
+
125
+ Existing `JobQueueAdapter` implementations remain valid because durable capabilities are optional and the new reservation metadata is provided as an optional second parameter to `reserve()`.
126
+
127
+ Adapters that need production visibility-timeout semantics should implement the new durable methods.
128
+
129
+ ## Delivery semantics
130
+
131
+ Durable jobs use at-least-once processing semantics.
132
+
133
+ Applications should make side-effecting handlers idempotent when duplicate execution would be unsafe.
134
+
135
+ ## Validation target
136
+
137
+ Before tagging/publishing `0.2.10`, run:
138
+
139
+ ```bash
140
+ npm run typecheck
141
+ npm run test:unit
142
+ npm run test:integration
143
+ npm run test:e2e
144
+ npm run test:package
145
+ npm run rc:check
146
+ ```
147
+
148
+ The final release tag must point to the exact commit that passed the complete RC sequence.
@@ -0,0 +1,162 @@
1
+ # BCP Framework 0.2.9 — Job Scheduling Platform
2
+
3
+ > Release state: unreleased until the complete RC workflow passes, the release commit is tagged `v0.2.9`, and npm publication completes.
4
+
5
+ BCP Framework `0.2.9` extends the `0.2.8` Background Jobs Platform with recurring schedules, dependency-free UTC cron expressions and a lease-aware schedule-store contract.
6
+
7
+ ## Highlights
8
+
9
+ - Added `createJobScheduler()` through `bcp/jobs`.
10
+ - Added interval-based recurring jobs with `everyMs`.
11
+ - Added five-field UTC cron expressions.
12
+ - Added `nextCronTime()` and `nextScheduleTime()` helpers.
13
+ - Added `JobScheduleStore` for durable/shared scheduler state.
14
+ - Added `createMemoryJobScheduleStore()` for local development and tests.
15
+ - Added atomic-style `acquireDue()` lease semantics to the schedule-store contract.
16
+ - Added deterministic scheduled queue ids to reduce duplicate enqueue risk.
17
+ - Added scheduler polling lifecycle with `start()`, `stop()` and `close()`.
18
+ - Added schedule inspection, removal and stable-id upsert behavior.
19
+ - Added unit, package-smoke and platform-contract coverage.
20
+
21
+ ## Scheduling API
22
+
23
+ ```ts
24
+ import {
25
+ createJobQueue,
26
+ createJobScheduler,
27
+ } from "bcp/jobs";
28
+
29
+ const jobs =
30
+ createJobQueue();
31
+
32
+ const scheduler =
33
+ createJobScheduler({
34
+ queue: jobs,
35
+ });
36
+ ```
37
+
38
+ Interval:
39
+
40
+ ```ts
41
+ await scheduler.schedule(
42
+ "cache.cleanup",
43
+ {},
44
+ {
45
+ id: "cache-cleanup",
46
+ everyMs: 300_000,
47
+ }
48
+ );
49
+ ```
50
+
51
+ Cron:
52
+
53
+ ```ts
54
+ await scheduler.schedule(
55
+ "report.weekday",
56
+ {},
57
+ {
58
+ cron: "30 9 * * 1-5",
59
+ }
60
+ );
61
+ ```
62
+
63
+ Cron evaluation is UTC in `0.2.9`.
64
+
65
+ ## Cron syntax
66
+
67
+ Five fields are supported:
68
+
69
+ ```text
70
+ minute hour day-of-month month day-of-week
71
+ ```
72
+
73
+ Supported tokens:
74
+
75
+ ```text
76
+ *
77
+ */n
78
+ comma-separated values
79
+ ranges
80
+ range steps
81
+ ```
82
+
83
+ Day of week accepts `0` and `7` for Sunday. When both day-of-month and day-of-week are restricted, standard cron OR semantics are used.
84
+
85
+ ## Scheduler lifecycle
86
+
87
+ Long-running scheduler:
88
+
89
+ ```ts
90
+ const runner =
91
+ scheduler.start({
92
+ pollIntervalMs: 1_000,
93
+ leaseMs: 30_000,
94
+ });
95
+
96
+ // shutdown
97
+ await runner.stop();
98
+ await scheduler.close();
99
+ ```
100
+
101
+ `runDue()` is also available for tests, external control loops and deterministic execution.
102
+
103
+ ## Multi-instance contract
104
+
105
+ `JobScheduleStore.acquireDue()` is the scheduler concurrency boundary. Durable implementations should atomically claim due schedules with a lease owner and expiry.
106
+
107
+ The default memory store is process-local and is not suitable for schedules that must survive restarts or coordinate across multiple containers.
108
+
109
+ Production multi-instance systems will normally pair:
110
+
111
+ ```text
112
+ shared JobScheduleStore
113
+ +
114
+ shared JobQueueAdapter
115
+ ```
116
+
117
+ `0.2.9` intentionally leaves provider choice to the application instead of forcing Redis, PostgreSQL or another queue/scheduler backend.
118
+
119
+ ## Duplicate protection
120
+
121
+ Each scheduled occurrence uses a deterministic queue id:
122
+
123
+ ```text
124
+ schedule:<schedule-id>:<scheduled-for-timestamp>
125
+ ```
126
+
127
+ This complements store leasing and protects the built-in queue from enqueuing the same occurrence twice if the same schedule occurrence is retried.
128
+
129
+ ## Compatibility
130
+
131
+ This milestone is additive. There are no intentional breaking changes from `0.2.8`.
132
+
133
+ Existing `createJobQueue()`, handlers, delayed jobs, retries, workers and adapters continue to work without scheduling enabled.
134
+
135
+ ## Documentation
136
+
137
+ New guide:
138
+
139
+ ```text
140
+ docs/job-scheduling.md
141
+ ```
142
+
143
+ Background jobs remain documented in:
144
+
145
+ ```text
146
+ docs/background-jobs.md
147
+ ```
148
+
149
+ ## Validation
150
+
151
+ Before tagging/publishing `0.2.9`, run:
152
+
153
+ ```bash
154
+ npm run typecheck
155
+ npm run test:unit
156
+ npm run test:integration
157
+ npm run test:e2e
158
+ npm run test:package
159
+ npm run rc:check
160
+ ```
161
+
162
+ The release tag must point to the exact commit that passed the complete RC sequence.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chidchanun/bcp",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "BCP Framework - a React full-stack framework with file-based routing, SSR, APIs, middleware, islands, caching and standalone production builds.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -65,7 +65,7 @@
65
65
  "./jobs": {
66
66
  "types": "./packages/client/src/jobs.ts",
67
67
  "browser": "./packages/client/src/server-only.browser.mjs",
68
- "default": "./packages/client/src/jobs.ts"
68
+ "default": "./packages/client/src/jobs.mjs"
69
69
  },
70
70
  "./observability": {
71
71
  "types": "./packages/client/src/observability.ts",