@warlock.js/scheduler 5.23.2 → 5.25.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/CHANGELOG.md CHANGED
@@ -1,9 +1,21 @@
1
- # Changelog — @warlock.js/scheduler
2
-
3
- All notable changes to `@warlock.js/scheduler` are documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
-
1
+ # Changelog — @warlock.js/scheduler
2
+
3
+ All notable changes to `@warlock.js/scheduler` are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
+
7
+ ## 5.25.0 - 2026-09-28
8
+
9
+ ### Changed
10
+
11
+ - `scheduler.start()` is now a no-op, logging `scheduler: not started (role: ...)`, on a process that doesn't serve the `worker` role (`WARLOCK_ROLES`) — so an app calling it in `main.ts` doesn't run its jobs on every `web`/`api` node. The "jobs registered but `start()` never called" development warning no longer fires on such a process. With no `--role` passed, every role is on and nothing changes.
12
+
13
+ ## 5.24.0 - 2026-09-27
14
+
15
+ ### Changed
16
+
17
+ - Lockstep release; package APIs are unchanged.
18
+
7
19
  ## 5.23.2 - 2026-09-26
8
20
 
9
21
  ### Changed
@@ -16,110 +28,110 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
16
28
 
17
29
  - Lockstep patch release; package APIs are unchanged.
18
30
 
