koatty_schedule 4.0.7 → 4.1.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/dist/index.mjs CHANGED
@@ -1,34 +1,49 @@
1
- import { IOCContainer } from 'koatty_container';
2
- import { Redlock } from '@sesamecare-oss/redlock';
3
- import { DefaultLogger } from 'koatty_logger';
4
1
  import Redis, { Cluster } from 'ioredis';
2
+ import { DefaultLogger } from 'koatty_logger';
3
+ import { Redlock } from '@sesamecare-oss/redlock';
4
+ import { IOCContainer } from 'koatty_container';
5
5
  import { Helper } from 'koatty_lib';
6
6
  import { CronJob } from 'cron';
7
7
 
8
8
  /*!
9
9
  * @Author: richen
10
- * @Date: 2026-03-07 15:34:44
10
+ * @Date: 2026-04-24 08:20:32
11
11
  * @License: BSD (3-Clause)
12
12
  * @Copyright (c) - <richenlin(at)gmail.com>
13
13
  * @HomePage: https://koatty.org/
14
14
  */
15
15
  var __defProp = Object.defineProperty;
16
+ var __getOwnPropNames = Object.getOwnPropertyNames;
16
17
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
18
+ var __esm = (fn, res) => function __init() {
19
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
20
+ };
21
+ var __export = (target, all) => {
22
+ for (var name in all)
23
+ __defProp(target, name, { get: all[name], enumerable: true });
24
+ };
17
25
 
18
26
  // src/config/config.ts
19
- var COMPONENT_SCHEDULED = "COMPONENT_SCHEDULED";
20
- var DecoratorType = /* @__PURE__ */ (function(DecoratorType2) {
21
- DecoratorType2["SCHEDULED"] = "SCHEDULED";
22
- DecoratorType2["REDLOCK"] = "REDLOCK";
23
- return DecoratorType2;
24
- })({});
27
+ var config_exports = {};
28
+ __export(config_exports, {
29
+ COMPONENT_REDLOCK: () => COMPONENT_REDLOCK,
30
+ COMPONENT_SCHEDULED: () => COMPONENT_SCHEDULED,
31
+ DecoratorType: () => DecoratorType,
32
+ getEffectiveRedLockOptions: () => getEffectiveRedLockOptions,
33
+ getEffectiveTimezone: () => getEffectiveTimezone,
34
+ getGlobalScheduledOptions: () => getGlobalScheduledOptions,
35
+ setGlobalScheduledOptions: () => setGlobalScheduledOptions,
36
+ validateCronExpression: () => validateCronExpression,
37
+ validateRedLockMethodOptions: () => validateRedLockMethodOptions,
38
+ validateRedLockOptions: () => validateRedLockOptions
39
+ });
25
40
  function validateCronExpression(cron) {
26
41
  if (!cron || typeof cron !== "string") {
27
- throw new Error("Cron \u8868\u8FBE\u5F0F\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32 (Cron expression must be a non-empty string)");
42
+ throw new Error("Cron expression must be a non-empty string");
28
43
  }
29
44
  const cronParts = cron.trim().split(/\s+/);
30
45
  if (cronParts.length < 5 || cronParts.length > 6) {
31
- throw new Error(`Cron \u8868\u8FBE\u5F0F\u683C\u5F0F\u65E0\u6548\u3002\u671F\u671B 5 \u6216 6 \u90E8\u5206\uFF0C\u5B9E\u9645\u5F97\u5230 ${cronParts.length} \u90E8\u5206 (Invalid cron format. Expected 5 or 6 parts, got ${cronParts.length})`);
46
+ throw new Error(`Invalid cron format. Expected 5 or 6 parts, got ${cronParts.length}`);
32
47
  }
33
48
  const hasSecs = cronParts.length === 6;
34
49
  const offset = hasSecs ? 0 : -1;
@@ -68,7 +83,6 @@ function validateCronExpression(cron) {
68
83
  "SAT"
69
84
  ]);
70
85
  }
