@morojs/moro 1.6.5 → 1.6.8

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.
Files changed (200) hide show
  1. package/README.md +20 -4
  2. package/dist/core/auth/morojs-adapter.js +17 -14
  3. package/dist/core/auth/morojs-adapter.js.map +1 -1
  4. package/dist/core/config/config-sources.js +44 -0
  5. package/dist/core/config/config-sources.js.map +1 -1
  6. package/dist/core/database/adapters/drizzle.js +5 -5
  7. package/dist/core/database/adapters/drizzle.js.map +1 -1
  8. package/dist/core/database/adapters/mongodb.js +5 -1
  9. package/dist/core/database/adapters/mongodb.js.map +1 -1
  10. package/dist/core/database/adapters/mysql.js +5 -1
  11. package/dist/core/database/adapters/mysql.js.map +1 -1
  12. package/dist/core/database/adapters/postgresql.js +1 -1
  13. package/dist/core/database/adapters/postgresql.js.map +1 -1
  14. package/dist/core/database/adapters/redis.js +2 -2
  15. package/dist/core/database/adapters/redis.js.map +1 -1
  16. package/dist/core/database/adapters/sqlite.js +5 -1
  17. package/dist/core/database/adapters/sqlite.js.map +1 -1
  18. package/dist/core/docs/index.js.map +1 -1
  19. package/dist/core/docs/simple-docs.js +2 -1
  20. package/dist/core/docs/simple-docs.js.map +1 -1
  21. package/dist/core/docs/swagger-ui.js +1 -0
  22. package/dist/core/docs/swagger-ui.js.map +1 -1
  23. package/dist/core/docs/zod-to-openapi.js +4 -0
  24. package/dist/core/docs/zod-to-openapi.js.map +1 -1
  25. package/dist/core/events/event-bus.d.ts +1 -1
  26. package/dist/core/events/event-bus.js +8 -4
  27. package/dist/core/events/event-bus.js.map +1 -1
  28. package/dist/core/framework.d.ts +1 -1
  29. package/dist/core/framework.js +3 -1
  30. package/dist/core/framework.js.map +1 -1
  31. package/dist/core/graphql/adapter.d.ts +73 -0
  32. package/dist/core/graphql/adapter.js +2 -0
  33. package/dist/core/graphql/adapter.js.map +1 -0
  34. package/dist/core/graphql/adapters/graphql-js-adapter.d.ts +26 -0
  35. package/dist/core/graphql/adapters/graphql-js-adapter.js +229 -0
  36. package/dist/core/graphql/adapters/graphql-js-adapter.js.map +1 -0
  37. package/dist/core/graphql/core.d.ts +60 -0
  38. package/dist/core/graphql/core.js +165 -0
  39. package/dist/core/graphql/core.js.map +1 -0
  40. package/dist/core/graphql/index.d.ts +4 -0
  41. package/dist/core/graphql/index.js +4 -0
  42. package/dist/core/graphql/index.js.map +1 -0
  43. package/dist/core/graphql/loader.d.ts +9 -0
  44. package/dist/core/graphql/loader.js +32 -0
  45. package/dist/core/graphql/loader.js.map +1 -0
  46. package/dist/core/graphql/types.d.ts +211 -0
  47. package/dist/core/graphql/types.js +2 -0
  48. package/dist/core/graphql/types.js.map +1 -0
  49. package/dist/core/http/http-server.d.ts +7 -0
  50. package/dist/core/http/http-server.js +267 -123
  51. package/dist/core/http/http-server.js.map +1 -1
  52. package/dist/core/http/utils/uws-worker-clustering.d.ts +28 -0
  53. package/dist/core/http/utils/uws-worker-clustering.js +313 -0
  54. package/dist/core/http/utils/uws-worker-clustering.js.map +1 -0
  55. package/dist/core/http/uws-http-server.d.ts +7 -1
  56. package/dist/core/http/uws-http-server.js +272 -189
  57. package/dist/core/http/uws-http-server.js.map +1 -1
  58. package/dist/core/jobs/cron-parser.d.ts +62 -0
  59. package/dist/core/jobs/cron-parser.js +239 -0
  60. package/dist/core/jobs/cron-parser.js.map +1 -0
  61. package/dist/core/jobs/index.d.ts +12 -0
  62. package/dist/core/jobs/index.js +9 -0
  63. package/dist/core/jobs/index.js.map +1 -0
  64. package/dist/core/jobs/job-executor.d.ts +134 -0
  65. package/dist/core/jobs/job-executor.js +413 -0
  66. package/dist/core/jobs/job-executor.js.map +1 -0
  67. package/dist/core/jobs/job-scheduler.d.ts +214 -0
  68. package/dist/core/jobs/job-scheduler.js +551 -0
  69. package/dist/core/jobs/job-scheduler.js.map +1 -0
  70. package/dist/core/jobs/job-state-manager.d.ts +158 -0
  71. package/dist/core/jobs/job-state-manager.js +444 -0
  72. package/dist/core/jobs/job-state-manager.js.map +1 -0
  73. package/dist/core/jobs/leader-election.d.ts +124 -0
  74. package/dist/core/jobs/leader-election.js +481 -0
  75. package/dist/core/jobs/leader-election.js.map +1 -0
  76. package/dist/core/jobs/types.d.ts +151 -0
  77. package/dist/core/jobs/types.js +4 -0
  78. package/dist/core/jobs/types.js.map +1 -0
  79. package/dist/core/jobs/utils.d.ts +95 -0
  80. package/dist/core/jobs/utils.js +258 -0
  81. package/dist/core/jobs/utils.js.map +1 -0
  82. package/dist/core/logger/filters.js +2 -0
  83. package/dist/core/logger/filters.js.map +1 -1
  84. package/dist/core/logger/logger.d.ts +7 -5
  85. package/dist/core/logger/logger.js +68 -27
  86. package/dist/core/logger/logger.js.map +1 -1
  87. package/dist/core/logger/outputs.js +2 -0
  88. package/dist/core/logger/outputs.js.map +1 -1
  89. package/dist/core/middleware/built-in/auth/helpers.js +1 -1
  90. package/dist/core/middleware/built-in/auth/helpers.js.map +1 -1
  91. package/dist/core/middleware/built-in/auth/jwt-helpers.js +1 -1
  92. package/dist/core/middleware/built-in/auth/jwt-helpers.js.map +1 -1
  93. package/dist/core/middleware/built-in/auth/providers.js +1 -1
  94. package/dist/core/middleware/built-in/auth/providers.js.map +1 -1
  95. package/dist/core/middleware/built-in/cache/adapters/cache/file.js +3 -3
  96. package/dist/core/middleware/built-in/cache/adapters/cache/file.js.map +1 -1
  97. package/dist/core/middleware/built-in/cache/adapters/cache/memory.js +1 -0
  98. package/dist/core/middleware/built-in/cache/adapters/cache/memory.js.map +1 -1
  99. package/dist/core/middleware/built-in/cache/adapters/cache/redis.js +1 -1
  100. package/dist/core/middleware/built-in/cache/adapters/cache/redis.js.map +1 -1
  101. package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.d.ts +8 -0
  102. package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js +100 -7
  103. package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js.map +1 -1
  104. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.d.ts +6 -0
  105. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js +97 -13
  106. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js.map +1 -1
  107. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js +1 -1
  108. package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js.map +1 -1
  109. package/dist/core/middleware/built-in/cookie/hook.d.ts +1 -1
  110. package/dist/core/middleware/built-in/cookie/hook.js +2 -2
  111. package/dist/core/middleware/built-in/cookie/hook.js.map +1 -1
  112. package/dist/core/middleware/built-in/csrf/core.js +1 -0
  113. package/dist/core/middleware/built-in/csrf/core.js.map +1 -1
  114. package/dist/core/middleware/built-in/graphql/core.d.ts +11 -0
  115. package/dist/core/middleware/built-in/graphql/core.js +24 -0
  116. package/dist/core/middleware/built-in/graphql/core.js.map +1 -0
  117. package/dist/core/middleware/built-in/graphql/helpers.d.ts +69 -0
  118. package/dist/core/middleware/built-in/graphql/helpers.js +187 -0
  119. package/dist/core/middleware/built-in/graphql/helpers.js.map +1 -0
  120. package/dist/core/middleware/built-in/graphql/hook.d.ts +7 -0
  121. package/dist/core/middleware/built-in/graphql/hook.js +78 -0
  122. package/dist/core/middleware/built-in/graphql/hook.js.map +1 -0
  123. package/dist/core/middleware/built-in/graphql/index.d.ts +5 -0
  124. package/dist/core/middleware/built-in/graphql/index.js +5 -0
  125. package/dist/core/middleware/built-in/graphql/index.js.map +1 -0
  126. package/dist/core/middleware/built-in/graphql/middleware.d.ts +7 -0
  127. package/dist/core/middleware/built-in/graphql/middleware.js +54 -0
  128. package/dist/core/middleware/built-in/graphql/middleware.js.map +1 -0
  129. package/dist/core/middleware/built-in/graphql/subscriptions.d.ts +20 -0
  130. package/dist/core/middleware/built-in/graphql/subscriptions.js +37 -0
  131. package/dist/core/middleware/built-in/graphql/subscriptions.js.map +1 -0
  132. package/dist/core/middleware/built-in/index.d.ts +2 -1
  133. package/dist/core/middleware/built-in/index.js +3 -0
  134. package/dist/core/middleware/built-in/index.js.map +1 -1
  135. package/dist/core/middleware/built-in/rate-limit/core.d.ts +5 -0
  136. package/dist/core/middleware/built-in/rate-limit/core.js +16 -8
  137. package/dist/core/middleware/built-in/rate-limit/core.js.map +1 -1
  138. package/dist/core/middleware/built-in/validation/core.js +42 -19
  139. package/dist/core/middleware/built-in/validation/core.js.map +1 -1
  140. package/dist/core/middleware/index.js +1 -0
  141. package/dist/core/middleware/index.js.map +1 -1
  142. package/dist/core/modules/auto-discovery.js +5 -4
  143. package/dist/core/modules/auto-discovery.js.map +1 -1
  144. package/dist/core/modules/modules.js.map +1 -1
  145. package/dist/core/networking/adapters/socketio-adapter.js +1 -1
  146. package/dist/core/networking/adapters/socketio-adapter.js.map +1 -1
  147. package/dist/core/networking/adapters/uws-adapter.js +7 -2
  148. package/dist/core/networking/adapters/uws-adapter.js.map +1 -1
  149. package/dist/core/networking/adapters/ws-adapter.js +5 -2
  150. package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
  151. package/dist/core/networking/websocket-manager.js +2 -0
  152. package/dist/core/networking/websocket-manager.js.map +1 -1
  153. package/dist/core/pooling/object-pool-manager.d.ts +8 -2
  154. package/dist/core/pooling/object-pool-manager.js +38 -18
  155. package/dist/core/pooling/object-pool-manager.js.map +1 -1
  156. package/dist/core/routing/app-integration.d.ts +3 -3
  157. package/dist/core/routing/app-integration.js +1 -1
  158. package/dist/core/routing/app-integration.js.map +1 -1
  159. package/dist/core/routing/index.d.ts +1 -1
  160. package/dist/core/routing/index.js +1 -1
  161. package/dist/core/routing/index.js.map +1 -1
  162. package/dist/core/routing/path-matcher.d.ts +6 -0
  163. package/dist/core/routing/path-matcher.js +46 -7
  164. package/dist/core/routing/path-matcher.js.map +1 -1
  165. package/dist/core/routing/unified-router.d.ts +4 -0
  166. package/dist/core/routing/unified-router.js +104 -43
  167. package/dist/core/routing/unified-router.js.map +1 -1
  168. package/dist/core/runtime/base-adapter.js +3 -3
  169. package/dist/core/runtime/base-adapter.js.map +1 -1
  170. package/dist/core/runtime/cloudflare-workers-adapter.js +1 -1
  171. package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -1
  172. package/dist/core/runtime/node-adapter.d.ts +1 -1
  173. package/dist/core/runtime/node-adapter.js +7 -4
  174. package/dist/core/runtime/node-adapter.js.map +1 -1
  175. package/dist/core/runtime/vercel-edge-adapter.js +1 -0
  176. package/dist/core/runtime/vercel-edge-adapter.js.map +1 -1
  177. package/dist/core/utilities/circuit-breaker.d.ts +9 -2
  178. package/dist/core/utilities/circuit-breaker.js +32 -3
  179. package/dist/core/utilities/circuit-breaker.js.map +1 -1
  180. package/dist/core/utilities/container.js +6 -0
  181. package/dist/core/utilities/container.js.map +1 -1
  182. package/dist/core/utilities/hooks.d.ts +4 -0
  183. package/dist/core/utilities/hooks.js +134 -22
  184. package/dist/core/utilities/hooks.js.map +1 -1
  185. package/dist/core/validation/index.js +6 -1
  186. package/dist/core/validation/index.js.map +1 -1
  187. package/dist/index.d.ts +6 -0
  188. package/dist/index.js +5 -0
  189. package/dist/index.js.map +1 -1
  190. package/dist/moro.d.ts +154 -1
  191. package/dist/moro.js +592 -16
  192. package/dist/moro.js.map +1 -1
  193. package/dist/types/config.d.ts +28 -0
  194. package/dist/types/core.d.ts +1 -0
  195. package/dist/types/events.d.ts +1 -1
  196. package/dist/types/events.js +1 -0
  197. package/dist/types/events.js.map +1 -1
  198. package/dist/types/logger.d.ts +1 -0
  199. package/dist/types/module.d.ts +2 -2
  200. package/package.json +21 -1
