@fluojs/cron 1.0.0-beta.1 → 1.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/dist/distributed-lock-manager.d.ts +4 -1
- package/dist/distributed-lock-manager.d.ts.map +1 -1
- package/dist/distributed-lock-manager.js +12 -2
- package/dist/service.d.ts +2 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +7 -1
- package/package.json +4 -4
package/README.ko.md
CHANGED
|
@@ -141,7 +141,7 @@ class TaskManager {
|
|
|
141
141
|
|
|
142
142
|
`CronModule`은 애플리케이션 종료 시 실행 중인 작업을 drain하지만, 이제는 제한된 타임아웃 안에서만 기다립니다. 따라서 하나의 hung task 때문에 프로세스 종료가 영원히 막히지 않습니다.
|
|
143
143
|
|
|
144
|
-
기본적으로 shutdown drain은 최대 `10_000ms` 동안 기다립니다. 이 시간이 지나면 스케줄러는 경고 로그를 남기고 hung task가 끝나기를 더 기다리지 않은 채 종료를 계속합니다.
|
|
144
|
+
기본적으로 shutdown drain은 최대 `10_000ms` 동안 기다립니다. 이 시간이 지나면 스케줄러는 경고 로그를 남기고 hung task가 끝나기를 더 기다리지 않은 채 종료를 계속합니다. 분산 락을 사용하는 경우 아직 실행 중인 작업이 보유한 락은 timeout 시점에 즉시 해제하지 않습니다. 해당 작업이 정상적으로 끝날 때까지 락 소유권을 유지하거나, 프로세스가 종료된 뒤 Redis TTL로 만료되게 두어 원래 작업이 아직 실행 중인데 다른 노드가 같은 작업을 시작하지 않도록 합니다.
|
|
145
145
|
|
|
146
146
|
```typescript
|
|
147
147
|
@Module({
|
package/README.md
CHANGED
|
@@ -141,7 +141,7 @@ class TaskManager {
|
|
|
141
141
|
|
|
142
142
|
`CronModule` drains active task executions during application shutdown, but it now does so with a bounded timeout so one hung task cannot block process termination forever.
|
|
143
143
|
|
|
144
|
-
By default the shutdown drain waits up to `10_000ms`. If that timeout expires, the scheduler logs a warning and continues shutdown without waiting for the hung task to settle.
|
|
144
|
+
By default the shutdown drain waits up to `10_000ms`. If that timeout expires, the scheduler logs a warning and continues shutdown without waiting for the hung task to settle. When distributed locking is enabled, locks held by still-running tasks are not eagerly released on timeout; they remain owned by that task until it settles normally, or until Redis expires the lock after the process exits. This prevents another node from starting the same job while the original task is still running.
|
|
145
145
|
|
|
146
146
|
```typescript
|
|
147
147
|
@Module({
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import type { Container } from '@fluojs/di';
|
|
2
2
|
import type { ApplicationLogger } from '@fluojs/runtime';
|
|
3
3
|
import type { CronTaskDescriptor, NormalizedCronModuleOptions } from './types.js';
|
|
4
|
+
/** Minimal Redis command surface required for distributed cron locks. */
|
|
4
5
|
export interface RedisLockClient {
|
|
5
6
|
eval(script: string, keysLength: number, ...keysAndArgs: string[]): Promise<unknown>;
|
|
6
7
|
set(key: string, value: string, mode: 'PX', ttl: number, existence: 'NX'): Promise<'OK' | null | undefined>;
|
|
7
8
|
}
|
|
9
|
+
/** Tracks renewal state for one acquired distributed cron lock. */
|
|
8
10
|
export interface LockRenewalMonitor {
|
|
9
11
|
getPostRunError(): Promise<Error | undefined>;
|
|
10
12
|
stop(): void;
|
|
11
13
|
}
|
|
14
|
+
/** Coordinates Redis lock acquisition, renewal, and release for scheduled cron tasks. */
|
|
12
15
|
export declare class CronDistributedLockManager {
|
|
13
16
|
private readonly options;
|
|
14
17
|
private readonly runtimeContainer;
|
|
@@ -27,7 +30,7 @@ export declare class CronDistributedLockManager {
|
|
|
27
30
|
tryAcquireLock(descriptor: CronTaskDescriptor): Promise<boolean>;
|
|
28
31
|
startLockRenewalMonitor(descriptor: CronTaskDescriptor): LockRenewalMonitor;
|
|
29
32
|
releaseLock(descriptor: CronTaskDescriptor): Promise<void>;
|
|
30
|
-
releaseOwnedLocks(): Promise<void>;
|
|
33
|
+
releaseOwnedLocks(excludedLockKeys?: ReadonlySet<string>): Promise<void>;
|
|
31
34
|
private createLockRenewalState;
|
|
32
35
|
private queueDueLockRenewalAttempts;
|
|
33
36
|
private runLockRenewalAttempt;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"distributed-lock-manager.d.ts","sourceRoot":"","sources":["../src/distributed-lock-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAElF,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrF,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CAC7G;AAED,MAAM,WAAW,kBAAkB;IACjC,eAAe,IAAI,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IAC9C,IAAI,IAAI,IAAI,CAAC;CACd;AAiBD,qBAAa,0BAA0B;IAOnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM;IARzB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,mBAAmB,CAAK;gBAGb,OAAO,EAAE,2BAA2B,EACpC,gBAAgB,EAAE,SAAS,EAC3B,MAAM,EAAE,iBAAiB;IAG5C,IAAI,cAAc,IAAI,eAAe,GAAG,SAAS,CAEhD;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;IAEK,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBpC,KAAK,IAAI,IAAI;IAIP,cAAc,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IA+BtE,uBAAuB,CAAC,UAAU,EAAE,kBAAkB,GAAG,kBAAkB;IA8BrE,WAAW,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D,iBAAiB,
|
|
1
|
+
{"version":3,"file":"distributed-lock-manager.d.ts","sourceRoot":"","sources":["../src/distributed-lock-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAElF,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrF,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CAC7G;AAED,mEAAmE;AACnE,MAAM,WAAW,kBAAkB;IACjC,eAAe,IAAI,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IAC9C,IAAI,IAAI,IAAI,CAAC;CACd;AAiBD,yFAAyF;AACzF,qBAAa,0BAA0B;IAOnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM;IARzB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,mBAAmB,CAAK;gBAGb,OAAO,EAAE,2BAA2B,EACpC,gBAAgB,EAAE,SAAS,EAC3B,MAAM,EAAE,iBAAiB;IAG5C,IAAI,cAAc,IAAI,eAAe,GAAG,SAAS,CAEhD;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;IAEK,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBpC,KAAK,IAAI,IAAI;IAIP,cAAc,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IA+BtE,uBAAuB,CAAC,UAAU,EAAE,kBAAkB,GAAG,kBAAkB;IA8BrE,WAAW,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D,iBAAiB,CAAC,gBAAgB,GAAE,WAAW,CAAC,MAAM,CAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBzF,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,2BAA2B;YAcrB,qBAAqB;IAqBnC,OAAO,CAAC,kBAAkB;YAYZ,SAAS;YAwCT,cAAc;CAgC7B"}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { getRedisClientToken } from '@fluojs/redis';
|
|
2
|
+
|
|
3
|
+
/** Minimal Redis command surface required for distributed cron locks. */
|
|
4
|
+
|
|
5
|
+
/** Tracks renewal state for one acquired distributed cron lock. */
|
|
6
|
+
|
|
2
7
|
const RELEASE_LOCK_SCRIPT = 'if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end';
|
|
3
8
|
const RENEW_LOCK_SCRIPT = 'if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("PEXPIRE", KEYS[1], ARGV[2]) else return 0 end';
|
|
9
|
+
|
|
10
|
+
/** Coordinates Redis lock acquisition, renewal, and release for scheduled cron tasks. */
|
|
4
11
|
export class CronDistributedLockManager {
|
|
5
12
|
ownedLockKeys = new Set();
|
|
6
13
|
redisClient;
|
|
@@ -85,11 +92,14 @@ export class CronDistributedLockManager {
|
|
|
85
92
|
async releaseLock(descriptor) {
|
|
86
93
|
await this.releaseLockKey(descriptor.lockKey, descriptor.taskName);
|
|
87
94
|
}
|
|
88
|
-
async releaseOwnedLocks() {
|
|
95
|
+
async releaseOwnedLocks(excludedLockKeys = new Set()) {
|
|
89
96
|
if (!this.redisClient || this.ownedLockKeys.size === 0) {
|
|
90
97
|
return;
|
|
91
98
|
}
|
|
92
|
-
const lockKeys = Array.from(this.ownedLockKeys);
|
|
99
|
+
const lockKeys = Array.from(this.ownedLockKeys).filter(lockKey => !excludedLockKeys.has(lockKey));
|
|
100
|
+
if (lockKeys.length === 0) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
93
103
|
await Promise.all(lockKeys.map(async lockKey => {
|
|
94
104
|
await this.releaseLockKey(lockKey, lockKey);
|
|
95
105
|
}));
|
package/dist/service.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare class CronLifecycleService implements SchedulingRegistry, OnAppli
|
|
|
15
15
|
private readonly logger;
|
|
16
16
|
private readonly tasks;
|
|
17
17
|
private readonly activeTasks;
|
|
18
|
+
private readonly runningDistributedLockKeys;
|
|
18
19
|
private readonly distributedLocks;
|
|
19
20
|
private readonly taskRunner;
|
|
20
21
|
private lifecycleState;
|
|
@@ -99,6 +100,7 @@ export declare class CronLifecycleService implements SchedulingRegistry, OnAppli
|
|
|
99
100
|
private validateDistributedLockConfiguration;
|
|
100
101
|
private handleStartupFailure;
|
|
101
102
|
private runShutdownLifecycle;
|
|
103
|
+
private getRunningDistributedLockKeys;
|
|
102
104
|
private registerDecoratorTasks;
|
|
103
105
|
private registerTask;
|
|
104
106
|
private assertTaskNameAvailable;
|
package/dist/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAC;AAQzB,OAAO,KAAK,EAEV,eAAe,EACf,mBAAmB,EACnB,2BAA2B,EAC3B,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,EACxB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAwCpB;;;;;;GAMG;AACH,qBACa,oBACX,YAAW,kBAAkB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,eAAe;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAC;AAQzB,OAAO,KAAK,EAEV,eAAe,EACf,mBAAmB,EACnB,2BAA2B,EAC3B,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,EACxB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAwCpB;;;;;;GAMG;AACH,qBACa,oBACX,YAAW,kBAAkB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,eAAe;IAY3F,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAbzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAC7D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA4B;IACxD,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAqB;IAChE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAC9D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiB;IAC5C,OAAO,CAAC,cAAc,CAAmF;IACzG,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,eAAe,CAA4B;gBAGhC,OAAO,EAAE,2BAA2B,EACpC,gBAAgB,EAAE,SAAS,EAC3B,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,MAAM,EAAE,iBAAiB;IAM5C;;;;;;;OAOG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,sBAAsB,EAAE,OAAO,GAAE,eAAoB,GAAG,IAAI;IAuBhH;;;;;;;OAOG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,sBAAsB,EAAE,OAAO,GAAE,mBAAwB,GAAG,IAAI;IAsBhH;;;;;;;OAOG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,sBAAsB,EAAE,OAAO,GAAE,kBAAuB,GAAG,IAAI;IAsB9G;;;;;OAKG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAY7B;;;;;OAKG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAoB7B;;;;;OAKG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAgB9B;;;;;OAKG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS;IAMvD;;;;OAIG;IACH,MAAM,IAAI,wBAAwB,EAAE;IAIpC;;;;;OAKG;IACH,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAuBtD,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBvC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,4BAA4B;IA6B5B,OAAO,CAAC,0BAA0B;YAkBpB,QAAQ;YAWR,cAAc;IAQ5B,OAAO,CAAC,oCAAoC;IAQ5C,OAAO,CAAC,oBAAoB;YAOd,oBAAoB;IAmBlC,OAAO,CAAC,6BAA6B;IAIrC,OAAO,CAAC,sBAAsB;IAQ9B,OAAO,CAAC,YAAY;IAsBpB,OAAO,CAAC,uBAAuB;IAM/B,OAAO,CAAC,oBAAoB;IAQ5B,OAAO,CAAC,YAAY;IA4DpB,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,cAAc;YAcR,cAAc;YAmBd,WAAW;IASzB,OAAO,CAAC,6BAA6B;YAIvB,sBAAsB;YAsBtB,kBAAkB;YA2BlB,gBAAgB;YAMhB,WAAW;IAYzB,OAAO,CAAC,qBAAqB;CAK9B"}
|
package/dist/service.js
CHANGED
|
@@ -52,6 +52,7 @@ class CronLifecycleService {
|
|
|
52
52
|
}
|
|
53
53
|
tasks = new Map();
|
|
54
54
|
activeTasks = new Set();
|
|
55
|
+
runningDistributedLockKeys = new Set();
|
|
55
56
|
distributedLocks;
|
|
56
57
|
taskRunner;
|
|
57
58
|
lifecycleState = 'created';
|
|
@@ -340,9 +341,12 @@ class CronLifecycleService {
|
|
|
340
341
|
if (shutdownTimedOut) {
|
|
341
342
|
this.logger.warn(`Cron shutdown timed out after ${String(this.options.shutdown.timeoutMs)}ms with ${String(this.activeTasks.size)} active task(s) still pending.`, 'CronLifecycleService');
|
|
342
343
|
}
|
|
343
|
-
await this.distributedLocks.releaseOwnedLocks();
|
|
344
|
+
await this.distributedLocks.releaseOwnedLocks(shutdownTimedOut ? this.getRunningDistributedLockKeys() : new Set());
|
|
344
345
|
this.lifecycleState = 'stopped';
|
|
345
346
|
}
|
|
347
|
+
getRunningDistributedLockKeys() {
|
|
348
|
+
return new Set(this.runningDistributedLockKeys);
|
|
349
|
+
}
|
|
346
350
|
registerDecoratorTasks() {
|
|
347
351
|
const descriptors = discoverCronTaskDescriptors(this.compiledModules, this.options, this.logger);
|
|
348
352
|
for (const descriptor of descriptors) {
|
|
@@ -474,6 +478,7 @@ class CronLifecycleService {
|
|
|
474
478
|
return;
|
|
475
479
|
}
|
|
476
480
|
const lockRenewalMonitor = this.distributedLocks.startLockRenewalMonitor(descriptor);
|
|
481
|
+
this.runningDistributedLockKeys.add(descriptor.lockKey);
|
|
477
482
|
try {
|
|
478
483
|
await this.executeTask(descriptor, taskState, async () => {
|
|
479
484
|
lockRenewalMonitor.stop();
|
|
@@ -482,6 +487,7 @@ class CronLifecycleService {
|
|
|
482
487
|
} finally {
|
|
483
488
|
lockRenewalMonitor.stop();
|
|
484
489
|
await this.distributedLocks.releaseLock(descriptor);
|
|
490
|
+
this.runningDistributedLockKeys.delete(descriptor.lockKey);
|
|
485
491
|
}
|
|
486
492
|
}
|
|
487
493
|
async waitForActiveTasks() {
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"timeout",
|
|
10
10
|
"distributed-lock"
|
|
11
11
|
],
|
|
12
|
-
"version": "1.0.0-beta.
|
|
12
|
+
"version": "1.0.0-beta.2",
|
|
13
13
|
"private": false,
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"repository": {
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"croner": "^8.1.2",
|
|
40
40
|
"@fluojs/core": "^1.0.0-beta.1",
|
|
41
|
-
"@fluojs/
|
|
42
|
-
"@fluojs/
|
|
43
|
-
"@fluojs/runtime": "^1.0.0-beta.
|
|
41
|
+
"@fluojs/di": "^1.0.0-beta.2",
|
|
42
|
+
"@fluojs/redis": "^1.0.0-beta.2",
|
|
43
|
+
"@fluojs/runtime": "^1.0.0-beta.2"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"vitest": "^3.2.4"
|