19
- ## 5.23.0 - 2026-09-25
20
-
21
- ### Added
22
-
23
- - `scheduler.around((job, run) => …)` wraps every job callback execution, each retry attempt included, so jobs can run inside a context such as `AsyncLocalStorage`. Multiple hooks compose with the first registered outermost; the call returns an unsubscribe function. A hook that never calls `run` skips the execution and emits `job:skip` (`JobResult.skipped` / `skipReason`); a hook that throws follows the normal retry/error path.
24
-
25
- ## 5.22.1 - 2026-09-25
26
-
27
- ### Changed
28
-
29
- - Lockstep patch release; package APIs are unchanged.
30
-
31
- ## 5.22.0 - 2026-09-25
32
-
33
- ### Fixed
34
-
35
- - Cron and time parsing handle absent fields explicitly under strict TypeScript checks.
36
-
37
- ## 5.21.0 - 2026-09-25
38
-
39
- ### Changed
40
-
41
- - Lockstep release maintenance and dependency refresh.
42
- ## 5.20.0 - 2026-09-24
43
-
44
- ### Added
45
-
46
- - `job.onOneServer({ lockTtl?, key? })` — run each tick on exactly one server. Every server races for a create-only, TTL-bounded cache claim keyed `scheduler.<key ?? name>.<scheduledTickEpochMs>`. The claim is never released, so exactly one server runs each tick even if another server's timer fires late. Losers skip the tick and emit `job:skip`. Requires a shared cache driver (redis/pg) and a job name or `key`. `@warlock.js/cache` is an optional peer dependency. Default claim TTL is `min(interval, 1h)` with a 60s floor (1h for cron jobs).
47
-
48
- ## 5.19.0 - 2026-09-23
49
-
50
- ### Changed
51
-
52
- - Refined package skill-discovery descriptions and regenerated the llms projections.
53
-
54
- ## 5.11.0 - 2026-09-14
55
-
56
- _Released in lockstep with the `@warlock.js/*` family; no package-specific changes in 5.11.0._
57
-
58
- ## 5.10.0 - 2026-09-14
59
-
60
- _Released in lockstep with the `@warlock.js/*` family; no package-specific changes in 5.10.0._
61
-
62
- ## 5.9.0 - 2026-09-13
63
-
64
- _Released in lockstep with the `@warlock.js/*` family; no package-specific changes in 5.9.0._
65
-
66
- ## 5.5.0 - 2026-09-07
67
-
68
- ### Fixed
69
-
70
- - Documentation shipped in this package's `skills/` told users to run `pnpm`-specific commands. `pnpm <binary>` has no npm equivalent, so those instructions failed outright for anyone not using pnpm. Commands are now package-manager neutral.
71
-
72
- ## 5.2.3 - 2026-09-02
73
-
74
- ### Fixed
75
-
76
- - Released in exact lockstep with Core's Web generator repairs so every family dependency remains installable at 5.2.3.
77
-
78
- ## 5.2.2
79
-
80
- - Documented: the README, CHANGELOG, and `overview` skill now describe the
81
- already-shipped `package.json` `"warlock": { "environment": "server" }`
82
- marker — `@warlock.js/scheduler`'s entire runtime surface is server-only.
83
- The marker is build-boundary metadata read by `@warlock.js/web`'s Gate A
84
- (import resolution) and Gate C (emitted-bundle verification); app client
85
- code must not value-import this package, type-only imports are allowed, and
86
- server loaders/controllers/modules may import it freely. No source change.
87
-
88
- ## 5.1.0
89
-
90
- No changes to `@warlock.js/scheduler`. Released in lockstep with the `@warlock.js/web`
91
- React-execution fix and the `@warlock.js/core` CLI additions — see those packages'
92
- changelogs.
93
-
94
- ## 5.0.2 - 2026-08-25
95
-
96
- No changes to `@warlock.js/scheduler`. Released in lockstep with the `@warlock.js/web` SSR
97
- fix (`ssr.noExternal`) — see that package's changelog.
98
-
99
- ## 5.0.1 - 2026-08-25
100
-
101
- No changes to `@warlock.js/scheduler`. Released in lockstep with the `create-warlock` vite
102
- resolution pin and the `@warlock.js/web` peer narrowing — see those packages'
103
- changelogs.
104
-
105
- ## 5.0.0 - 2026-08-25
106
-
107
- ### Changed
108
-
109
- - This package is unchanged in 5.0.0; its version moved only because the Warlock family releases in lockstep.
110
-
111
- ## 4.12.0
112
-
113
- ### Changed
114
-
115
- - Declares its own test runner and pins it to an exact version (`vitest@4.1.10`). The package is its own repository, so a runner resolved from a workspace root it may not be cloned with is a runner it cannot rely on. The pin is exact rather than a range because the version moved underneath the suite mid-development on an unrelated install — a suite whose runner can change without anyone choosing it proves less than it appears to
116
-
117
- ## 4.6.0
118
-
119
- ### Fixed
120
-
121
- - Warn in development when jobs are registered but `start()` is never called — a one-shot deferred check logs `N job(s) registered but scheduler.start() was never called`, is suppressed once `start()` runs or in production (`NODE_ENV=production`), and is unref'd so it never holds the process open.
122
-
123
- ## 4.1.15
124
-
125
- - Baseline — per-package changelog tracking starts at this version.
31
+ ## 5.23.0 - 2026-09-25
32
+
33
+ ### Added
34
+
35
+ - `scheduler.around((job, run) => …)` wraps every job callback execution, each retry attempt included, so jobs can run inside a context such as `AsyncLocalStorage`. Multiple hooks compose with the first registered outermost; the call returns an unsubscribe function. A hook that never calls `run` skips the execution and emits `job:skip` (`JobResult.skipped` / `skipReason`); a hook that throws follows the normal retry/error path.
36
+
37
+ ## 5.22.1 - 2026-09-25
38
+
39
+ ### Changed
40
+
41
+ - Lockstep patch release; package APIs are unchanged.
42
+
43
+ ## 5.22.0 - 2026-09-25
44
+
45
+ ### Fixed
46
+
47
+ - Cron and time parsing handle absent fields explicitly under strict TypeScript checks.
48
+
49
+ ## 5.21.0 - 2026-09-25
50
+
51
+ ### Changed
52
+
53
+ - Lockstep release maintenance and dependency refresh.
54
+ ## 5.20.0 - 2026-09-24
55
+
56
+ ### Added
57
+
58
+ - `job.onOneServer({ lockTtl?, key? })` — run each tick on exactly one server. Every server races for a create-only, TTL-bounded cache claim keyed `scheduler.<key ?? name>.<scheduledTickEpochMs>`. The claim is never released, so exactly one server runs each tick even if another server's timer fires late. Losers skip the tick and emit `job:skip`. Requires a shared cache driver (redis/pg) and a job name or `key`. `@warlock.js/cache` is an optional peer dependency. Default claim TTL is `min(interval, 1h)` with a 60s floor (1h for cron jobs).
59
+
60
+ ## 5.19.0 - 2026-09-23
61
+
62
+ ### Changed
63
+
64
+ - Refined package skill-discovery descriptions and regenerated the llms projections.
65
+
66
+ ## 5.11.0 - 2026-09-14
67
+
68
+ _Released in lockstep with the `@warlock.js/*` family; no package-specific changes in 5.11.0._
69
+
70
+ ## 5.10.0 - 2026-09-14
71
+
72
+ _Released in lockstep with the `@warlock.js/*` family; no package-specific changes in 5.10.0._
73
+
74
+ ## 5.9.0 - 2026-09-13
75
+
76
+ _Released in lockstep with the `@warlock.js/*` family; no package-specific changes in 5.9.0._
77
+
78
+ ## 5.5.0 - 2026-09-07
79
+
80
+ ### Fixed
81
+
82
+ - Documentation shipped in this package's `skills/` told users to run `pnpm`-specific commands. `pnpm <binary>` has no npm equivalent, so those instructions failed outright for anyone not using pnpm. Commands are now package-manager neutral.
83
+
84
+ ## 5.2.3 - 2026-09-02
85
+
86
+ ### Fixed
87
+
88
+ - Released in exact lockstep with Core's Web generator repairs so every family dependency remains installable at 5.2.3.
89
+
90
+ ## 5.2.2
91
+
92
+ - Documented: the README, CHANGELOG, and `overview` skill now describe the
93
+ already-shipped `package.json` `"warlock": { "environment": "server" }`
94
+ marker — `@warlock.js/scheduler`'s entire runtime surface is server-only.
95
+ The marker is build-boundary metadata read by `@warlock.js/web`'s Gate A
96
+ (import resolution) and Gate C (emitted-bundle verification); app client
97
+ code must not value-import this package, type-only imports are allowed, and
98
+ server loaders/controllers/modules may import it freely. No source change.
99
+
100
+ ## 5.1.0
101
+
102
+ No changes to `@warlock.js/scheduler`. Released in lockstep with the `@warlock.js/web`
103
+ React-execution fix and the `@warlock.js/core` CLI additions — see those packages'
104
+ changelogs.
105
+
106
+ ## 5.0.2 - 2026-08-25
107
+
108
+ No changes to `@warlock.js/scheduler`. Released in lockstep with the `@warlock.js/web` SSR
109
+ fix (`ssr.noExternal`) — see that package's changelog.
110
+
111
+ ## 5.0.1 - 2026-08-25
112
+
113
+ No changes to `@warlock.js/scheduler`. Released in lockstep with the `create-warlock` vite
114
+ resolution pin and the `@warlock.js/web` peer narrowing — see those packages'
115
+ changelogs.
116
+
117
+ ## 5.0.0 - 2026-08-25
118
+
119
+ ### Changed
120
+
121
+ - This package is unchanged in 5.0.0; its version moved only because the Warlock family releases in lockstep.
122
+
123
+ ## 4.12.0
124
+
125
+ ### Changed
126
+
127
+ - Declares its own test runner and pins it to an exact version (`vitest@4.1.10`). The package is its own repository, so a runner resolved from a workspace root it may not be cloned with is a runner it cannot rely on. The pin is exact rather than a range because the version moved underneath the suite mid-development on an unrelated install — a suite whose runner can change without anyone choosing it proves less than it appears to
128
+
129
+ ## 4.6.0
130
+
131
+ ### Fixed
132
+
133
+ - Warn in development when jobs are registered but `start()` is never called — a one-shot deferred check logs `N job(s) registered but scheduler.start() was never called`, is suppressed once `start()` runs or in production (`NODE_ENV=production`), and is unref'd so it never holds the process open.
134
+
135
+ ## 4.1.15
136
+
137
+ - Baseline — per-package changelog tracking starts at this version.
package/cjs/index.cjs CHANGED
@@ -993,6 +993,41 @@ function job(name, callback) {
993
993
  return new Job(name, callback);
994
994
  }