@@ -0,0 +1,124 @@
1
+ import { EventEmitter } from 'events';
2
+ import { Logger } from '../../types/logger.js';
3
+ export interface LeaderElectionOptions {
4
+ strategy: 'file' | 'redis' | 'none';
5
+ lockPath?: string;
6
+ lockTimeout?: number;
7
+ heartbeatInterval?: number;
8
+ redisClient?: any;
9
+ forceLeader?: boolean;
10
+ instanceId?: string;
11
+ }
12
+ export interface LeaderInfo {
13
+ instanceId: string;
14
+ hostname: string;
15
+ pid: number;
16
+ electedAt: Date;
17
+ lastHeartbeat: Date;
18
+ metadata?: Record<string, any>;
19
+ }
20
+ /**
21
+ * LeaderElection - Ensures only one job scheduler runs across distributed instances
22
+ * Supports:
23
+ * - File-based locking (for local/NFS environments)
24
+ * - Redis-based locking (for distributed systems)
25
+ * - Kubernetes pod detection
26
+ * - Automatic failover with health checks
27
+ */
28
+ export declare class LeaderElection extends EventEmitter {
29
+ private isLeader;
30
+ private strategy;
31
+ private lockPath?;
32
+ private lockTimeout;
33
+ private heartbeatInterval;
34
+ private heartbeatTimer?;
35
+ private checkTimer?;
36
+ private logger;
37
+ private loggerContext;
38
+ private instanceId;
39
+ private leaderInfo?;
40
+ private redisClient?;
41
+ private isShuttingDown;
42
+ private lockAcquireAttempts;
43
+ constructor(logger: Logger, options?: LeaderElectionOptions);
44
+ /**
45
+ * Start leader election process
46
+ */
47
+ start(): Promise<void>;
48
+ /**
49
+ * Check if this process should participate in leader election
50
+ */
51
+ private shouldParticipate;
52
+ /**
53
+ * Try to acquire leadership
54
+ */
55
+ private tryAcquireLeadership;
56
+ /**
57
+ * Try to acquire file-based lock
58
+ */
59
+ private tryAcquireFileLock;
60
+ /**
61
+ * Try to acquire Redis-based lock
62
+ */
63
+ private tryAcquireRedisLock;
64
+ /**
65
+ * Become leader
66
+ */
67
+ private becomeLeader;
68
+ /**
69
+ * Step down as leader
70
+ */
71
+ private stepDown;
72
+ /**
73
+ * Start heartbeat to maintain leadership
74
+ */
75
+ private startHeartbeat;
76
+ /**
77
+ * Stop heartbeat
78
+ */
79
+ private stopHeartbeat;
80
+ /**
81
+ * Send heartbeat to maintain lock
82
+ */
83
+ private sendHeartbeat;
84
+ /**
85
+ * Start periodic check for leadership changes
86
+ */
87
+ private startPeriodicCheck;
88
+ /**
89
+ * Stop periodic check
90
+ */
91
+ private stopPeriodicCheck;
92
+ /**
93
+ * Verify we still hold leadership
94
+ */
95
+ private verifyLeadership;
96
+ /**
97
+ * Check health of current leader
98
+ */
99
+ private checkLeaderHealth;
100
+ /**
101
+ * Get instance metadata
102
+ */
103
+ private getInstanceMetadata;
104
+ /**
105
+ * Check if current instance is leader
106
+ */
107
+ isCurrentLeader(): boolean;
108
+ /**
109
+ * Get current leader info
110
+ */
111
+ getLeaderInfo(): LeaderInfo | undefined;
112
+ /**
113
+ * Get instance ID
114
+ */
115
+ getInstanceId(): string;
116
+ /**
117
+ * Force step down (for testing/admin)
118
+ */
119
+ forceStepDown(): Promise<void>;
120
+ /**
121
+ * Shutdown and cleanup
122
+ */
123
+ shutdown(): Promise<void>;
124
+ }
@@ -0,0 +1,481 @@
1
+ // Production-grade Leader Election for Distributed Job Scheduling
2
+ // Supports file-based and Redis-based locking for K8s and clustered environments
3
+ import { EventEmitter } from 'events';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import os from 'os';
7
+ import cluster from 'cluster';
8
+ import { isMainThread } from 'worker_threads';
9
+ /**
10
+ * LeaderElection - Ensures only one job scheduler runs across distributed instances
11
+ * Supports:
12
+ * - File-based locking (for local/NFS environments)
13
+ * - Redis-based locking (for distributed systems)
14
+ * - Kubernetes pod detection
15
+ * - Automatic failover with health checks
16
+ */
17
+ export class LeaderElection extends EventEmitter {
18
+ isLeader = false;
19
+ strategy;
20
+ lockPath;
21
+ lockTimeout;
22
+ heartbeatInterval;
23
+ heartbeatTimer;
24
+ checkTimer;
25
+ logger;
26
+ loggerContext = 'LeaderElection';
27
+ instanceId;
28
+ leaderInfo;
29
+ redisClient;
30
+ isShuttingDown = false;
31
+ lockAcquireAttempts = 0;
32
+ constructor(logger, options = { strategy: 'file' }) {
33
+ super();
34
+ this.logger = logger;
35
+ this.strategy = options.strategy;
36
+ this.lockTimeout = options.lockTimeout ?? 30000; // 30s default
37
+ this.heartbeatInterval = options.heartbeatInterval ?? 10000; // 10s default
38
+ this.redisClient = options.redisClient;
39
+ // Generate unique instance ID
40
+ this.instanceId =
41
+ options.instanceId ||
42
+ `${os.hostname()}-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
43
+ // Setup lock path for file-based strategy
44
+ if (this.strategy === 'file') {
45
+ if (options.lockPath) {
46
+ this.lockPath = options.lockPath;
47
+ }
48
+ else {
49
+ const tmpDir = os.tmpdir();
50
+ this.lockPath = path.join(tmpDir, 'moro-jobs-leader.lock');
51
+ }
52
+ }
53
+ // Force leader mode (for dev/testing)
54
+ if (options.forceLeader) {
55
+ this.isLeader = true;
56
+ this.logger.warn('Leader election forced - running as leader');
57
+ }
58
+ this.logger.debug('LeaderElection initialized', this.loggerContext, {
59
+ strategy: this.strategy,
60
+ instanceId: this.instanceId,
61
+ lockPath: this.lockPath,
62
+ forceLeader: options.forceLeader,
63
+ });
64
+ }
65
+ /**
66
+ * Start leader election process
67
+ */
68
+ async start() {
69
+ if (this.strategy === 'none') {
70
+ this.logger.debug('Leader election disabled, assuming leader role');
71
+ this.becomeLeader();
72
+ return;
73
+ }
74
+ // Check if we should participate in leader election
75
+ if (!this.shouldParticipate()) {
76
+ this.logger.info('Not participating in leader election (worker process/thread)');
77
+ return;
78
+ }
79
+ this.logger.info('Starting leader election...', this.loggerContext, {
80
+ strategy: this.strategy,
81
+ instanceId: this.instanceId,
82
+ });
83
+ // Try to acquire leadership
84
+ await this.tryAcquireLeadership();
85
+ // Start periodic checks
86
+ this.startPeriodicCheck();
87
+ }
88
+ /**
89
+ * Check if this process should participate in leader election
90
+ */
91
+ shouldParticipate() {
92
+ // Only main thread and primary cluster process can be leader
93
+ if (!isMainThread) {
94
+ return false;
95
+ }
96
+ if (cluster.isWorker) {
97
+ return false;
98
+ }
99
+ return true;
100
+ }
101
+ /**
102
+ * Try to acquire leadership
103
+ */
104
+ async tryAcquireLeadership() {
105
+ if (this.isShuttingDown) {
106
+ return false;
107
+ }
108
+ this.lockAcquireAttempts++;
109
+ try {
110
+ let acquired = false;
111
+ if (this.strategy === 'file') {
112
+ acquired = await this.tryAcquireFileLock();
113
+ }
114
+ else if (this.strategy === 'redis') {
115
+ acquired = await this.tryAcquireRedisLock();
116
+ }
117
+ if (acquired) {
118
+ this.becomeLeader();
119
+ return true;
120
+ }
121
+ else {
122
+ // Check if current leader is still alive
123
+ await this.checkLeaderHealth();
124
+ return false;
125
+ }
126
+ }
127
+ catch (error) {
128
+ this.logger.error('Error during leader election', this.loggerContext, {
129
+ error,
130
+ attempt: this.lockAcquireAttempts,
131
+ });
132
+ return false;
133
+ }
134
+ }
135
+ /**
136
+ * Try to acquire file-based lock
137
+ */
138
+ async tryAcquireFileLock() {
139
+ if (!this.lockPath) {
140
+ throw new Error('Lock path not configured');
141
+ }
142
+ try {
143
+ // Check if lock file exists
144
+ const exists = await fs.promises
145
+ .access(this.lockPath)
146
+ .then(() => true)
147
+ .catch(() => false);
148
+ if (exists) {
149
+ // Read existing lock
150
+ const lockData = await fs.promises.readFile(this.lockPath, 'utf-8');
151
+ const existingLeader = JSON.parse(lockData);
152
+ // Check if lock is expired
153
+ const lockAge = Date.now() - new Date(existingLeader.lastHeartbeat).getTime();
154
+ if (lockAge < this.lockTimeout) {
155
+ // Lock is still valid
156
+ this.leaderInfo = existingLeader;
157
+ this.logger.debug('Leader lock held by another instance', this.loggerContext, {
158
+ leader: existingLeader.instanceId,
159
+ age: lockAge,
160
+ });
161
+ return false;
162
+ }
163
+ // Lock expired, take over
164
+ this.logger.warn('Leader lock expired, taking over', this.loggerContext, {
165
+ previousLeader: existingLeader.instanceId,
166
+ lockAge,
167
+ });
168
+ }
169
+ // Create/update lock file
170
+ const lockInfo = {
171
+ instanceId: this.instanceId,
172
+ hostname: os.hostname(),
173
+ pid: process.pid,
174
+ electedAt: new Date(),
175
+ lastHeartbeat: new Date(),
176
+ metadata: this.getInstanceMetadata(),
177
+ };
178
+ await fs.promises.writeFile(this.lockPath, JSON.stringify(lockInfo, null, 2), 'utf-8');
179
+ this.leaderInfo = lockInfo;
180
+ return true;
181
+ }
182
+ catch (error) {
183
+ this.logger.error('Failed to acquire file lock', this.loggerContext, {
184
+ error,
185
+ lockPath: this.lockPath,
186
+ });
187
+ return false;
188
+ }
189
+ }
190
+ /**
191
+ * Try to acquire Redis-based lock
192
+ */
193
+ async tryAcquireRedisLock() {
194
+ if (!this.redisClient) {
195
+ throw new Error('Redis client not configured');
196
+ }
197
+ try {
198
+ const lockKey = 'moro:jobs:leader:lock';
199
+ const lockInfo = {
200
+ instanceId: this.instanceId,
201
+ hostname: os.hostname(),
202
+ pid: process.pid,
203
+ electedAt: new Date(),
204
+ lastHeartbeat: new Date(),
205
+ metadata: this.getInstanceMetadata(),
206
+ };
207
+ // Try to set lock with NX (only if not exists) and PX (expiration in ms)
208
+ const result = await this.redisClient.set(lockKey, JSON.stringify(lockInfo), 'PX', this.lockTimeout, 'NX');
209
+ if (result === 'OK') {
210
+ this.leaderInfo = lockInfo;
211
+ return true;
212
+ }
213
+ // Lock exists, get current leader info
214
+ const existingLock = await this.redisClient.get(lockKey);
215
+ if (existingLock) {
216
+ this.leaderInfo = JSON.parse(existingLock);
217
+ }
218
+ return false;
219
+ }
220
+ catch (error) {
221
+ this.logger.error('Failed to acquire Redis lock', this.loggerContext, { error });
222
+ return false;
223
+ }
224
+ }
225
+ /**
226
+ * Become leader
227
+ */
228
+ becomeLeader() {
229
+ if (this.isLeader) {
230
+ return;
231
+ }
232
+ this.isLeader = true;
233
+ this.logger.info('Became leader', this.loggerContext, {
234
+ instanceId: this.instanceId,
235
+ strategy: this.strategy,
236
+ attempts: this.lockAcquireAttempts,
237
+ });
238
+ this.emit('leader:elected', {
239
+ instanceId: this.instanceId,
240
+ electedAt: this.leaderInfo?.electedAt,
241
+ });
242
+ // Start heartbeat
243
+ this.startHeartbeat();
244
+ }
245
+ /**
246
+ * Step down as leader
247
+ */
248
+ async stepDown() {
249
+ if (!this.isLeader) {
250
+ return;
251
+ }
252
+ this.logger.info('Stepping down as leader', this.loggerContext, {
253
+ instanceId: this.instanceId,
254
+ });
255
+ this.isLeader = false;
256
+ this.stopHeartbeat();
257
+ // Release lock
258
+ try {
259
+ if (this.strategy === 'file' && this.lockPath) {
260
+ await fs.promises.unlink(this.lockPath).catch(() => { });
261
+ }
262
+ else if (this.strategy === 'redis' && this.redisClient) {
263
+ await this.redisClient.del('moro:jobs:leader:lock');
264
+ }
265
+ }
266
+ catch (error) {
267
+ this.logger.error('Failed to release lock during step down', this.loggerContext, { error });
268
+ }
269
+ this.emit('leader:stepdown', { instanceId: this.instanceId });
270
+ }
271
+ /**
272
+ * Start heartbeat to maintain leadership
273
+ */
274
+ startHeartbeat() {
275
+ if (this.heartbeatTimer) {
276
+ return;
277
+ }
278
+ this.heartbeatTimer = setInterval(async () => {
279
+ if (this.isShuttingDown) {
280
+ return;
281
+ }
282
+ try {
283
+ await this.sendHeartbeat();
284
+ }
285
+ catch (error) {
286
+ this.logger.error('Heartbeat failed', this.loggerContext, { error });
287
+ // Lost leadership
288
+ await this.stepDown();
289
+ // Try to reacquire
290
+ setTimeout(() => this.tryAcquireLeadership(), 1000);
291
+ }
292
+ }, this.heartbeatInterval);
293
+ // Don't keep process alive
294
+ this.heartbeatTimer.unref();
295
+ }
296
+ /**
297
+ * Stop heartbeat
298
+ */
299
+ stopHeartbeat() {
300
+ if (this.heartbeatTimer) {
301
+ clearInterval(this.heartbeatTimer);
302
+ this.heartbeatTimer = undefined;
303
+ }
304
+ }
305
+ /**
306
+ * Send heartbeat to maintain lock
307
+ */
308
+ async sendHeartbeat() {
309
+ if (!this.leaderInfo) {
310
+ return;
311
+ }
312
+ this.leaderInfo.lastHeartbeat = new Date();
313
+ if (this.strategy === 'file' && this.lockPath) {
314
+ await fs.promises.writeFile(this.lockPath, JSON.stringify(this.leaderInfo, null, 2), 'utf-8');
315
+ }
316
+ else if (this.strategy === 'redis' && this.redisClient) {
317
+ const lockKey = 'moro:jobs:leader:lock';
318
+ await this.redisClient.set(lockKey, JSON.stringify(this.leaderInfo), 'PX', this.lockTimeout);
319
+ }
320
+ this.logger.debug('Heartbeat sent', this.loggerContext, { instanceId: this.instanceId });
321
+ this.emit('leader:heartbeat', {
322
+ instanceId: this.instanceId,
323
+ timestamp: this.leaderInfo.lastHeartbeat,
324
+ });
325
+ }
326
+ /**
327
+ * Start periodic check for leadership changes
328
+ */
329
+ startPeriodicCheck() {
330
+ if (this.checkTimer) {
331
+ return;
332
+ }
333
+ const checkInterval = Math.floor(this.heartbeatInterval * 1.5);
334
+ this.checkTimer = setInterval(async () => {
335
+ if (this.isShuttingDown) {
336
+ return;
337
+ }
338
+ if (!this.isLeader) {
339
+ // Try to acquire leadership
340
+ await this.tryAcquireLeadership();
341
+ }
342
+ else {
343
+ // Verify we still hold the lock
344
+ await this.verifyLeadership();
345
+ }
346
+ }, checkInterval);
347
+ // Don't keep process alive
348
+ this.checkTimer.unref();
349
+ }
350
+ /**
351
+ * Stop periodic check
352
+ */
353
+ stopPeriodicCheck() {
354
+ if (this.checkTimer) {
355
+ clearInterval(this.checkTimer);
356
+ this.checkTimer = undefined;
357
+ }
358
+ }
359
+ /**
360
+ * Verify we still hold leadership
361
+ */
362
+ async verifyLeadership() {
363
+ if (!this.isLeader) {
364
+ return;
365
+ }
366
+ try {
367
+ let stillLeader = false;
368
+ if (this.strategy === 'file' && this.lockPath) {
369
+ const lockData = await fs.promises.readFile(this.lockPath, 'utf-8');
370
+ const currentLeader = JSON.parse(lockData);
371
+ stillLeader = currentLeader.instanceId === this.instanceId;
372
+ }
373
+ else if (this.strategy === 'redis' && this.redisClient) {
374
+ const lockData = await this.redisClient.get('moro:jobs:leader:lock');
375
+ if (lockData) {
376
+ const currentLeader = JSON.parse(lockData);
377
+ stillLeader = currentLeader.instanceId === this.instanceId;
378
+ }
379
+ }
380
+ if (!stillLeader) {
381
+ this.logger.warn('Lost leadership', this.loggerContext, { instanceId: this.instanceId });
382
+ await this.stepDown();
383
+ }
384
+ }
385
+ catch (error) {
386
+ this.logger.error('Failed to verify leadership', this.loggerContext, { error });
387
+ await this.stepDown();
388
+ }
389
+ }
390
+ /**
391
+ * Check health of current leader
392
+ */
393
+ async checkLeaderHealth() {
394
+ if (!this.leaderInfo || this.leaderInfo.instanceId === this.instanceId) {
395
+ return;
396
+ }
397
+ const heartbeatAge = Date.now() - new Date(this.leaderInfo.lastHeartbeat).getTime();
398
+ if (heartbeatAge > this.lockTimeout) {
399
+ this.logger.warn('Current leader appears unhealthy', this.loggerContext, {
400
+ leader: this.leaderInfo.instanceId,
401
+ heartbeatAge,
402
+ });
403
+ this.emit('leader:unhealthy', {
404
+ leader: this.leaderInfo.instanceId,
405
+ heartbeatAge,
406
+ });
407
+ }
408
+ }
409
+ /**
410
+ * Get instance metadata
411
+ */
412
+ getInstanceMetadata() {
413
+ const metadata = {
414
+ nodeVersion: process.version,
415
+ platform: process.platform,
416
+ arch: process.arch,
417
+ uptime: process.uptime(),
418
+ };
419
+ // K8s detection
420
+ if (process.env.KUBERNETES_SERVICE_HOST) {
421
+ metadata.kubernetes = {
422
+ podName: process.env.HOSTNAME || os.hostname(),
423
+ namespace: process.env.POD_NAMESPACE,
424
+ nodeName: process.env.NODE_NAME,
425
+ };
426
+ }
427
+ // Container detection
428
+ if (process.env.CONTAINER) {
429
+ metadata.container = process.env.CONTAINER;
430
+ }
431
+ return metadata;
432
+ }
433
+ /**
434
+ * Check if current instance is leader
435
+ */
436
+ isCurrentLeader() {
437
+ return this.isLeader;
438
+ }
439
+ /**
440
+ * Get current leader info
441
+ */
442
+ getLeaderInfo() {
443
+ return this.leaderInfo ? { ...this.leaderInfo } : undefined;
444
+ }
445
+ /**
446
+ * Get instance ID
447
+ */
448
+ getInstanceId() {
449
+ return this.instanceId;
450
+ }
451
+ /**
452
+ * Force step down (for testing/admin)
453
+ */
454
+ async forceStepDown() {
455
+ if (!this.isLeader) {
456
+ return;
457
+ }
458
+ this.logger.warn('Forced step down requested', this.loggerContext, {
459
+ instanceId: this.instanceId,
460
+ });
461
+ await this.stepDown();
462
+ }
463
+ /**
464
+ * Shutdown and cleanup
465
+ */
466
+ async shutdown() {
467
+ this.logger.info('LeaderElection shutting down...', this.loggerContext, {
468
+ instanceId: this.instanceId,
469
+ wasLeader: this.isLeader,
470
+ });
471
+ this.isShuttingDown = true;
472
+ this.stopHeartbeat();
473
+ this.stopPeriodicCheck();
474
+ if (this.isLeader) {
475
+ await this.stepDown();
476
+ }
477
+ this.removeAllListeners();
478
+ this.logger.info('LeaderElection shutdown complete');
479
+ }
480
+ }
481
+ //# sourceMappingURL=leader-election.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leader-election.js","sourceRoot":"","sources":["../../../src/core/jobs/leader-election.ts"],"names":[],"mappings":"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"}