@commandkit/redis 0.1.1 → 1.2.0-rc.1

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.md CHANGED
@@ -31,4 +31,143 @@ async function getCachedData() {
31
31
 
32
32
  return data;
33
33
  }
34
+ ```
35
+
36
+ ## Manual configuration
37
+
38
+ If you want to configure the Redis client manually, you can do so by not registering this plugin and instead updating the cache provider at runtime:
39
+
40
+ ```ts
41
+ import { setCacheProvider } from '@commandkit/cache';
42
+ import { RedisCacheProvider } from '@commandkit/redis';
43
+ import { Redis } from 'ioredis';
44
+
45
+ // configure the redis client as needed
46
+ const redis = new Redis();
47
+ const redisProvider = new RedisCacheProvider(redis);
48
+
49
+ setCacheProvider(redisProvider)
50
+ ```
51
+
52
+ ## Redis Mutex Storage
53
+
54
+ This package also provides a Redis-based mutex storage implementation for distributed locking:
55
+
56
+ ```ts
57
+ import { createMutex } from 'commandkit/mutex';
58
+ import { RedisMutexStorage } from '@commandkit/redis';
59
+ import { Redis } from 'ioredis';
60
+
61
+ // Create Redis client
62
+ const redis = new Redis();
63
+
64
+ // Create Redis-based mutex storage
65
+ const redisMutexStorage = new RedisMutexStorage(redis);
66
+
67
+ // Create mutex with Redis storage
68
+ const mutex = createMutex({
69
+ timeout: 30000,
70
+ storage: redisMutexStorage,
71
+ });
72
+
73
+ // Use the mutex for distributed locking
74
+ const result = await mutex.withLock('shared-resource', async () => {
75
+ // This code runs with exclusive access across all instances
76
+ return await updateSharedResource();
77
+ });
78
+ ```
79
+
80
+ ### Redis Mutex Features
81
+
82
+ - **Distributed Locking**: Works across multiple application instances
83
+ - **Automatic Expiration**: Locks automatically expire to prevent deadlocks
84
+ - **Abort Signal Support**: Can be cancelled using AbortSignal
85
+ - **Atomic Operations**: Uses Lua scripts for atomic lock operations
86
+ - **Lock Extension**: Extend lock timeouts if needed
87
+ - **Force Release**: Emergency release of locks (use with caution)
88
+
89
+ ### Advanced Mutex Usage
90
+
91
+ ```ts
92
+ import { RedisMutexStorage } from '@commandkit/redis';
93
+
94
+ const redisStorage = new RedisMutexStorage(redis);
95
+
96
+ // Get detailed lock information
97
+ const lockInfo = await redisStorage.getLockInfo('my-resource');
98
+ console.log(`Locked: ${lockInfo.locked}, TTL: ${lockInfo.ttl}ms`);
99
+
100
+ // Extend a lock
101
+ const extended = await redisStorage.extendLock('my-resource', 60000);
102
+ if (extended) {
103
+ console.log('Lock extended by 60 seconds');
104
+ }
105
+
106
+ // Force release a lock (emergency use only)
107
+ await redisStorage.forceRelease('my-resource');
108
+ ```
109
+
110
+ ## Redis Semaphore Storage
111
+
112
+ This package also provides a Redis-based semaphore storage implementation for distributed concurrency control:
113
+
114
+ ```ts
115
+ import { createSemaphore } from 'commandkit/semaphore';
116
+ import { RedisSemaphoreStorage } from '@commandkit/redis';
117
+ import { Redis } from 'ioredis';
118
+
119
+ // Create Redis client
120
+ const redis = new Redis();
121
+
122
+ // Create Redis-based semaphore storage
123
+ const redisSemaphoreStorage = new RedisSemaphoreStorage(redis);
124
+
125
+ // Create semaphore with Redis storage
126
+ const semaphore = createSemaphore({
127
+ permits: 5,
128
+ timeout: 30000,
129
+ storage: redisSemaphoreStorage,
130
+ });
131
+
132
+ // Use the semaphore for distributed concurrency control
133
+ const result = await semaphore.withPermit('database-connection', async () => {
134
+ // This code runs with limited concurrency across all instances
135
+ return await executeDatabaseQuery();
136
+ });
137
+ ```
138
+
139
+ ### Redis Semaphore Features
140
+
141
+ - **Distributed Concurrency Control**: Works across multiple application instances
142
+ - **Automatic Initialization**: Semaphores are automatically initialized when first used
143
+ - **Abort Signal Support**: Can be cancelled using AbortSignal
144
+ - **Atomic Operations**: Uses Lua scripts for atomic permit operations
145
+ - **Dynamic Permit Management**: Increase or decrease permits at runtime
146
+ - **Semaphore Information**: Get detailed information about permit usage
147
+
148
+ ### Advanced Semaphore Usage
149
+
150
+ ```ts
151
+ import { RedisSemaphoreStorage } from '@commandkit/redis';
152
+
153
+ const redisStorage = new RedisSemaphoreStorage(redis);
154
+
155
+ // Get detailed semaphore information
156
+ const info = await redisStorage.getSemaphoreInfo('database');
157
+ console.log(`Total: ${info.total}, Available: ${info.available}, Acquired: ${info.acquired}`);
158
+
159
+ // Initialize a semaphore with specific permits
160
+ await redisStorage.initialize('api-endpoint', 10);
161
+
162
+ // Increase permits dynamically
163
+ await redisStorage.increasePermits('database', 5);
164
+
165
+ // Decrease permits dynamically
166
+ await redisStorage.decreasePermits('api-endpoint', 2);
167
+
168
+ // Reset semaphore to initial state
169
+ await redisStorage.reset('database');
170
+
171
+ // Clear semaphore completely
172
+ await redisStorage.clear('old-semaphore');
34
173
  ```
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Redis, type RedisOptions } from 'ioredis';
2
- import { CacheProvider, CommandKitPluginRuntime, RuntimePlugin } from 'commandkit';
2
+ import { CommandKitPluginRuntime, RuntimePlugin } from 'commandkit';
3
+ import { CacheProvider, CacheEntry } from '@commandkit/cache';
3
4
  export type Awaitable<T> = T | Promise<T>;
4
5
  export type SerializeFunction = (value: any) => Awaitable<string>;
5
6
  export type DeserializeFunction = (value: string) => Awaitable<any>;
@@ -12,7 +13,7 @@ export type DeserializeFunction = (value: string) => Awaitable<any>;
12
13
  * });
13
14
  */
14
15
  export declare class RedisCache extends CacheProvider {
15
- protected redis: Redis;
16
+ redis: Redis;
16
17
  /**
17
18
  * Serialize function used to serialize values before storing them in the cache.
18
19
  * By default, it uses `JSON.stringify`.
@@ -42,7 +43,7 @@ export declare class RedisCache extends CacheProvider {
42
43
  * @param key The key to retrieve the value for.
43
44
  * @returns The value stored in the cache, or `undefined` if it does not exist.
44
45
  */
45
- get<T>(key: string): Promise<T | undefined>;
46
+ get<T>(key: string): Promise<CacheEntry<T> | undefined>;
46
47
  /**
47
48
  * Store a value in the cache.
48
49
  * @param key The key to store the value under.
@@ -76,4 +77,13 @@ export declare class RedisPlugin extends RuntimePlugin<RedisOptions> {
76
77
  readonly name = "RedisPlugin";
77
78
  activate(ctx: CommandKitPluginRuntime): Promise<void>;
78
79
  }
80
+ /**
81
+ * Create a new Redis plugin instance.
82
+ * @param options The options to configure the Redis client.
83
+ * @returns The created Redis plugin instance.
84
+ */
79
85
  export declare function redis(options?: RedisOptions): RedisPlugin;
86
+ export * from './ratelimit-storage';
87
+ export * from './mutex-storage';
88
+ export * from './semaphore-storage';
89
+ export { RedisCache as RedisCacheProvider };
package/dist/index.js CHANGED
@@ -1,9 +1,24 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RedisPlugin = exports.RedisCache = void 0;
17
+ exports.RedisCacheProvider = exports.RedisPlugin = exports.RedisCache = void 0;
4
18
  exports.redis = redis;
5
19
  const ioredis_1 = require("ioredis");
6
20
  const commandkit_1 = require("commandkit");
21
+ const cache_1 = require("@commandkit/cache");
7
22
  /**
8
23
  * A cache provider that uses Redis as the cache store.
9
24
  * @example const redisCache = new RedisCache();
@@ -12,7 +27,7 @@ const commandkit_1 = require("commandkit");
12
27
  * cacheProvider: redisCache,
13
28
  * });
14
29
  */
15
- class RedisCache extends commandkit_1.CacheProvider {
30
+ class RedisCache extends cache_1.CacheProvider {
16
31
  constructor(redis) {
17
32
  super();
18
33
  if (redis instanceof ioredis_1.Redis) {
@@ -34,7 +49,12 @@ class RedisCache extends commandkit_1.CacheProvider {
34
49
  if (value === null) {
35
50
  return undefined;
36
51
  }
37
- return JSON.parse(value);
52
+ const entry = this.deserialize(value);
53
+ if (entry.ttl && Date.now() > entry.ttl) {
54
+ await this.delete(key);
55
+ return undefined;
56
+ }
57
+ return entry;
38
58
  }
39
59
  /**
40
60
  * Store a value in the cache.
@@ -43,12 +63,17 @@ class RedisCache extends commandkit_1.CacheProvider {
43
63
  * @param ttl The time-to-live for the cache entry in milliseconds.
44
64
  */
45
65
  async set(key, value, ttl) {
46
- const serialized = await this.serialize(value);
66
+ const entry = {
67
+ value,
68
+ ttl: ttl != null ? Date.now() + ttl : undefined,
69
+ };
70
+ const serialized = this.serialize(entry);
71
+ const finalValue = serialized instanceof Promise ? await serialized : serialized;
47
72
  if (typeof ttl === 'number') {
48
- await this.redis.set(key, serialized, 'PX', ttl);
73
+ await this.redis.set(key, finalValue, 'PX', ttl);
49
74
  }
50
75
  else {
51
- await this.redis.set(key, serialized);
76
+ await this.redis.set(key, finalValue);
52
77
  }
53
78
  }
54
79
  /**
@@ -82,17 +107,26 @@ class RedisCache extends commandkit_1.CacheProvider {
82
107
  }
83
108
  }
84
109
  exports.RedisCache = RedisCache;
110
+ exports.RedisCacheProvider = RedisCache;
85
111
  class RedisPlugin extends commandkit_1.RuntimePlugin {
86
112
  constructor() {
87
113
  super(...arguments);
88
114
  this.name = 'RedisPlugin';
89
115
  }
90
116
  async activate(ctx) {
91
- ctx.commandkit.setCacheProvider(new RedisCache(this.options));
117
+ (0, cache_1.setCacheProvider)(new RedisCache(this.options));
92
118
  }
93
119
  }
94
120
  exports.RedisPlugin = RedisPlugin;
121
+ /**
122
+ * Create a new Redis plugin instance.
123
+ * @param options The options to configure the Redis client.
124
+ * @returns The created Redis plugin instance.
125
+ */
95
126
  function redis(options) {
96
127
  return new RedisPlugin(options ?? {});
97
128
  }
98
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBc0lBLHNCQUVDO0FBeElELHFDQUFtRDtBQUNuRCwyQ0FJb0I7QUFNcEI7Ozs7Ozs7R0FPRztBQUNILE1BQWEsVUFBVyxTQUFRLDBCQUFhO0lBNkIzQyxZQUFtQixLQUE0QjtRQUM3QyxLQUFLLEVBQUUsQ0FBQztRQUVSLElBQUksS0FBSyxZQUFZLGVBQUssRUFBRSxDQUFDO1lBQzNCLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1FBQ3JCLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLGVBQUssQ0FBQyxLQUFLLElBQUksRUFBRSxDQUFDLENBQUM7UUFDdEMsQ0FBQztRQUVELElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUNoQyxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7SUFDaEMsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsR0FBRyxDQUFJLEdBQVc7UUFDN0IsTUFBTSxLQUFLLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUV4QyxJQUFJLEtBQUssS0FBSyxJQUFJLEVBQUUsQ0FBQztZQUNuQixPQUFPLFNBQVMsQ0FBQztRQUNuQixDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBTSxDQUFDO0lBQ2hDLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEtBQUssQ0FBQyxHQUFHLENBQUksR0FBVyxFQUFFLEtBQVEsRUFBRSxHQUFZO1FBQ3JELE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUUvQyxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQzVCLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDbkQsQ0FBQzthQUFNLENBQUM7WUFDTixNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUN4QyxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLEtBQUs7UUFDaEIsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQzlCLENBQUM7SUFFRDs7O09BR0c7SUFDSSxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQVc7UUFDN0IsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM1QixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBVztRQUM3QixPQUFPLE9BQU8sQ0FBQyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDL0MsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQVcsRUFBRSxHQUFXO1FBQzFDLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ3JDLENBQUM7Q0FDRjtBQXpHRCxnQ0F5R0M7QUFFRCxNQUFhLFdBQVksU0FBUSwwQkFBMkI7SUFBNUQ7O1FBQ2tCLFNBQUksR0FBRyxhQUFhLENBQUM7SUFLdkMsQ0FBQztJQUhRLEtBQUssQ0FBQyxRQUFRLENBQUMsR0FBNEI7UUFDaEQsR0FBRyxDQUFDLFVBQVUsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLFVBQVUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUNoRSxDQUFDO0NBQ0Y7QUFORCxrQ0FNQztBQUVELFNBQWdCLEtBQUssQ0FBQyxPQUFzQjtJQUMxQyxPQUFPLElBQUksV0FBVyxDQUFDLE9BQU8sSUFBSSxFQUFFLENBQUMsQ0FBQztBQUN4QyxDQUFDIn0=
129
+ __exportStar(require("./ratelimit-storage"), exports);
130
+ __exportStar(require("./mutex-storage"), exports);
131
+ __exportStar(require("./semaphore-storage"), exports);
132
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFxSkEsc0JBRUM7QUF2SkQscUNBQW1EO0FBQ25ELDJDQUFvRTtBQUNwRSw2Q0FBZ0Y7QUFNaEY7Ozs7Ozs7R0FPRztBQUNILE1BQWEsVUFBVyxTQUFRLHFCQUFhO0lBNkIzQyxZQUFtQixLQUE0QjtRQUM3QyxLQUFLLEVBQUUsQ0FBQztRQUVSLElBQUksS0FBSyxZQUFZLGVBQUssRUFBRSxDQUFDO1lBQzNCLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1FBQ3JCLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLGVBQUssQ0FBQyxLQUFLLElBQUksRUFBRSxDQUFDLENBQUM7UUFDdEMsQ0FBQztRQUVELElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUNoQyxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7SUFDaEMsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsR0FBRyxDQUFJLEdBQVc7UUFDN0IsTUFBTSxLQUFLLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUV4QyxJQUFJLEtBQUssS0FBSyxJQUFJLEVBQUUsQ0FBQztZQUNuQixPQUFPLFNBQVMsQ0FBQztRQUNuQixDQUFDO1FBRUQsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxLQUFLLENBQWtCLENBQUM7UUFDdkQsSUFBSSxLQUFLLENBQUMsR0FBRyxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDeEMsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3ZCLE9BQU8sU0FBUyxDQUFDO1FBQ25CLENBQUM7UUFFRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEtBQUssQ0FBQyxHQUFHLENBQUksR0FBVyxFQUFFLEtBQVEsRUFBRSxHQUFZO1FBQ3JELE1BQU0sS0FBSyxHQUFrQjtZQUMzQixLQUFLO1lBQ0wsR0FBRyxFQUFFLEdBQUcsSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLFNBQVM7U0FDaEQsQ0FBQztRQUVGLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDekMsTUFBTSxVQUFVLEdBQ2QsVUFBVSxZQUFZLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxVQUFVLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQztRQUVoRSxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQzVCLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDbkQsQ0FBQzthQUFNLENBQUM7WUFDTixNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUN4QyxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLEtBQUs7UUFDaEIsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQzlCLENBQUM7SUFFRDs7O09BR0c7SUFDSSxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQVc7UUFDN0IsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM1QixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBVztRQUM3QixPQUFPLE9BQU8sQ0FBQyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDL0MsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQVcsRUFBRSxHQUFXO1FBQzFDLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ3JDLENBQUM7Q0FDRjtBQXRIRCxnQ0FzSEM7QUFzQnNCLHdDQUFrQjtBQXBCekMsTUFBYSxXQUFZLFNBQVEsMEJBQTJCO0lBQTVEOztRQUNrQixTQUFJLEdBQUcsYUFBYSxDQUFDO0lBS3ZDLENBQUM7SUFIUSxLQUFLLENBQUMsUUFBUSxDQUFDLEdBQTRCO1FBQ2hELElBQUEsd0JBQWdCLEVBQUMsSUFBSSxVQUFVLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDakQsQ0FBQztDQUNGO0FBTkQsa0NBTUM7QUFFRDs7OztHQUlHO0FBQ0gsU0FBZ0IsS0FBSyxDQUFDLE9BQXNCO0lBQzFDLE9BQU8sSUFBSSxXQUFXLENBQUMsT0FBTyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0FBQ3hDLENBQUM7QUFFRCxzREFBb0M7QUFDcEMsa0RBQWdDO0FBQ2hDLHNEQUFvQyJ9
@@ -0,0 +1,35 @@
1
+ import type { MutexStorage } from 'commandkit/mutex';
2
+ import Redis from 'ioredis';
3
+ export declare class RedisMutexStorage implements MutexStorage {
4
+ private readonly redis;
5
+ private readonly lockPrefix;
6
+ private readonly defaultTimeout;
7
+ constructor(redis: Redis);
8
+ acquire(key: string, timeout?: number, signal?: AbortSignal): Promise<boolean>;
9
+ release(key: string): Promise<void>;
10
+ isLocked(key: string): Promise<boolean>;
11
+ /**
12
+ * Gets information about a lock including its TTL
13
+ * @param key - The lock key
14
+ * @returns Object containing lock information
15
+ */
16
+ getLockInfo(key: string): Promise<{
17
+ locked: boolean;
18
+ ttl: number;
19
+ value?: string;
20
+ }>;
21
+ /**
22
+ * Force releases a lock (use with caution)
23
+ * @param key - The lock key
24
+ */
25
+ forceRelease(key: string): Promise<void>;
26
+ /**
27
+ * Extends the lock timeout
28
+ * @param key - The lock key
29
+ * @param additionalTime - Additional time in milliseconds
30
+ * @returns True if lock was extended, false if lock doesn't exist
31
+ */
32
+ extendLock(key: string, additionalTime: number): Promise<boolean>;
33
+ private generateLockValue;
34
+ private delay;
35
+ }
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisMutexStorage = void 0;
4
+ class RedisMutexStorage {
5
+ constructor(redis) {
6
+ this.redis = redis;
7
+ this.lockPrefix = 'mutex:';
8
+ this.defaultTimeout = 30000; // 30 seconds
9
+ }
10
+ async acquire(key, timeout = this.defaultTimeout, signal) {
11
+ const lockKey = this.lockPrefix + key;
12
+ const lockValue = this.generateLockValue();
13
+ const startTime = Date.now();
14
+ while (Date.now() - startTime < timeout) {
15
+ // Check if aborted
16
+ if (signal?.aborted) {
17
+ throw new Error('Lock acquisition was aborted');
18
+ }
19
+ // Try to acquire the lock using SET with NX (only if not exists) and EX (expiration)
20
+ const result = await this.redis.set(lockKey, lockValue, 'EX', Math.ceil(timeout / 1000), // Convert to seconds
21
+ 'NX');
22
+ if (result === 'OK') {
23
+ return true;
24
+ }
25
+ // Wait a bit before trying again
26
+ await this.delay(10);
27
+ }
28
+ return false;
29
+ }
30
+ async release(key) {
31
+ const lockKey = this.lockPrefix + key;
32
+ // Simple delete - in a real scenario, you might want to check ownership
33
+ // but for simplicity, we'll just delete the key
34
+ await this.redis.del(lockKey);
35
+ }
36
+ async isLocked(key) {
37
+ const lockKey = this.lockPrefix + key;
38
+ const exists = await this.redis.exists(lockKey);
39
+ return exists === 1;
40
+ }
41
+ /**
42
+ * Gets information about a lock including its TTL
43
+ * @param key - The lock key
44
+ * @returns Object containing lock information
45
+ */
46
+ async getLockInfo(key) {
47
+ const lockKey = this.lockPrefix + key;
48
+ const exists = await this.redis.exists(lockKey);
49
+ if (exists === 0) {
50
+ return { locked: false, ttl: 0 };
51
+ }
52
+ const ttl = await this.redis.ttl(lockKey);
53
+ const value = await this.redis.get(lockKey);
54
+ return {
55
+ locked: true,
56
+ ttl: ttl > 0 ? ttl * 1000 : 0, // Convert to milliseconds
57
+ value: value || undefined,
58
+ };
59
+ }
60
+ /**
61
+ * Force releases a lock (use with caution)
62
+ * @param key - The lock key
63
+ */
64
+ async forceRelease(key) {
65
+ const lockKey = this.lockPrefix + key;
66
+ await this.redis.del(lockKey);
67
+ }
68
+ /**
69
+ * Extends the lock timeout
70
+ * @param key - The lock key
71
+ * @param additionalTime - Additional time in milliseconds
72
+ * @returns True if lock was extended, false if lock doesn't exist
73
+ */
74
+ async extendLock(key, additionalTime) {
75
+ const lockKey = this.lockPrefix + key;
76
+ const exists = await this.redis.exists(lockKey);
77
+ if (exists === 0) {
78
+ return false;
79
+ }
80
+ const result = await this.redis.expire(lockKey, Math.ceil(additionalTime / 1000));
81
+ return result === 1;
82
+ }
83
+ generateLockValue() {
84
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
85
+ }
86
+ delay(ms) {
87
+ return new Promise((resolve) => setTimeout(resolve, ms));
88
+ }
89
+ }
90
+ exports.RedisMutexStorage = RedisMutexStorage;
91
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXV0ZXgtc3RvcmFnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9tdXRleC1zdG9yYWdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUdBLE1BQWEsaUJBQWlCO0lBSTVCLFlBQW9DLEtBQVk7UUFBWixVQUFLLEdBQUwsS0FBSyxDQUFPO1FBSC9CLGVBQVUsR0FBRyxRQUFRLENBQUM7UUFDdEIsbUJBQWMsR0FBRyxLQUFLLENBQUMsQ0FBQyxhQUFhO0lBRUgsQ0FBQztJQUU3QyxLQUFLLENBQUMsT0FBTyxDQUNsQixHQUFXLEVBQ1gsVUFBa0IsSUFBSSxDQUFDLGNBQWMsRUFDckMsTUFBb0I7UUFFcEIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFVBQVUsR0FBRyxHQUFHLENBQUM7UUFDdEMsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDM0MsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRTdCLE9BQU8sSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLFNBQVMsR0FBRyxPQUFPLEVBQUUsQ0FBQztZQUN4QyxtQkFBbUI7WUFDbkIsSUFBSSxNQUFNLEVBQUUsT0FBTyxFQUFFLENBQUM7Z0JBQ3BCLE1BQU0sSUFBSSxLQUFLLENBQUMsOEJBQThCLENBQUMsQ0FBQztZQUNsRCxDQUFDO1lBRUQscUZBQXFGO1lBQ3JGLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQ2pDLE9BQU8sRUFDUCxTQUFTLEVBQ1QsSUFBSSxFQUNKLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxFQUFFLHFCQUFxQjtZQUNoRCxJQUFJLENBQ0wsQ0FBQztZQUVGLElBQUksTUFBTSxLQUFLLElBQUksRUFBRSxDQUFDO2dCQUNwQixPQUFPLElBQUksQ0FBQztZQUNkLENBQUM7WUFFRCxpQ0FBaUM7WUFDakMsTUFBTSxJQUFJLENB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
@@ -0,0 +1,9 @@
1
+ import type { RateLimitStorage } from 'commandkit/ratelimit';
2
+ import Redis from 'ioredis';
3
+ export declare class RedisRateLimitStorage implements RateLimitStorage {
4
+ private readonly redis;
5
+ constructor(redis: Redis);
6
+ get(key: string): Promise<number>;
7
+ set(key: string, value: number): Promise<void>;
8
+ delete(key: string): Promise<void>;
9
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisRateLimitStorage = void 0;
4
+ class RedisRateLimitStorage {
5
+ constructor(redis) {
6
+ this.redis = redis;
7
+ }
8
+ async get(key) {
9
+ return Number(await this.redis.get(key)) || 0;
10
+ }
11
+ async set(key, value) {
12
+ await this.redis.set(key, value);
13
+ }
14
+ async delete(key) {
15
+ await this.redis.del(key);
16
+ }
17
+ }
18
+ exports.RedisRateLimitStorage = RedisRateLimitStorage;
19
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmF0ZWxpbWl0LXN0b3JhZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvcmF0ZWxpbWl0LXN0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBR0EsTUFBYSxxQkFBcUI7SUFDaEMsWUFBb0MsS0FBWTtRQUFaLFVBQUssR0FBTCxLQUFLLENBQU87SUFBRyxDQUFDO0lBRTdDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBVztRQUMxQixPQUFPLE1BQU0sQ0FBQyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2hELENBQUM7SUFFTSxLQUFLLENBQUMsR0FBRyxDQUFDLEdBQVcsRUFBRSxLQUFhO1FBQ3pDLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ25DLENBQUM7SUFFTSxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQVc7UUFDN0IsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM1QixDQUFDO0NBQ0Y7QUFkRCxzREFjQyJ9
@@ -0,0 +1,53 @@
1
+ import type { SemaphoreStorage } from 'commandkit/semaphore';
2
+ import Redis from 'ioredis';
3
+ export declare class RedisSemaphoreStorage implements SemaphoreStorage {
4
+ private readonly redis;
5
+ private readonly semaphorePrefix;
6
+ private readonly defaultTimeout;
7
+ constructor(redis: Redis);
8
+ acquire(key: string, timeout?: number, signal?: AbortSignal): Promise<boolean>;
9
+ release(key: string): Promise<void>;
10
+ getAvailablePermits(key: string): Promise<number>;
11
+ getTotalPermits(key: string): Promise<number>;
12
+ /**
13
+ * Initializes a semaphore with the specified number of permits
14
+ * @param key - The semaphore key
15
+ * @param permits - The total number of permits to allocate
16
+ */
17
+ initialize(key: string, permits: number): Promise<void>;
18
+ /**
19
+ * Gets detailed information about a semaphore
20
+ * @param key - The semaphore key
21
+ * @returns Object containing semaphore information
22
+ */
23
+ getSemaphoreInfo(key: string): Promise<{
24
+ total: number;
25
+ available: number;
26
+ acquired: number;
27
+ initialized: boolean;
28
+ }>;
29
+ /**
30
+ * Resets a semaphore to its initial state
31
+ * @param key - The semaphore key
32
+ * @param permits - The number of permits to reset to (optional, uses current total if not provided)
33
+ */
34
+ reset(key: string, permits?: number): Promise<void>;
35
+ /**
36
+ * Increases the total number of permits for a semaphore
37
+ * @param key - The semaphore key
38
+ * @param additionalPermits - The number of additional permits to add
39
+ */
40
+ increasePermits(key: string, additionalPermits: number): Promise<void>;
41
+ /**
42
+ * Decreases the total number of permits for a semaphore
43
+ * @param key - The semaphore key
44
+ * @param permitsToRemove - The number of permits to remove
45
+ */
46
+ decreasePermits(key: string, permitsToRemove: number): Promise<void>;
47
+ /**
48
+ * Clears a semaphore completely
49
+ * @param key - The semaphore key
50
+ */
51
+ clear(key: string): Promise<void>;
52
+ private delay;
53
+ }
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisSemaphoreStorage = void 0;
4
+ class RedisSemaphoreStorage {
5
+ constructor(redis) {
6
+ this.redis = redis;
7
+ this.semaphorePrefix = 'semaphore:';
8
+ this.defaultTimeout = 30000; // 30 seconds
9
+ }
10
+ async acquire(key, timeout = this.defaultTimeout, signal) {
11
+ const semaphoreKey = this.semaphorePrefix + key;
12
+ const startTime = Date.now();
13
+ while (Date.now() - startTime < timeout) {
14
+ // Check if aborted
15
+ if (signal?.aborted) {
16
+ throw new Error('Permit acquisition was aborted');
17
+ }
18
+ // Check if semaphore exists, if not initialize it
19
+ const total = await this.redis.get(semaphoreKey + ':total');
20
+ if (!total) {
21
+ await this.initialize(key, 10); // Default 10 permits
22
+ }
23
+ // Try to decrement available permits
24
+ const available = await this.redis.get(semaphoreKey + ':available');
25
+ const availableCount = Number(available) || 0;
26
+ if (availableCount > 0) {
27
+ // Use DECR to atomically decrease the count
28
+ const newCount = await this.redis.decr(semaphoreKey + ':available');
29
+ if (newCount >= 0) {
30
+ return true;
31
+ }
32
+ else {
33
+ // If we went negative, increment back
34
+ await this.redis.incr(semaphoreKey + ':available');
35
+ }
36
+ }
37
+ // Wait a bit before trying again
38
+ await this.delay(10);
39
+ }
40
+ return false;
41
+ }
42
+ async release(key) {
43
+ const semaphoreKey = this.semaphorePrefix + key;
44
+ // Get current values
45
+ const total = await this.redis.get(semaphoreKey + ':total');
46
+ const available = await this.redis.get(semaphoreKey + ':available');
47
+ const totalPermits = Number(total) || 0;
48
+ const availablePermits = Number(available) || 0;
49
+ // Only increment if we haven't reached the total
50
+ if (availablePermits < totalPermits) {
51
+ await this.redis.incr(semaphoreKey + ':available');
52
+ }
53
+ }
54
+ async getAvailablePermits(key) {
55
+ const semaphoreKey = this.semaphorePrefix + key;
56
+ const available = await this.redis.get(semaphoreKey + ':available');
57
+ return Number(available) || 0;
58
+ }
59
+ async getTotalPermits(key) {
60
+ const semaphoreKey = this.semaphorePrefix + key;
61
+ const total = await this.redis.get(semaphoreKey + ':total');
62
+ return Number(total) || 0;
63
+ }
64
+ /**
65
+ * Initializes a semaphore with the specified number of permits
66
+ * @param key - The semaphore key
67
+ * @param permits - The total number of permits to allocate
68
+ */
69
+ async initialize(key, permits) {
70
+ const semaphoreKey = this.semaphorePrefix + key;
71
+ await this.redis.set(semaphoreKey + ':total', permits);
72
+ await this.redis.set(semaphoreKey + ':available', permits);
73
+ }
74
+ /**
75
+ * Gets detailed information about a semaphore
76
+ * @param key - The semaphore key
77
+ * @returns Object containing semaphore information
78
+ */
79
+ async getSemaphoreInfo(key) {
80
+ const semaphoreKey = this.semaphorePrefix + key;
81
+ const total = await this.redis.get(semaphoreKey + ':total');
82
+ const available = await this.redis.get(semaphoreKey + ':available');
83
+ const totalPermits = Number(total) || 0;
84
+ const availablePermits = Number(available) || 0;
85
+ return {
86
+ total: totalPermits,
87
+ available: availablePermits,
88
+ acquired: totalPermits - availablePermits,
89
+ initialized: totalPermits > 0,
90
+ };
91
+ }
92
+ /**
93
+ * Resets a semaphore to its initial state
94
+ * @param key - The semaphore key
95
+ * @param permits - The number of permits to reset to (optional, uses current total if not provided)
96
+ */
97
+ async reset(key, permits) {
98
+ const semaphoreKey = this.semaphorePrefix + key;
99
+ if (permits !== undefined) {
100
+ await this.initialize(key, permits);
101
+ }
102
+ else {
103
+ const total = await this.getTotalPermits(key);
104
+ if (total > 0) {
105
+ await this.redis.set(semaphoreKey + ':available', total);
106
+ }
107
+ }
108
+ }
109
+ /**
110
+ * Increases the total number of permits for a semaphore
111
+ * @param key - The semaphore key
112
+ * @param additionalPermits - The number of additional permits to add
113
+ */
114
+ async increasePermits(key, additionalPermits) {
115
+ const semaphoreKey = this.semaphorePrefix + key;
116
+ const total = await this.redis.get(semaphoreKey + ':total');
117
+ const available = await this.redis.get(semaphoreKey + ':available');
118
+ const totalPermits = Number(total) || 0;
119
+ const availablePermits = Number(available) || 0;
120
+ const newTotal = totalPermits + additionalPermits;
121
+ const newAvailable = availablePermits + additionalPermits;
122
+ await this.redis.set(semaphoreKey + ':total', newTotal);
123
+ await this.redis.set(semaphoreKey + ':available', newAvailable);
124
+ }
125
+ /**
126
+ * Decreases the total number of permits for a semaphore
127
+ * @param key - The semaphore key
128
+ * @param permitsToRemove - The number of permits to remove
129
+ */
130
+ async decreasePermits(key, permitsToRemove) {
131
+ const semaphoreKey = this.semaphorePrefix + key;
132
+ const total = await this.redis.get(semaphoreKey + ':total');
133
+ const available = await this.redis.get(semaphoreKey + ':available');
134
+ const totalPermits = Number(total) || 0;
135
+ const availablePermits = Number(available) || 0;
136
+ const newTotal = Math.max(0, totalPermits - permitsToRemove);
137
+ const newAvailable = Math.max(0, availablePermits - permitsToRemove);
138
+ await this.redis.set(semaphoreKey + ':total', newTotal);
139
+ await this.redis.set(semaphoreKey + ':available', newAvailable);
140
+ }
141
+ /**
142
+ * Clears a semaphore completely
143
+ * @param key - The semaphore key
144
+ */
145
+ async clear(key) {
146
+ const semaphoreKey = this.semaphorePrefix + key;
147
+ await this.redis.del(semaphoreKey + ':total', semaphoreKey + ':available');
148
+ }
149
+ delay(ms) {
150
+ return new Promise((resolve) => setTimeout(resolve, ms));
151
+ }
152
+ }
153
+ exports.RedisSemaphoreStorage = RedisSemaphoreStorage;
154
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VtYXBob3JlLXN0b3JhZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc2VtYXBob3JlLXN0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBR0EsTUFBYSxxQkFBcUI7SUFJaEMsWUFBb0MsS0FBWTtRQUFaLFVBQUssR0FBTCxLQUFLLENBQU87UUFIL0Isb0JBQWUsR0FBRyxZQUFZLENBQUM7UUFDL0IsbUJBQWMsR0FBRyxLQUFLLENBQUMsQ0FBQyxhQUFhO0lBRUgsQ0FBQztJQUU3QyxLQUFLLENBQUMsT0FBTyxDQUNsQixHQUFXLEVBQ1gsVUFBa0IsSUFBSSxDQUFDLGNBQWMsRUFDckMsTUFBb0I7UUFFcEIsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLGVBQWUsR0FBRyxHQUFHLENBQUM7UUFDaEQsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRTdCLE9BQU8sSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLFNBQVMsR0FBRyxPQUFPLEVBQUUsQ0FBQztZQUN4QyxtQkFBbUI7WUFDbkIsSUFBSSxNQUFNLEVBQUUsT0FBTyxFQUFFLENBQUM7Z0JBQ3BCLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0NBQWdDLENBQUMsQ0FBQztZQUNwRCxDQUFDO1lBRUQsa0RBQWtEO1lBQ2xELE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsWUFBWSxHQUFHLFFBQVEsQ0FBQyxDQUFDO1lBQzVELElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDWCxNQUFNLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMscUJBQXFCO1lBQ3ZELENBQUM7WUFFRCxxQ0FBcUM7WUFDckMsTUFBTSxTQUFTLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxZQUFZLEdBQUcsWUFBWSxDQUFDLENBQUM7WUFDcEUsTUFBTSxj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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commandkit/redis",
3
- "version": "0.1.1",
3
+ "version": "1.2.0-rc.1",
4
4
  "description": "Redis cache provider for CommandKit",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -16,7 +16,10 @@
16
16
  "redis",
17
17
  "cache"
18
18
  ],
19
- "author": "twilight <hello@twlite.dev>",
19
+ "contributors": [
20
+ "Twilight <hello@twlite.dev>",
21
+ "Avraj <avraj@underctrl.io>"
22
+ ],
20
23
  "license": "MIT",
21
24
  "bugs": {
22
25
  "url": "https://github.com/underctrl-io/commandkit/issues"
@@ -27,11 +30,12 @@
27
30
  },
28
31
  "devDependencies": {
29
32
  "typescript": "^5.7.3",
30
- "tsconfig": "0.0.0",
31
- "commandkit": "0.1.11"
33
+ "@commandkit/cache": "1.2.0-rc.1",
34
+ "commandkit": "1.2.0-rc.1",
35
+ "tsconfig": "0.0.0-rc.1"
32
36
  },
33
37
  "scripts": {
34
- "lint": "tsc --noEmit",
38
+ "check-types": "tsc --noEmit",
35
39
  "build": "tsc"
36
40
  }
37
41
  }