995
995
 
996
+ //#endregion
997
+ //#region ../scheduler/src/process-roles.ts
998
+ /**
999
+ * `WARLOCK_ROLES`, parsed for the scheduler's own use.
1000
+ *
1001
+ * The scheduler has no dependency on `@warlock.js/core` (see package.json),
1002
+ * so it cannot call `Application.hasRole`. Core's `parseRoles` already
1003
+ * validates the raw value at process boot — an unknown role or an explicit
1004
+ * empty list throws there — so this only needs to decide membership, not
1005
+ * re-validate.
1006
+ */
1007
+ function parsedRoles() {
1008
+ const value = process.env.WARLOCK_ROLES;
1009
+ if (!value) return [];
1010
+ return value.split(",").map((role) => role.trim().toLowerCase()).filter((role) => role.length > 0);
1011
+ }
1012
+ /**
1013
+ * Whether this process serves the `worker` role.
1014
+ *
1015
+ * `true` when `WARLOCK_ROLES` is unset, matching core's default of "every
1016
+ * role is on" for plain `start`, `dev`, and tests.
1017
+ */
1018
+ function hasWorkerRole() {
1019
+ const roles = parsedRoles();
1020
+ return roles.length === 0 || roles.includes("worker");
1021
+ }
1022
+ /**
1023
+ * The roles this process serves, formatted for a log line — `"all"` when
1024
+ * `WARLOCK_ROLES` is unset.
1025
+ */
1026
+ function rolesLabel() {
1027
+ const roles = parsedRoles();
1028
+ return roles.length === 0 ? "all" : roles.join(",");
1029
+ }
1030
+
996
1031
  //#endregion