71
- __name(validateCronExpression, "validateCronExpression");
72
86
  function validateCronField(field, min, max, fieldName, fieldNameCN, allowedStrings) {
73
87
  if (field === "*") {
74
88
  return;
@@ -83,7 +97,7 @@ function validateCronField(field, min, max, fieldName, fieldNameCN, allowedStrin
83
97
  const [range, step] = field.split("/");
84
98
  const stepValue = parseInt(step);
85
99
  if (isNaN(stepValue) || stepValue <= 0) {
86
- throw new Error(`${fieldNameCN}\u5B57\u6BB5\u7684\u6B65\u957F\u503C\u65E0\u6548: ${step} (Invalid step value for ${fieldName}: ${step})`);
100
+ throw new Error(`Invalid step value for ${fieldName}: ${step}`);
87
101
  }
88
102
  if (range !== "*") {
89
103
  validateCronField(range, min, max, fieldName, fieldNameCN, allowedStrings);
@@ -95,13 +109,13 @@ function validateCronField(field, min, max, fieldName, fieldNameCN, allowedStrin
95
109
  const startValue = parseInt(start);
96
110
  const endValue = parseInt(end);
97
111
  if (isNaN(startValue) || startValue < min || startValue > max) {
98
- throw new Error(`${fieldNameCN}\u5B57\u6BB5\u7684\u8303\u56F4\u8D77\u59CB\u503C\u65E0\u6548: ${start}\uFF0C\u5FC5\u987B\u5728 ${min}-${max} \u4E4B\u95F4 (Invalid range start for ${fieldName}: ${start}, must be between ${min}-${max})`);
112
+ throw new Error(`Invalid range start for ${fieldName}: ${start}, must be between ${min}-${max}`);
99
113
  }
100
114
  if (isNaN(endValue) || endValue < min || endValue > max) {
101
- throw new Error(`${fieldNameCN}\u5B57\u6BB5\u7684\u8303\u56F4\u7ED3\u675F\u503C\u65E0\u6548: ${end}\uFF0C\u5FC5\u987B\u5728 ${min}-${max} \u4E4B\u95F4 (Invalid range end for ${fieldName}: ${end}, must be between ${min}-${max})`);
115
+ throw new Error(`Invalid range end for ${fieldName}: ${end}, must be between ${min}-${max}`);
102
116
  }
103
117
  if (startValue > endValue) {
104
- throw new Error(`${fieldNameCN}\u5B57\u6BB5\u7684\u8303\u56F4\u65E0\u6548: ${start}-${end}\uFF0C\u8D77\u59CB\u503C\u4E0D\u80FD\u5927\u4E8E\u7ED3\u675F\u503C (Invalid range for ${fieldName}: ${start}-${end}, start cannot be greater than end)`);
118
+ throw new Error(`Invalid range for ${fieldName}: ${start}-${end}, start cannot be greater than end`);
105
119
  }
106
120
  return;
107
121
  }
@@ -114,10 +128,9 @@ function validateCronField(field, min, max, fieldName, fieldNameCN, allowedStrin
114
128
  }
115
129
  const numValue = parseInt(field);
116
130
  if (isNaN(numValue) || numValue < min || numValue > max) {
117
- throw new Error(`${fieldNameCN}\u5B57\u6BB5\u7684\u503C\u65E0\u6548: ${field}\uFF0C\u5FC5\u987B\u5728 ${min}-${max} \u4E4B\u95F4 (Invalid ${fieldName} value: ${field}, must be between ${min}-${max})`);
131
+ throw new Error(`Invalid ${fieldName} value: ${field}, must be between ${min}-${max}`);
118
132
  }
119
133
  }
120
- __name(validateCronField, "validateCronField");
121
134
  function validateRedLockMethodOptions(options) {
122
135
  if (!options || typeof options !== "object") {
123
136
  throw new Error("RedLock method options must be an object");
@@ -143,16 +156,42 @@ function validateRedLockMethodOptions(options) {
143
156
  }
144
157
  }
145
158
  }
146
- __name(validateRedLockMethodOptions, "validateRedLockMethodOptions");
147
- var globalScheduledOptions = {};
159
+ function validateRedLockOptions(options) {
160
+ if (!options || typeof options !== "object") {
161
+ throw new Error("RedLock options must be an object");
162
+ }
163
+ if (options.lockTimeOut !== void 0) {
164
+ if (typeof options.lockTimeOut !== "number" || options.lockTimeOut <= 0) {
165
+ throw new Error("lockTimeOut must be a positive number");
166
+ }
167
+ }
168
+ if (options.retryCount !== void 0) {
169
+ if (typeof options.retryCount !== "number" || options.retryCount < 0) {
170
+ throw new Error("retryCount must be a non-negative number");
171
+ }
172
+ }
173
+ if (options.retryDelay !== void 0) {
174
+ if (typeof options.retryDelay !== "number" || options.retryDelay < 0) {
175
+ throw new Error("retryDelay must be a non-negative number");
176
+ }
177
+ }
178
+ if (options.retryJitter !== void 0) {
179
+ if (typeof options.retryJitter !== "number" || options.retryJitter < 0) {
180
+ throw new Error("retryJitter must be a non-negative number");
181
+ }
182
+ }
183
+ }
184
+ function setGlobalScheduledOptions(options) {
185
+ globalScheduledOptions = {
186
+ ...options
187
+ };
188
+ }
148
189
  function getGlobalScheduledOptions() {
149
190
  return globalScheduledOptions;
150
191
  }
151
- __name(getGlobalScheduledOptions, "getGlobalScheduledOptions");
152
192
  function getEffectiveTimezone(options, userTimezone) {
153
193
  return userTimezone || options.timezone || "Asia/Beijing";
154
194
  }
155
- __name(getEffectiveTimezone, "getEffectiveTimezone");
156
195
  function getEffectiveRedLockOptions(methodOptions) {
157
196
  const globalOptions = getGlobalScheduledOptions();
158
197
  return {
@@ -162,580 +201,626 @@ function getEffectiveRedLockOptions(methodOptions) {
162
201
  retryDelayMs: methodOptions?.retryDelayMs || globalOptions.retryDelayMs || 200
163
202
  };
164
203
  }
165
- __name(getEffectiveRedLockOptions, "getEffectiveRedLockOptions");
204
+ var COMPONENT_SCHEDULED, COMPONENT_REDLOCK, DecoratorType, globalScheduledOptions;
205
+ var init_config = __esm({
206
+ "src/config/config.ts"() {
207
+ COMPONENT_SCHEDULED = "COMPONENT_SCHEDULED";
208
+ COMPONENT_REDLOCK = "COMPONENT_REDLOCK";
209
+ DecoratorType = /* @__PURE__ */ (function(DecoratorType2) {
210
+ DecoratorType2["SCHEDULED"] = "SCHEDULED";
211
+ DecoratorType2["REDLOCK"] = "REDLOCK";
212
+ return DecoratorType2;
213
+ })({});
214
+ __name(validateCronExpression, "validateCronExpression");
215
+ __name(validateCronField, "validateCronField");
216
+ __name(validateRedLockMethodOptions, "validateRedLockMethodOptions");
217
+ __name(validateRedLockOptions, "validateRedLockOptions");
218
+ globalScheduledOptions = {};
219
+ __name(setGlobalScheduledOptions, "setGlobalScheduledOptions");
220
+ __name(getGlobalScheduledOptions, "getGlobalScheduledOptions");
221
+ __name(getEffectiveTimezone, "getEffectiveTimezone");
222
+ __name(getEffectiveRedLockOptions, "getEffectiveRedLockOptions");
223
+ }
224
+ });
166
225
 
167
226
  // src/locker/interface.ts
168
- var RedisMode = /* @__PURE__ */ (function(RedisMode2) {
169
- RedisMode2["STANDALONE"] = "standalone";
170
- RedisMode2["SENTINEL"] = "sentinel";
171
- RedisMode2["CLUSTER"] = "cluster";
172
- return RedisMode2;
173
- })({});
174
- var RedisClientAdapter = class RedisClientAdapter2 {
175
- static {
176
- __name(this, "RedisClientAdapter");
177
- }
178
- client;
179
- constructor(client) {
180
- this.client = client;
181
- }
182
- get status() {
183
- return this.client.status;
184
- }
185
- async call(command, ...args) {
186
- return this.client.call(command, ...args);
187
- }
188
- async set(key, value, mode, duration) {
189
- if (mode && duration) {
190
- return this.client.set(key, value, mode, duration);
191
- }
192
- return this.client.set(key, value);
193
- }
194
- async get(key) {
195
- return this.client.get(key);
196
- }
197
- async del(...keys) {
198
- return this.client.del(...keys);
199
- }
200
- async exists(key) {
201
- return this.client.exists(key);
202
- }
203
- async eval(script, numKeys, ...args) {
204
- return this.client.eval(script, numKeys, ...args);
205
- }
206
- async quit() {
207
- return this.client.quit();
208
- }
209
- disconnect() {
210
- this.client.disconnect();
211
- }
212
- /**
213
- * Get underlying Redis/Cluster instance
214
- * Used for RedLock initialization
215
- */
216
- getClient() {
217
- return this.client;
218
- }
219
- };
220
- var RedisFactory = class {
221
- static {
222
- __name(this, "RedisFactory");
223
- }
224
- /**
225
- * Create Redis client based on configuration mode
226
- * @param config - Redis configuration
227
- * @returns Redis client adapter
228
- */
229
- static createClient(config) {
230
- const mode = config.mode || RedisMode.STANDALONE;
231
- DefaultLogger.Debug(`Creating Redis client in ${mode} mode`);
232
- switch (mode) {
233
- case RedisMode.STANDALONE:
234
- return this.createStandaloneClient(config);
235
- case RedisMode.SENTINEL:
236
- return this.createSentinelClient(config);
237
- case RedisMode.CLUSTER:
238
- return this.createClusterClient(config);
239
- default:
240
- throw new Error(`\u4E0D\u652F\u6301\u7684 Redis \u6A21\u5F0F: ${mode} (Unsupported Redis mode: ${mode})`);
241
- }
242
- }
243
- /**
244
- * Create standalone Redis client
245
- * @param config - Standalone configuration
246
- */
247
- static createStandaloneClient(config) {
248
- DefaultLogger.Debug(`Creating standalone Redis client: ${config.host}:${config.port}`);
249
- const options = {
250
- host: config.host,
251
- port: config.port,
252
- password: config.password || void 0,
253
- db: config.db || 0,
254
- keyPrefix: config.keyPrefix || "",
255
- connectTimeout: config.connectTimeout || 1e4,
256
- commandTimeout: config.commandTimeout || 5e3,
257
- maxRetriesPerRequest: config.maxRetriesPerRequest || 3,
258
- retryStrategy: /* @__PURE__ */ __name((times) => {
259
- const delay = Math.min(times * 50, 2e3);
260
- DefaultLogger.Debug(`Redis reconnecting, attempt ${times}, delay ${delay}ms`);
261
- return delay;
262
- }, "retryStrategy"),
263
- reconnectOnError: /* @__PURE__ */ __name((err) => {
264
- DefaultLogger.Warn("Redis connection error, attempting reconnect:", err.message);
265
- return true;
266
- }, "reconnectOnError")
267
- };
268
- const client = new Redis(options);
269
- client.on("connect", () => {
270
- DefaultLogger.Info("Redis standalone client connected successfully");
271
- });
272
- client.on("error", (err) => {
273
- DefaultLogger.Error("Redis standalone client error:", err);
274
- });
275
- return new RedisClientAdapter(client);
276
- }
277
- /**
278
- * Create sentinel Redis client
279
- * @param config - Sentinel configuration
280
- */
281
- static createSentinelClient(config) {
282
- DefaultLogger.Debug(`Creating sentinel Redis client for master: ${config.name}`);
283
- const options = {
284
- sentinels: config.sentinels,
285
- name: config.name,
286
- password: config.password || void 0,
287
- sentinelPassword: config.sentinelPassword || void 0,
288
- db: config.db || 0,
289
- keyPrefix: config.keyPrefix || "",
290
- connectTimeout: config.connectTimeout || 1e4,
291
- commandTimeout: config.commandTimeout || 5e3,
292
- maxRetriesPerRequest: config.maxRetriesPerRequest || 3,
293
- retryStrategy: /* @__PURE__ */ __name((times) => {
294
- const delay = Math.min(times * 50, 2e3);
295
- DefaultLogger.Debug(`Sentinel Redis reconnecting, attempt ${times}, delay ${delay}ms`);
296
- return delay;
297
- }, "retryStrategy")
227
+ var RedisMode;
228
+ var init_interface = __esm({
229
+ "src/locker/interface.ts"() {
230
+ RedisMode = /* @__PURE__ */ (function(RedisMode2) {
231
+ RedisMode2["STANDALONE"] = "standalone";
232
+ RedisMode2["SENTINEL"] = "sentinel";
233
+ RedisMode2["CLUSTER"] = "cluster";
234
+ return RedisMode2;
235
+ })({});
236
+ }
237
+ });
238
+ var RedisClientAdapter, RedisFactory;
239
+ var init_redis_factory = __esm({
240
+ "src/locker/redis-factory.ts"() {
241
+ init_interface();
242
+ RedisClientAdapter = class RedisClientAdapter2 {
243
+ static {
244
+ __name(this, "RedisClientAdapter");
245
+ }
246
+ client;
247
+ constructor(client) {
248
+ this.client = client;
249
+ }
250
+ get status() {
251
+ return this.client.status;
252
+ }
253
+ async call(command, ...args) {
254
+ return this.client.call(command, ...args);
255
+ }
256
+ async set(key, value, mode, duration) {
257
+ if (mode && duration) {
258
+ return this.client.set(key, value, mode, duration);
259
+ }
260
+ return this.client.set(key, value);
261
+ }
262
+ async get(key) {
263
+ return this.client.get(key);
264
+ }
265
+ async del(...keys) {
266
+ return this.client.del(...keys);
267
+ }
268
+ async exists(key) {
269
+ return this.client.exists(key);
270
+ }
271
+ async eval(script, numKeys, ...args) {
272
+ return this.client.eval(script, numKeys, ...args);
273
+ }
274
+ async quit() {
275
+ return this.client.quit();
276
+ }
277
+ disconnect() {
278
+ this.client.disconnect();
279
+ }
280
+ /**
281
+ * Get underlying Redis/Cluster instance
282
+ * Used for RedLock initialization
283
+ */
284
+ getClient() {
285
+ return this.client;
286
+ }
298
287
  };
299
- const client = new Redis(options);
300
- client.on("connect", () => {
301
- DefaultLogger.Info(`Redis sentinel client connected to master: ${config.name}`);
302
- });
303
- client.on("error", (err) => {
304
- DefaultLogger.Error("Redis sentinel client error:", err);
305
- });
306
- return new RedisClientAdapter(client);
307
- }
308
- /**
309
- * Create cluster Redis client
310
- * @param config - Cluster configuration
311
- */
312
- static createClusterClient(config) {
313
- DefaultLogger.Debug(`Creating cluster Redis client with ${config.nodes.length} nodes`);
314
- const clusterOptions = {
315
- redisOptions: {
316
- password: config.redisOptions?.password || config.password || void 0,
317
- db: config.redisOptions?.db || config.db || 0,
318
- keyPrefix: config.keyPrefix || "",
319
- connectTimeout: config.connectTimeout || 1e4,
320
- commandTimeout: config.commandTimeout || 5e3,
321
- maxRetriesPerRequest: config.maxRetriesPerRequest || 3
322
- },
323
- clusterRetryStrategy: /* @__PURE__ */ __name((times) => {
324
- const delay = Math.min(times * 50, 2e3);
325
- DefaultLogger.Debug(`Cluster Redis reconnecting, attempt ${times}, delay ${delay}ms`);
326
- return delay;
327
- }, "clusterRetryStrategy")
288
+ RedisFactory = class {
289
+ static {
290
+ __name(this, "RedisFactory");
291
+ }
292
+ /**
293
+ * Create Redis client based on configuration mode
294
+ * @param config - Redis configuration
295
+ * @returns Redis client adapter
296
+ */
297
+ static createClient(config) {
298
+ const mode = config.mode || RedisMode.STANDALONE;
299
+ DefaultLogger.Debug(`Creating Redis client in ${mode} mode`);
300
+ switch (mode) {
301
+ case RedisMode.STANDALONE:
302
+ return this.createStandaloneClient(config);
303
+ case RedisMode.SENTINEL:
304
+ return this.createSentinelClient(config);
305
+ case RedisMode.CLUSTER:
306
+ return this.createClusterClient(config);
307
+ default:
308
+ throw new Error(`Unsupported Redis mode: ${mode}`);
309
+ }
310
+ }
311
+ /**
312
+ * Create standalone Redis client
313
+ * @param config - Standalone configuration
314
+ */
315
+ static createStandaloneClient(config) {
316
+ DefaultLogger.Debug(`Creating standalone Redis client: ${config.host}:${config.port}`);
317
+ const options = {
318
+ host: config.host,
319
+ port: config.port,
320
+ password: config.password || void 0,
321
+ db: config.db || 0,
322
+ keyPrefix: config.keyPrefix || "",
323
+ connectTimeout: config.connectTimeout || 1e4,
324
+ commandTimeout: config.commandTimeout || 5e3,
325
+ maxRetriesPerRequest: config.maxRetriesPerRequest || 3,
326
+ retryStrategy: /* @__PURE__ */ __name((times) => {
327
+ const delay = Math.min(times * 50, 2e3);
328
+ DefaultLogger.Debug(`Redis reconnecting, attempt ${times}, delay ${delay}ms`);
329
+ return delay;
330
+ }, "retryStrategy"),
331
+ reconnectOnError: /* @__PURE__ */ __name((err) => {
332
+ DefaultLogger.Warn("Redis connection error, attempting reconnect:", err.message);
333
+ return true;
334
+ }, "reconnectOnError")
335
+ };
336
+ const client = new Redis(options);
337
+ client.on("connect", () => {
338
+ DefaultLogger.Info("Redis standalone client connected successfully");
339
+ });
340
+ client.on("error", (err) => {
341
+ DefaultLogger.Error("Redis standalone client error:", err);
342
+ });
343
+ return new RedisClientAdapter(client);
344
+ }
345
+ /**
346
+ * Create sentinel Redis client
347
+ * @param config - Sentinel configuration
348
+ */
349
+ static createSentinelClient(config) {
350
+ DefaultLogger.Debug(`Creating sentinel Redis client for master: ${config.name}`);
351
+ const options = {
352
+ sentinels: config.sentinels,
353
+ name: config.name,
354
+ password: config.password || void 0,
355
+ sentinelPassword: config.sentinelPassword || void 0,
356
+ db: config.db || 0,
357
+ keyPrefix: config.keyPrefix || "",
358
+ connectTimeout: config.connectTimeout || 1e4,
359
+ commandTimeout: config.commandTimeout || 5e3,
360
+ maxRetriesPerRequest: config.maxRetriesPerRequest || 3,
361
+ retryStrategy: /* @__PURE__ */ __name((times) => {
362
+ const delay = Math.min(times * 50, 2e3);
363
+ DefaultLogger.Debug(`Sentinel Redis reconnecting, attempt ${times}, delay ${delay}ms`);
364
+ return delay;
365
+ }, "retryStrategy")
366
+ };
367
+ const client = new Redis(options);
368
+ client.on("connect", () => {
369
+ DefaultLogger.Info(`Redis sentinel client connected to master: ${config.name}`);
370
+ });
371
+ client.on("error", (err) => {
372
+ DefaultLogger.Error("Redis sentinel client error:", err);
373
+ });
374
+ return new RedisClientAdapter(client);
375
+ }
376
+ /**
377
+ * Create cluster Redis client
378
+ * @param config - Cluster configuration
379
+ */
380
+ static createClusterClient(config) {
381
+ DefaultLogger.Debug(`Creating cluster Redis client with ${config.nodes.length} nodes`);
382
+ const clusterOptions = {
383
+ redisOptions: {
384
+ password: config.redisOptions?.password || config.password || void 0,
385
+ db: config.redisOptions?.db || config.db || 0,
386
+ keyPrefix: config.keyPrefix || "",
387
+ connectTimeout: config.connectTimeout || 1e4,
388
+ commandTimeout: config.commandTimeout || 5e3,
389
+ maxRetriesPerRequest: config.maxRetriesPerRequest || 3
390
+ },
391
+ clusterRetryStrategy: /* @__PURE__ */ __name((times) => {
392
+ const delay = Math.min(times * 50, 2e3);
393
+ DefaultLogger.Debug(`Cluster Redis reconnecting, attempt ${times}, delay ${delay}ms`);
394
+ return delay;
395
+ }, "clusterRetryStrategy")
396
+ };
397
+ const cluster = new Cluster(config.nodes, clusterOptions);
398
+ cluster.on("connect", () => {
399
+ DefaultLogger.Info("Redis cluster client connected successfully");
400
+ });
401
+ cluster.on("error", (err) => {
402
+ DefaultLogger.Error("Redis cluster client error:", err);
403
+ });
404
+ cluster.on("node error", (err, address) => {
405
+ DefaultLogger.Error(`Redis cluster node error at ${address}:`, err);
406
+ });
407
+ return new RedisClientAdapter(cluster);
408
+ }
409
+ /**
410
+ * Validate Redis configuration
411
+ * @param config - Redis configuration to validate
412
+ */
413
+ static validateConfig(config) {
414
+ if (!config) {
415
+ throw new Error("Redis configuration cannot be empty");
416
+ }
417
+ const mode = config.mode || RedisMode.STANDALONE;
418
+ switch (mode) {
419
+ case RedisMode.STANDALONE:
420
+ this.validateStandaloneConfig(config);
421
+ break;
422
+ case RedisMode.SENTINEL:
423
+ this.validateSentinelConfig(config);
424
+ break;
425
+ case RedisMode.CLUSTER:
426
+ this.validateClusterConfig(config);
427
+ break;
428
+ default:
429
+ throw new Error(`Unsupported Redis mode: ${mode}`);
430
+ }
431
+ }
432
+ static validateStandaloneConfig(config) {
433
+ if (!config.host) {
434
+ throw new Error("Standalone mode requires host configuration");
435
+ }
436
+ if (!config.port) {
437
+ throw new Error("Standalone mode requires port configuration");
438
+ }
439
+ }
440
+ static validateSentinelConfig(config) {
441
+ if (!config.sentinels || config.sentinels.length === 0) {
442
+ throw new Error("Sentinel mode requires at least one sentinel node");
443
+ }
444
+ if (!config.name) {
445
+ throw new Error("Sentinel mode requires master name");
446
+ }
447
+ }
448
+ static validateClusterConfig(config) {
449
+ if (!config.nodes || config.nodes.length === 0) {
450
+ throw new Error("Cluster mode requires at least one node");
451
+ }
452
+ }
328
453
  };
329
- const cluster = new Cluster(config.nodes, clusterOptions);
330
- cluster.on("connect", () => {
331
- DefaultLogger.Info("Redis cluster client connected successfully");
332
- });
333
- cluster.on("error", (err) => {
334
- DefaultLogger.Error("Redis cluster client error:", err);
335
- });
336
- cluster.on("node error", (err, address) => {
337
- DefaultLogger.Error(`Redis cluster node error at ${address}:`, err);
338
- });
339
- return new RedisClientAdapter(cluster);
340
- }
341
- /**
342
- * Validate Redis configuration
343
- * @param config - Redis configuration to validate
344
- */
345
- static validateConfig(config) {
346
- if (!config) {
347
- throw new Error("Redis \u914D\u7F6E\u4E0D\u80FD\u4E3A\u7A7A (Redis configuration cannot be empty)");
348
- }
349
- const mode = config.mode || RedisMode.STANDALONE;
350
- switch (mode) {
351
- case RedisMode.STANDALONE:
352
- this.validateStandaloneConfig(config);
353
- break;
354
- case RedisMode.SENTINEL:
355
- this.validateSentinelConfig(config);
356
- break;
357
- case RedisMode.CLUSTER:
358
- this.validateClusterConfig(config);
359
- break;
360
- default:
361
- throw new Error(`\u4E0D\u652F\u6301\u7684 Redis \u6A21\u5F0F: ${mode} (Unsupported Redis mode: ${mode})`);
362
- }
363
454
  }
364
- static validateStandaloneConfig(config) {
365
- if (!config.host) {
366
- throw new Error("\u5355\u673A\u6A21\u5F0F\u9700\u8981 host \u914D\u7F6E (Standalone mode requires host configuration)");
367
- }
368
- if (!config.port) {
369
- throw new Error("\u5355\u673A\u6A21\u5F0F\u9700\u8981 port \u914D\u7F6E (Standalone mode requires port configuration)");
370
- }
371
- }
372
- static validateSentinelConfig(config) {
373
- if (!config.sentinels || config.sentinels.length === 0) {
374
- throw new Error("\u54E8\u5175\u6A21\u5F0F\u9700\u8981\u81F3\u5C11\u4E00\u4E2A\u54E8\u5175\u8282\u70B9\u914D\u7F6E (Sentinel mode requires at least one sentinel node)");
375
- }
376
- if (!config.name) {
377
- throw new Error("\u54E8\u5175\u6A21\u5F0F\u9700\u8981 master name \u914D\u7F6E (Sentinel mode requires master name)");
378
- }
379
- }
380
- static validateClusterConfig(config) {
381
- if (!config.nodes || config.nodes.length === 0) {
382
- throw new Error("\u96C6\u7FA4\u6A21\u5F0F\u9700\u8981\u81F3\u5C11\u4E00\u4E2A\u8282\u70B9\u914D\u7F6E (Cluster mode requires at least one node)");
383
- }
384
- }
385
- };
455
+ });
386
456
 
387
457
  // src/locker/redlock.ts
388
- var defaultRedLockConfig = {
389
- lockTimeOut: 1e4,
390
- clockDriftFactor: 0.01,
391
- maxRetries: 3,
392
- retryDelayMs: 200,
393
- redisConfig: {
394
- mode: RedisMode.STANDALONE,
395
- host: "127.0.0.1",
396
- port: 6379,
397
- password: "",
398
- db: 0,
399
- keyPrefix: "redlock:"
400
- }
401
- };
402
- var defaultRedlockSettings = {
403
- driftFactor: 0.01,
404
- retryCount: 3,
405
- retryDelay: 200,
406
- retryJitter: 200,
407
- automaticExtensionThreshold: 500
408
- };
409
- var RedLocker = class _RedLocker {
410
- static {
411
- __name(this, "RedLocker");
412
- }
413
- static instance = null;
414
- static instanceLock = /* @__PURE__ */ Symbol("RedLocker.instanceLock");
415
- redlock = null;
416
- redisClient = null;
417
- config;
418
- isInitialized = false;
419
- initializationPromise = null;
420
- // 私有构造函数防止外部直接实例化
421
- constructor(options) {
422
- this.config = {
423
- ...defaultRedLockConfig,
424
- ...options
458
+ var redlock_exports = {};
459
+ __export(redlock_exports, {
460
+ RedLocker: () => RedLocker
461
+ });
462
+ var defaultRedLockConfig, defaultRedlockSettings, RedLocker;
463
+ var init_redlock = __esm({
464
+ "src/locker/redlock.ts"() {
465
+ init_interface();
466
+ init_redis_factory();
467
+ defaultRedLockConfig = {
468
+ lockTimeOut: 1e4,
469
+ clockDriftFactor: 0.01,
470
+ maxRetries: 3,
471
+ retryDelayMs: 200,
472
+ redisConfig: {
473
+ mode: RedisMode.STANDALONE,
474
+ host: "127.0.0.1",
475
+ port: 6379,
476
+ password: "",
477
+ db: 0,
478
+ keyPrefix: "redlock:"
479
+ }
425
480
  };
426
- this.registerInContainer();
427
- }
428
- /**
429
- * Register RedLocker in IOC container
430
- * @private
431
- */
432
- registerInContainer() {
433
- try {
434
- IOCContainer.reg("RedLocker", this, {
435
- type: "COMPONENT",
436
- args: []
437
- });
438
- DefaultLogger.Debug("RedLocker registered in IOC container");
439
- } catch (_error) {
440
- DefaultLogger.Warn("Failed to register RedLocker in IOC container:", _error);
441
- }
442
- }
443
- /**
444
- * Get RedLocker singleton instance with thread-safe initialization
445
- * @static
446
- * @param options - RedLock configuration options (only used for first initialization)
447
- * @returns RedLocker singleton instance
448
- */
449
- static getInstance(options) {
450
- if (!_RedLocker.instance) {
451
- if (_RedLocker.instance === null) {
481
+ defaultRedlockSettings = {
482
+ driftFactor: 0.01,
483
+ retryCount: 3,
484
+ retryDelay: 200,
485
+ retryJitter: 200,
486
+ automaticExtensionThreshold: 500
487
+ };
488
+ RedLocker = class _RedLocker {
489
+ static {
490
+ __name(this, "RedLocker");
491
+ }
492
+ static instance = null;
493
+ static instanceLock = /* @__PURE__ */ Symbol("RedLocker.instanceLock");
494
+ redlock = null;
495
+ redisClient = null;
496
+ config;
497
+ isInitialized = false;
498
+ initializationPromise = null;
499
+ // 私有构造函数防止外部直接实例化
500
+ constructor(options) {
501
+ this.config = {
502
+ ...defaultRedLockConfig,
503
+ ...options
504
+ };
505
+ this.registerInContainer();
506
+ }
507
+ /**
508
+ * Register RedLocker in IOC container
509
+ * @private
510
+ */
511
+ registerInContainer() {
452
512
  try {
453
- const containerInstance = IOCContainer.get("RedLocker", "COMPONENT");
454
- if (containerInstance) {
455
- _RedLocker.instance = containerInstance;
456
- DefaultLogger.Debug("Retrieved existing RedLocker instance from IOC container");
457
- } else {
458
- _RedLocker.instance = new _RedLocker(options);
459
- DefaultLogger.Debug("Created new RedLocker singleton instance");
513
+ const RedLockerClass = this.constructor;
514
+ IOCContainer.saveClass("COMPONENT", RedLockerClass, "RedLocker");
515
+ IOCContainer.setExistingInstance(RedLockerClass, this);
516
+ DefaultLogger.Debug("RedLocker registered in IOC container");
517
+ } catch (_error) {
518
+ DefaultLogger.Warn("Failed to register RedLocker in IOC container:", _error);
519
+ }
520
+ }
521
+ /**
522
+ * Get RedLocker singleton instance with thread-safe initialization
523
+ * @static
524
+ * @param options - RedLock configuration options (only used for first initialization)
525
+ * @returns RedLocker singleton instance
526
+ */
527
+ static getInstance(options) {
528
+ if (!_RedLocker.instance) {
529
+ if (_RedLocker.instance === null) {
530
+ try {
531
+ const containerInstance = IOCContainer.get("RedLocker", "COMPONENT");
532
+ if (containerInstance) {
533
+ _RedLocker.instance = containerInstance;
534
+ DefaultLogger.Debug("Retrieved existing RedLocker instance from IOC container");
535
+ } else {
536
+ _RedLocker.instance = new _RedLocker(options);
537
+ DefaultLogger.Debug("Created new RedLocker singleton instance");
538
+ }
539
+ } catch {
540
+ _RedLocker.instance = new _RedLocker(options);
541
+ DefaultLogger.Debug("Created new RedLocker instance outside IOC container");
542
+ }
460
543
  }
461
- } catch {
462
- _RedLocker.instance = new _RedLocker(options);
463
- DefaultLogger.Debug("Created new RedLocker instance outside IOC container");
544
+ } else if (options) {
545
+ DefaultLogger.Warn("RedLocker instance already exists, ignoring new options. Use updateConfig() to change configuration.");
464
546
  }
547
+ return _RedLocker.instance;
465
548
  }
466
- } else if (options) {
467
- DefaultLogger.Warn("RedLocker instance already exists, ignoring new options. Use updateConfig() to change configuration.");
468
- }
469
- return _RedLocker.instance;
470
- }
471
- /**
472
- * Reset singleton instance (主要用于测试)
473
- * @static
474
- */
475
- static resetInstance() {
476
- if (_RedLocker.instance) {
477
- _RedLocker.instance.close().catch((err) => DefaultLogger.Warn("Error while closing RedLocker instance during reset:", err));
478
- _RedLocker.instance = null;
479
- }
480
- }
481
- /**
482
- * Initialize RedLock with Redis connection
483
- * Uses cached promise to avoid duplicate initialization
484
- * @private
485
- */
486
- async initialize() {
487
- if (this.isInitialized) {
488
- return;
489
- }
490
- if (this.initializationPromise) {
491
- return this.initializationPromise;
492
- }
493
- this.initializationPromise = this.performInitialization();
494
- try {
495
- await this.initializationPromise;
496
- } catch (error) {
497
- this.initializationPromise = null;
498
- this.isInitialized = false;
499
- this.redlock = null;
500
- DefaultLogger.Warn("RedLocker initialization failed, state has been reset for retry");
501
- throw error;
502
- }
503
- }
504
- /**
505
- * 执行实际的初始化操作
506
- * @private
507
- */
508
- async performInitialization() {
509
- try {
510
- if (this.config.redisConfig) {
511
- RedisFactory.validateConfig(this.config.redisConfig);
549
+ /**
550
+ * Reset singleton instance (主要用于测试)
551
+ * @static
552
+ */
553
+ static resetInstance() {
554
+ if (_RedLocker.instance) {
555
+ _RedLocker.instance.close().catch((err) => DefaultLogger.Warn("Error while closing RedLocker instance during reset:", err));
556
+ _RedLocker.instance = null;
557
+ }
512
558
  }
513
- try {
514
- const existingRedis = IOCContainer.get("Redis", "COMPONENT");
515
- if (existingRedis) {
516
- if (existingRedis instanceof RedisClientAdapter) {
517
- this.redisClient = existingRedis;
518
- } else {
519
- this.redisClient = new RedisClientAdapter(existingRedis);
559
+ /**
560
+ * Initialize RedLock with Redis connection
561
+ * Uses cached promise to avoid duplicate initialization
562
+ * @private
563
+ */
564
+ async initialize() {
565
+ if (this.isInitialized) {
566
+ return;
567
+ }
568
+ if (this.initializationPromise) {
569
+ return this.initializationPromise;
570
+ }
571
+ this.initializationPromise = this.performInitialization();
572
+ try {
573
+ await this.initializationPromise;
574
+ } catch (error) {
575
+ this.initializationPromise = null;
576
+ this.isInitialized = false;
577
+ this.redlock = null;
578
+ DefaultLogger.Warn("RedLocker initialization failed, state has been reset for retry");
579
+ throw error;
580
+ }
581
+ }
582
+ /**
583
+ * 执行实际的初始化操作
584
+ * @private
585
+ */
586
+ async performInitialization() {
587
+ try {
588
+ if (this.config.redisConfig) {
589
+ RedisFactory.validateConfig(this.config.redisConfig);
520
590
  }
521
- DefaultLogger.Debug("Using Redis instance from IOC container");
591
+ try {
592
+ const existingRedis = IOCContainer.get("Redis", "COMPONENT");
593
+ if (existingRedis) {
594
+ if (existingRedis instanceof RedisClientAdapter) {
595
+ this.redisClient = existingRedis;
596
+ } else {
597
+ this.redisClient = new RedisClientAdapter(existingRedis);
598
+ }
599
+ DefaultLogger.Debug("Using Redis instance from IOC container");
600
+ }
601
+ } catch {
602
+ }
603
+ if (!this.redisClient && this.config.redisConfig) {
604
+ this.redisClient = RedisFactory.createClient(this.config.redisConfig);
605
+ DefaultLogger.Debug("Created new Redis connection for RedLocker");
606
+ }
607
+ if (!this.redisClient) {
608
+ throw new Error("Failed to initialize Redis connection: no configuration provided");
609
+ }
610
+ const underlyingClient = this.redisClient.getClient();
611
+ const userSettings = this.config;
612
+ const redlockSettings = {
613
+ ...defaultRedlockSettings,
614
+ ...userSettings.driftFactor !== void 0 && {
615
+ driftFactor: userSettings.driftFactor
616
+ },
617
+ ...userSettings.retryCount !== void 0 && {
618
+ retryCount: userSettings.retryCount
619
+ },
620
+ ...userSettings.retryDelay !== void 0 && {
621
+ retryDelay: userSettings.retryDelay
622
+ },
623
+ ...userSettings.retryJitter !== void 0 && {
624
+ retryJitter: userSettings.retryJitter
625
+ },
626
+ ...userSettings.automaticExtensionThreshold !== void 0 && {
627
+ automaticExtensionThreshold: userSettings.automaticExtensionThreshold
628
+ }
629
+ };
630
+ this.redlock = new Redlock([
631
+ underlyingClient
632
+ ], redlockSettings);
633
+ this.redlock.on("clientError", (err) => {
634
+ DefaultLogger.Error("Redis client error in RedLock:", err);
635
+ });
636
+ this.isInitialized = true;
637
+ DefaultLogger.Info("RedLocker initialized successfully");
638
+ } catch (error) {
639
+ this.isInitialized = false;
640
+ DefaultLogger.Error("Failed to initialize RedLocker:", error);
641
+ throw new Error(`RedLocker initialization failed: ${error instanceof Error ? error.message : "Unknown error"}`);
522
642
  }
523
- } catch {
524
643
  }
525
- if (!this.redisClient && this.config.redisConfig) {
526
- this.redisClient = RedisFactory.createClient(this.config.redisConfig);
527
- DefaultLogger.Debug("Created new Redis connection for RedLocker");
644
+ /**
645
+ * Acquire a distributed lock
646
+ * @param resources - Resource identifiers to lock
647
+ * @param ttl - Time to live in milliseconds
648
+ * @returns Promise<Lock>
649
+ */
650
+ async acquire(resources, ttl) {
651
+ if (!Array.isArray(resources) || resources.length === 0) {
652
+ throw new Error("Resources array cannot be empty");
653
+ }
654
+ const lockTtl = ttl || this.config.lockTimeOut;
655
+ if (lockTtl <= 0) {
656
+ throw new Error("Lock TTL must be positive");
657
+ }
658
+ await this.initialize();
659
+ if (!this.redlock) {
660
+ throw new Error("RedLock is not initialized");
661
+ }
662
+ try {
663
+ const prefixedResources = resources.map((resource) => `${this.config.redisConfig.keyPrefix}${resource}`);
664
+ DefaultLogger.Debug(`Acquiring lock for resources: ${prefixedResources.join(", ")} with TTL: ${lockTtl}ms`);
665
+ const lock = await this.redlock.acquire(prefixedResources, lockTtl);
666
+ DefaultLogger.Debug(`Lock acquired successfully for resources: ${prefixedResources.join(", ")}`);
667
+ return lock;
668
+ } catch (error) {
669
+ DefaultLogger.Error(`Failed to acquire lock for resources: ${resources.join(", ")}`, error);
670
+ if (error instanceof Error) {
671
+ error.message = `Lock acquisition failed: ${error.message}`;
672
+ throw error;
673
+ }
674
+ throw new Error(`Lock acquisition failed: Unknown error`);
675
+ }
528
676
  }
529
- if (!this.redisClient) {
530
- throw new Error("Failed to initialize Redis connection: no configuration provided");
677
+ /**
678
+ * Release a lock
679
+ * @param lock - Lock instance to release
680
+ */
681
+ async release(lock) {
682
+ if (!lock) {
683
+ throw new Error("Lock instance is required");
684
+ }
685
+ try {
686
+ await lock.release();
687
+ DefaultLogger.Debug("Lock released successfully");
688
+ } catch (error) {
689
+ DefaultLogger.Error("Failed to release lock:", error);
690
+ if (error instanceof Error) {
691
+ error.message = `Lock release failed: ${error.message}`;
692
+ throw error;
693
+ }
694
+ throw new Error(`Lock release failed: Unknown error`);
695
+ }
531
696
  }
532
- const underlyingClient = this.redisClient.getClient();
533
- const userSettings = this.config;
534
- const redlockSettings = {
535
- ...defaultRedlockSettings,
536
- ...userSettings.driftFactor !== void 0 && {
537
- driftFactor: userSettings.driftFactor
538
- },
539
- ...userSettings.retryCount !== void 0 && {
540
- retryCount: userSettings.retryCount
541
- },
542
- ...userSettings.retryDelay !== void 0 && {
543
- retryDelay: userSettings.retryDelay
544
- },
545
- ...userSettings.retryJitter !== void 0 && {
546
- retryJitter: userSettings.retryJitter
547
- },
548
- ...userSettings.automaticExtensionThreshold !== void 0 && {
549
- automaticExtensionThreshold: userSettings.automaticExtensionThreshold
697
+ /**
698
+ * Extend a lock's TTL
699
+ * @param lock - Lock instance to extend
700
+ * @param ttl - New TTL in milliseconds
701
+ * @returns Extended lock
702
+ */
703
+ async extend(lock, ttl) {
704
+ if (!lock) {
705
+ throw new Error("Lock instance is required");
706
+ }
707
+ if (ttl <= 0) {
708
+ throw new Error("TTL must be positive");
709
+ }
710
+ try {
711
+ const extendedLock = await lock.extend(ttl);
712
+ DefaultLogger.Debug(`Lock extended successfully with TTL: ${ttl}ms`);
713
+ return extendedLock;
714
+ } catch (error) {
715
+ DefaultLogger.Error("Failed to extend lock:", error);
716
+ throw new Error(`Lock extension failed: ${error instanceof Error ? error.message : "Unknown error"}`);
550
717
  }
551
- };
552
- this.redlock = new Redlock([
553
- underlyingClient
554
- ], redlockSettings);
555
- this.redlock.on("clientError", (err) => {
556
- DefaultLogger.Error("Redis client error in RedLock:", err);
557
- });
558
- this.isInitialized = true;
559
- DefaultLogger.Info("RedLocker initialized successfully");
560
- } catch (error) {
561
- this.isInitialized = false;
562
- DefaultLogger.Error("Failed to initialize RedLocker:", error);
563
- throw new Error(`RedLocker initialization failed: ${error instanceof Error ? error.message : "Unknown error"}`);
564
- }
565
- }
566
- /**
567
- * Acquire a distributed lock
568
- * @param resources - Resource identifiers to lock
569
- * @param ttl - Time to live in milliseconds
570
- * @returns Promise<Lock>
571
- */
572
- async acquire(resources, ttl) {
573
- if (!Array.isArray(resources) || resources.length === 0) {
574
- throw new Error("Resources array cannot be empty");
575
- }
576
- const lockTtl = ttl || this.config.lockTimeOut;
577
- if (lockTtl <= 0) {
578
- throw new Error("Lock TTL must be positive");
579
- }
580
- await this.initialize();
581
- if (!this.redlock) {
582
- throw new Error("RedLock is not initialized");
583
- }
584
- try {
585
- const prefixedResources = resources.map((resource) => `${this.config.redisConfig.keyPrefix}${resource}`);
586
- DefaultLogger.Debug(`Acquiring lock for resources: ${prefixedResources.join(", ")} with TTL: ${lockTtl}ms`);
587
- const lock = await this.redlock.acquire(prefixedResources, lockTtl);
588
- DefaultLogger.Debug(`Lock acquired successfully for resources: ${prefixedResources.join(", ")}`);
589
- return lock;
590
- } catch (error) {
591
- DefaultLogger.Error(`Failed to acquire lock for resources: ${resources.join(", ")}`, error);
592
- if (error instanceof Error) {
593
- error.message = `Lock acquisition failed: ${error.message}`;
594
- throw error;
595
718
  }
596
- throw new Error(`Lock acquisition failed: Unknown error`);
597
- }
598
- }
599
- /**
600
- * Release a lock
601
- * @param lock - Lock instance to release
602
- */
603
- async release(lock) {
604
- if (!lock) {
605
- throw new Error("Lock instance is required");
606
- }
607
- try {
608
- await lock.release();
609
- DefaultLogger.Debug("Lock released successfully");
610
- } catch (error) {
611
- DefaultLogger.Error("Failed to release lock:", error);
612
- if (error instanceof Error) {
613
- error.message = `Lock release failed: ${error.message}`;
614
- throw error;
719
+ /**
720
+ * Check if RedLocker is initialized
721
+ * @returns true if initialized, false otherwise
722
+ */
723
+ isReady() {
724
+ return this.isInitialized && !!this.redlock && !!this.redisClient;
615
725
  }
616
- throw new Error(`Lock release failed: Unknown error`);
617
- }
618
- }
619
- /**
620
- * Extend a lock's TTL
621
- * @param lock - Lock instance to extend
622
- * @param ttl - New TTL in milliseconds
623
- * @returns Extended lock
624
- */
625
- async extend(lock, ttl) {
626
- if (!lock) {
627
- throw new Error("Lock instance is required");
628
- }
629
- if (ttl <= 0) {
630
- throw new Error("TTL must be positive");
631
- }
632
- try {
633
- const extendedLock = await lock.extend(ttl);
634
- DefaultLogger.Debug(`Lock extended successfully with TTL: ${ttl}ms`);
635
- return extendedLock;
636
- } catch (error) {
637
- DefaultLogger.Error("Failed to extend lock:", error);
638
- throw new Error(`Lock extension failed: ${error instanceof Error ? error.message : "Unknown error"}`);
639
- }
640
- }
641
- /**
642
- * Check if RedLocker is initialized
643
- * @returns true if initialized, false otherwise
644
- */
645
- isReady() {
646
- return this.isInitialized && !!this.redlock && !!this.redisClient;
647
- }
648
- /**
649
- * Get current configuration
650
- * @returns Current RedLock configuration
651
- */
652
- getConfig() {
653
- return {
654
- ...this.config
655
- };
656
- }
657
- /**
658
- * Update configuration (requires reinitialization)
659
- * @param options - New RedLock options
660
- */
661
- updateConfig(options) {
662
- if (options) {
663
- this.config = {
664
- ...this.config,
665
- ...options
666
- };
667
- }
668
- this.isInitialized = false;
669
- this.initializationPromise = null;
670
- this.redlock = null;
671
- DefaultLogger.Debug("RedLocker configuration updated, will reinitialize on next use");
672
- }
673
- /**
674
- * Close Redis connection and cleanup
675
- */
676
- async close() {
677
- try {
678
- if (this.redisClient && this.redisClient.status === "ready") {
679
- await this.redisClient.quit();
680
- DefaultLogger.Debug("Redis connection closed");
726
+ /**
727
+ * Get current configuration
728
+ * @returns Current RedLock configuration
729
+ */
730
+ getConfig() {
731
+ return {
732
+ ...this.config
733
+ };
681
734
  }
682
- this.redisClient = null;
683
- this.redlock = null;
684
- this.isInitialized = false;
685
- } catch (error) {
686
- DefaultLogger.Error("Error closing RedLocker:", error);
687
- }
688
- }
689
- /**
690
- * Get container registration status
691
- * @returns Registration information
692
- */
693
- getContainerInfo() {
694
- try {
695
- const instance = IOCContainer.get("RedLocker", "COMPONENT");
696
- return {
697
- registered: !!instance,
698
- identifier: "RedLocker"
699
- };
700
- } catch {
701
- return {
702
- registered: false,
703
- identifier: "RedLocker"
704
- };
705
- }
706
- }
707
- /**
708
- * Health check for RedLocker
709
- * @returns Health status
710
- */
711
- async healthCheck() {
712
- try {
713
- await this.initialize();
714
- const redisStatus = this.redisClient?.status || "unknown";
715
- const isReady = this.isReady();
716
- return {
717
- status: isReady ? "healthy" : "unhealthy",
718
- details: {
719
- initialized: this.isInitialized,
720
- redisStatus,
721
- redisMode: this.config.redisConfig?.mode || "unknown",
722
- redlockReady: !!this.redlock,
723
- containerRegistered: this.getContainerInfo().registered
735
+ /**
736
+ * Update configuration (requires reinitialization)
737
+ * @param options - New RedLock options
738
+ */
739
+ updateConfig(options) {
740
+ if (options) {
741
+ this.config = {
742
+ ...this.config,
743
+ ...options
744
+ };
724
745
  }
725
- };
726
- } catch (error) {
727
- return {
728
- status: "unhealthy",
729
- details: {
730
- error: error instanceof Error ? error.message : "Unknown error",
731
- initialized: this.isInitialized
746
+ this.isInitialized = false;
747
+ this.initializationPromise = null;
748
+ this.redlock = null;
749
+ DefaultLogger.Debug("RedLocker configuration updated, will reinitialize on next use");
750
+ }
751
+ /**
752
+ * Close Redis connection and cleanup
753
+ */
754
+ async close() {
755
+ try {
756
+ if (this.redisClient && this.redisClient.status === "ready") {
757
+ await this.redisClient.quit();
758
+ DefaultLogger.Debug("Redis connection closed");
759
+ }
760
+ this.redisClient = null;
761
+ this.redlock = null;
762
+ this.isInitialized = false;
763
+ } catch (error) {
764
+ DefaultLogger.Error("Error closing RedLocker:", error);
732
765
  }
733
- };
734
- }
766
+ }
767
+ /**
768
+ * Get container registration status
769
+ * @returns Registration information
770
+ */
771
+ getContainerInfo() {
772
+ try {
773
+ const instance = IOCContainer.get("RedLocker", "COMPONENT");
774
+ return {
775
+ registered: !!instance,
776
+ identifier: "RedLocker"
777
+ };
778
+ } catch {
779
+ return {
780
+ registered: false,
781
+ identifier: "RedLocker"
782
+ };
783
+ }
784
+ }
785
+ /**
786
+ * Health check for RedLocker
787
+ * @returns Health status
788
+ */
789
+ async healthCheck() {
790
+ try {
791
+ await this.initialize();
792
+ const redisStatus = this.redisClient?.status || "unknown";
793
+ const isReady = this.isReady();
794
+ return {
795
+ status: isReady ? "healthy" : "unhealthy",
796
+ details: {
797
+ initialized: this.isInitialized,
798
+ redisStatus,
799
+ redisMode: this.config.redisConfig?.mode || "unknown",
800
+ redlockReady: !!this.redlock,
801
+ containerRegistered: this.getContainerInfo().registered
802
+ }
803
+ };
804
+ } catch (error) {
805
+ return {
806
+ status: "unhealthy",
807
+ details: {
808
+ error: error instanceof Error ? error.message : "Unknown error",
809
+ initialized: this.isInitialized
810
+ }
811
+ };
812
+ }
813
+ }
814
+ };
735
815
  }
736
- };
816
+ });
737
817
 
738
818
  // src/utils/lib.ts
819
+ var lib_exports = {};
820
+ __export(lib_exports, {
821
+ timeoutPromise: () => timeoutPromise,
822
+ wrappedPromise: () => wrappedPromise
823
+ });
739
824
  function timeoutPromise(ms) {
740
825
  let timeoutId = null;
741
826
  const promise = new Promise((resolve, reject) => {
@@ -752,9 +837,30 @@ function timeoutPromise(ms) {
752
837
  };
753
838
  return promise;
754
839
  }
755
- __name(timeoutPromise, "timeoutPromise");
840
+ function wrappedPromise(fn, args) {
841
+ return new Promise((resolve, reject) => {
842
+ try {
843
+ const result = fn(...args);
844
+ resolve(result);
845
+ } catch (error) {
846
+ reject(error);
847
+ }
848
+ });
849
+ }
850
+ var init_lib = __esm({
851
+ "src/utils/lib.ts"() {
852
+ __name(timeoutPromise, "timeoutPromise");
853
+ __name(wrappedPromise, "wrappedPromise");
854
+ }
855
+ });
856
+
857
+ // src/decorator/redlock.ts
858
+ init_config();
756
859
 
757
860
  // src/process/locker.ts
861
+ init_redlock();
862
+ init_lib();
863
+ init_config();
758
864
  async function initRedLock(options, app) {
759
865
  if (!app || !Helper.isFunction(app.once)) {
760
866
  DefaultLogger.Warn(`RedLock initialization skipped: Koatty app not available or not initialized`);
@@ -878,33 +984,89 @@ __name(generateLockName, "generateLockName");
878
984
 
879
985
  // src/decorator/redlock.ts
880
986
  function RedLock(lockName, options) {
881
- return (target, propertyKey, descriptor) => {
882
- const methodName = propertyKey.toString();
883
- const targetClass = target.constructor;
884
- const componentType = IOCContainer.getType(targetClass);
885
- if (componentType !== "SERVICE" && componentType !== "COMPONENT") {
886
- throw Error("@RedLock decorator can only be used on SERVICE or COMPONENT classes.");
887
- }
888
- if (!methodName || typeof methodName !== "string") {
889
- throw Error("Method name is required for @RedLock decorator");
890
- }
891
- if (!descriptor || typeof descriptor.value !== "function") {
892
- throw Error("@RedLock decorator can only be applied to methods");
893
- }
894
- const finalLockName = lockName || generateLockName(lockName, methodName, target);
895
- if (options) {
896
- validateRedLockMethodOptions(options);
897
- }
898
- IOCContainer.saveClass("COMPONENT", targetClass, targetClass.name);
899
- try {
900
- const enhancedDescriptor = redLockerDescriptor(descriptor, finalLockName, methodName, options);
901
- return enhancedDescriptor;
902
- } catch (error) {
903
- throw new Error(`Failed to apply RedLock to ${methodName}: ${error.message}`);
987
+ return IOCContainer.createDecorator(({ target, methodName, descriptor, method, context }) => {
988
+ if (context) {
989
+ if (!methodName || typeof methodName !== "string") {
990
+ throw Error("Method name is required for @RedLock decorator");
991
+ }
992
+ if (options) {
993
+ validateRedLockMethodOptions(options);
994
+ }
995
+ context.addInitializer?.(function() {
996
+ const targetClass = this.constructor;
997
+ const componentType = IOCContainer.getType(targetClass);
998
+ if (componentType !== "SERVICE" && componentType !== "COMPONENT") {
999
+ throw Error("@RedLock decorator can only be used on SERVICE or COMPONENT classes.");
1000
+ }
1001
+ IOCContainer.saveClass("COMPONENT", targetClass, targetClass.name);
1002
+ });
1003
+ const originalMethod = method;
1004
+ return async function(...props) {
1005
+ try {
1006
+ const { RedLocker: RedLocker2 } = await Promise.resolve().then(() => (init_redlock(), redlock_exports));
1007
+ const { getEffectiveRedLockOptions: getEffectiveRedLockOptions2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1008
+ const { timeoutPromise: timeoutPromise2 } = await Promise.resolve().then(() => (init_lib(), lib_exports));
1009
+ const { Lock } = await import('@sesamecare-oss/redlock');
1010
+ const resolvedLockName = lockName || generateLockName(lockName, methodName, Object.getPrototypeOf(this));
1011
+ const redlock = RedLocker2.getInstance();
1012
+ const lockOptions = getEffectiveRedLockOptions2(options);
1013
+ const lockTime = lockOptions.lockTimeOut || 1e4;
1014
+ if (lockTime <= 200) {
1015
+ throw new Error("Lock timeout must be greater than 200ms to allow for proper execution");
1016
+ }
1017
+ const lock = await redlock.acquire([
1018
+ methodName,
1019
+ resolvedLockName
1020
+ ], lockTime);
1021
+ const timeout = lockTime - 200;
1022
+ try {
1023
+ const result = await Promise.race([
1024
+ originalMethod.apply(this, props),
1025
+ timeoutPromise2(timeout)
1026
+ ]);
1027
+ return result;
1028
+ } catch (error) {
1029
+ throw error;
1030
+ } finally {
1031
+ try {
1032
+ await lock.release();
1033
+ } catch (releaseError) {
1034
+ }
1035
+ }
1036
+ } catch (error) {
1037
+ throw error;
1038
+ }
1039
+ };
1040
+ } else {
1041
+ const targetClass = target.constructor;
1042
+ const componentType = IOCContainer.getType(targetClass);
1043
+ if (componentType !== "SERVICE" && componentType !== "COMPONENT") {
1044
+ throw Error("@RedLock decorator can only be used on SERVICE or COMPONENT classes.");
1045
+ }
1046
+ if (!methodName || typeof methodName !== "string") {
1047
+ throw Error("Method name is required for @RedLock decorator");
1048
+ }
1049
+ if (!descriptor || typeof descriptor.value !== "function") {
1050
+ throw Error("@RedLock decorator can only be applied to methods");
1051
+ }
1052
+ const finalLockName = lockName || generateLockName(lockName, methodName, target);
1053
+ if (options) {
1054
+ validateRedLockMethodOptions(options);
1055
+ }
1056
+ IOCContainer.saveClass("COMPONENT", targetClass, targetClass.name);
1057
+ try {
1058
+ const enhancedDescriptor = redLockerDescriptor(descriptor, finalLockName, methodName, options);
1059
+ return enhancedDescriptor;
1060
+ } catch (error) {
1061
+ throw new Error(`Failed to apply RedLock to ${methodName}: ${error.message}`);
1062
+ }
904
1063
  }
905
- };
1064
+ }, "method");
906
1065
  }
907
1066
  __name(RedLock, "RedLock");
1067
+
1068
+ // src/decorator/scheduled.ts
1069
+ init_config();
908
1070
  function Scheduled(cron, timezone = "Asia/Beijing") {
909
1071
  if (Helper.isEmpty(cron)) {
910
1072
  throw Error("Cron expression is required and cannot be empty");
@@ -917,28 +1079,50 @@ function Scheduled(cron, timezone = "Asia/Beijing") {
917
1079
  if (timezone && typeof timezone !== "string") {
918
1080
  throw Error("Timezone must be a string");
919
1081
  }
920
- return (target, propertyKey, descriptor) => {
921
- const targetClass = target.constructor;
922
- const componentType = IOCContainer.getType(targetClass);
923
- if (componentType !== "SERVICE" && componentType !== "COMPONENT") {
924
- throw Error("@Scheduled decorator can only be used on SERVICE or COMPONENT classes.");
925
- }
926
- const methodName = propertyKey.toString();
927
- if (!methodName || typeof methodName !== "string") {
928
- throw Error("Method name is required for @Scheduled decorator");
929
- }
930
- if (!descriptor || typeof descriptor.value !== "function") {
931
- throw Error("@Scheduled decorator can only be applied to methods");
1082
+ return IOCContainer.createDecorator(({ target, methodName, descriptor, method, context }) => {
1083
+ if (context) {
1084
+ context.addInitializer?.(function() {
1085
+ const targetClass = this.constructor;
1086
+ const componentType = IOCContainer.getType(targetClass);
1087
+ if (componentType !== "SERVICE" && componentType !== "COMPONENT") {
1088
+ throw Error("@Scheduled decorator can only be used on SERVICE or COMPONENT classes.");
1089
+ }
1090
+ if (!methodName || typeof methodName !== "string") {
1091
+ throw Error("Method name is required for @Scheduled decorator");
1092
+ }
1093
+ IOCContainer.saveClass("COMPONENT", targetClass, targetClass.name);
1094
+ IOCContainer.attachClassMetadata(COMPONENT_SCHEDULED, DecoratorType.SCHEDULED, {
1095
+ method: methodName,
1096
+ cron,
1097
+ timezone
1098
+ }, this, methodName);
1099
+ });
1100
+ return method;
1101
+ } else {
1102
+ const targetClass = target.constructor;
1103
+ const componentType = IOCContainer.getType(targetClass);
1104
+ if (componentType !== "SERVICE" && componentType !== "COMPONENT") {
1105
+ throw Error("@Scheduled decorator can only be used on SERVICE or COMPONENT classes.");
1106
+ }
1107
+ if (!methodName || typeof methodName !== "string") {
1108
+ throw Error("Method name is required for @Scheduled decorator");
1109
+ }
1110
+ if (!descriptor || typeof descriptor.value !== "function") {
1111
+ throw Error("@Scheduled decorator can only be applied to methods");
1112
+ }
1113
+ IOCContainer.saveClass("COMPONENT", targetClass, targetClass.name);
1114
+ IOCContainer.attachClassMetadata(COMPONENT_SCHEDULED, DecoratorType.SCHEDULED, {
1115
+ method: methodName,
1116
+ cron,
1117
+ timezone
1118
+ }, target, methodName);
932
1119
  }
933
- IOCContainer.saveClass("COMPONENT", targetClass, targetClass.name);
934
- IOCContainer.attachClassMetadata(COMPONENT_SCHEDULED, DecoratorType.SCHEDULED, {
935
- method: methodName,
936
- cron,
937
- timezone
938
- }, target, methodName);
939
- };
1120
+ }, "method");
940
1121
  }
941
1122
  __name(Scheduled, "Scheduled");
1123
+
1124
+ // src/process/schedule.ts
1125
+ init_config();
942
1126
  async function initSchedule(options, app) {
943
1127
  if (!app || !Helper.isFunction(app.once)) {
944
1128
  DefaultLogger.Warn(`Schedule initialization skipped: Koatty app not available or not initialized`);
@@ -956,54 +1140,45 @@ __name(initSchedule, "initSchedule");
956
1140
  async function injectSchedule(options) {
957
1141
  try {
958
1142
  DefaultLogger.Debug("Starting batch schedule injection...");
1143
+ let totalScheduled = 0;
959
1144
  const componentList = IOCContainer.listClass("COMPONENT");
960
1145
  for (const component of componentList) {
961
1146
  const classMetadata = IOCContainer.getClassMetadata(COMPONENT_SCHEDULED, DecoratorType.SCHEDULED, component.target);
962
- if (!classMetadata) {
1147
+ if (!classMetadata || !Array.isArray(classMetadata)) {
1148
+ continue;
1149
+ }
1150
+ const instance = IOCContainer.get(component.id);
1151
+ if (!instance) {
963
1152
  continue;
964
1153
  }
965
- let scheduledCount = 0;
966
- for (const [className, metadata] of classMetadata) {
1154
+ for (const scheduleData of classMetadata) {
967
1155
  try {
968
- const instance = IOCContainer.get(className);
969
- if (!instance) {
1156
+ if (!scheduleData || !scheduleData.method) {
970
1157
  continue;
971
1158
  }
972
- for (const [key, value] of Object.entries(metadata)) {
973
- if (key.startsWith("SCHEDULED")) {
974
- const scheduleData = value;
975
- const targetMethod = instance[scheduleData.method];
976
- if (!Helper.isFunction(targetMethod)) {
977
- DefaultLogger.Warn(`Schedule injection skipped: method ${scheduleData.method} is not a function in ${className}`);
978
- continue;
979
- }
980
- const taskName = `${className}_${scheduleData.method}`;
981
- const tz = getEffectiveTimezone(options, scheduleData.timezone);
982
- new CronJob(
983
- scheduleData.cron,
984
- () => {
985
- DefaultLogger.Debug(`The schedule job ${taskName} started.`);
986
- Promise.resolve(targetMethod.call(instance)).then(() => {
987
- DefaultLogger.Debug(`The schedule job ${taskName} completed.`);
988
- }).catch((error) => {
989
- DefaultLogger.Error(`The schedule job ${taskName} failed:`, error);
990
- });
991
- },
992
- null,
993
- true,
994
- tz
995
- // timeZone
996
- );
997
- scheduledCount++;
998
- DefaultLogger.Debug(`Schedule job ${taskName} registered with cron: ${scheduleData.cron}`);
999
- }
1159
+ const targetMethod = instance[scheduleData.method];
1160
+ if (!Helper.isFunction(targetMethod)) {
1161
+ DefaultLogger.Warn(`Schedule injection skipped: method ${scheduleData.method} is not a function in ${component.id}`);
1162
+ continue;
1000
1163
  }
1164
+ const taskName = `${component.id}_${scheduleData.method}`;
1165
+ const tz = getEffectiveTimezone(options, scheduleData.timezone);
1166
+ new CronJob(scheduleData.cron, () => {
1167
+ DefaultLogger.Debug(`The schedule job ${taskName} started.`);
1168
+ Promise.resolve(targetMethod.call(instance)).then(() => {
1169
+ DefaultLogger.Debug(`The schedule job ${taskName} completed.`);
1170
+ }).catch((error) => {
1171
+ DefaultLogger.Error(`The schedule job ${taskName} failed:`, error);
1172
+ });
1173
+ }, null, true, tz);
1174
+ totalScheduled++;
1175
+ DefaultLogger.Debug(`Schedule job ${taskName} registered with cron: ${scheduleData.cron}`);
1001
1176
  } catch (error) {
1002
- DefaultLogger.Error(`Failed to process class ${className}:`, error);
1177
+ DefaultLogger.Error(`Failed to process schedule for ${component.id}:`, error);
1003
1178
  }
1004
1179
  }
1005
- DefaultLogger.Info(`Batch schedule injection completed. ${scheduledCount} jobs registered.`);
1006
1180
  }
1181
+ DefaultLogger.Info(`Batch schedule injection completed. ${totalScheduled} jobs registered.`);
1007
1182
  } catch (error) {
1008
1183
  DefaultLogger.Error("Failed to inject schedules:", error);
1009
1184
  }
@@ -1011,6 +1186,10 @@ async function injectSchedule(options) {
1011
1186
  __name(injectSchedule, "injectSchedule");
1012
1187
 
1013
1188
  // src/index.ts
1189
+ init_interface();
1190
+ init_redlock();
1191
+ init_interface();
1192
+ init_redis_factory();
1014
1193
  var SchedulerLock = RedLock;
1015
1194
  var defaultOptions = {
1016
1195
  timezone: "Asia/Beijing",