997
1032
  //#region ../scheduler/src/scheduler.ts
998
1033
  /**
@@ -1161,9 +1196,18 @@ var Scheduler = class extends node_events.EventEmitter {
1161
1196
  /**
1162
1197
  * Start the scheduler
1163
1198
  *
1199
+ * A no-op, logging one info line, on a process that does not serve the
1200
+ * `worker` role (`WARLOCK_ROLES`) — so an app that calls this in `main.ts`
1201
+ * doesn't run its jobs on every `web`/`api` node. With no `--role` passed,
1202
+ * every role is on and this behaves exactly as before.
1203
+ *
1164
1204
  * @throws Error if scheduler is already running
1165
1205
  */
1166
1206
  start() {
1207
+ if (!hasWorkerRole()) {
1208
+ console.info(`scheduler: not started (role: ${rolesLabel()})`);
1209
+ return;
1210
+ }
1167
1211
  if (this.isRunning) throw new Error("Scheduler is already running.");
1168
1212
  if (this._jobs.length === 0) throw new Error("Cannot start scheduler with no jobs.");
1169
1213
  this._hasStarted = true;
@@ -1203,12 +1247,14 @@ var Scheduler = class extends node_events.EventEmitter {
1203
1247
  * Arm the one-shot "registered but never started" development warning.
1204
1248
  *
1205
1249
  * Called whenever a job is registered. Does nothing in production
1206
- * (`NODE_ENV === "production"`), once `start()` has been called, or when a
1207
- * check is already armed. The deferred check is unref'd so it can never keep
1250
+ * (`NODE_ENV === "production"`), once `start()` has been called, when a
1251
+ * check is already armed, or when this process doesn't serve the `worker`
1252
+ * role — `start()` is never expected to be called there, so the warning
1253
+ * would only be noise. The deferred check is unref'd so it can never keep
1208
1254
  * the event loop (and the process) alive on its own.
1209
1255
  */
1210
1256
  _armStartWarning() {
1211
- if (process.env.NODE_ENV === "production") return;
1257
+ if (process.env.NODE_ENV === "production" || !hasWorkerRole()) return;
1212
1258
  if (this._hasStarted || this._startWarningTimer !== null) return;
1213
1259
  const timer = setTimeout(() => {
1214
1260
  this._startWarningTimer = null;