@gixcopilot/redis 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ahmed khaled
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # @gixcopilot/redis
2
+
3
+ Redis coordination for multi-instance AI Copilot deployments (**Beta**). Redis holds
4
+ ephemeral, shared state only; durable state lives in PostgreSQL.
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ npm install @gixcopilot/redis
10
+ ```
11
+
12
+ Requires Node.js >=22.12.0. ESM only.
13
+
14
+ | Export | Purpose |
15
+ | --- | --- |
16
+ | `createRedisRateLimiter(client)` | Distributed fixed-window limiter: one atomic Lua script per check, window derived from the Redis server clock (instances with skewed clocks share windows), weighted `cost` (e.g. tokens), rejected requests do not consume |
17
+ | `createMemoryRateLimiter()` | Same interface in-process (development, tests, single instance) |
18
+ | `createRedisLock(client)` | Lease lock (`SET NX PX` + token-checked release) for temporary coordination; work under it must still be idempotent |
19
+ | `redisHealth(client)` | Readiness check (PING with timeout), never throws |
20
+
21
+ ```ts
22
+ const limiter = createRedisRateLimiter(new Redis(config.secrets.redisUrl.reveal()));
23
+ const decision = await limiter.consume(`tenant:${tenantId}:requests`, { limit: 100, windowMs: 60_000 });
24
+ ```
25
+
26
+ Background jobs use BullMQ through `@gixcopilot/jobs`. Tested against real Redis with three
27
+ limiter instances sharing one limit. Server-only.
28
+
29
+ ## Documentation
30
+
31
+ - [production guide](https://github.com/AfoudaSWE/gix_ai_copilot_nx/blob/main/docs/guides/production.md)
32
+ - [Getting started](https://github.com/AfoudaSWE/gix_ai_copilot_nx/blob/main/docs/guides/getting-started.md)
33
+ - [Source](https://github.com/AfoudaSWE/gix_ai_copilot_nx/tree/main/packages/redis)
34
+
35
+ ## License
36
+
37
+ MIT
@@ -0,0 +1,5 @@
1
+ export { createMemoryRateLimiter, createRedisRateLimiter } from './rate-limiter.js';
2
+ export type { DistributedRateLimitDecision, DistributedRateLimitRule, DistributedRateLimiter } from './rate-limiter.js';
3
+ export { createRedisLock, redisHealth } from './lock.js';
4
+ export type { DistributedLock, LockHandle } from './lock.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACpF,YAAY,EAAE,4BAA4B,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACxH,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createMemoryRateLimiter, createRedisRateLimiter } from './rate-limiter.js';
2
+ export { createRedisLock, redisHealth } from './lock.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAEpF,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
package/dist/lock.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ import type { Redis } from 'ioredis';
2
+ export interface LockHandle {
3
+ readonly key: string;
4
+ /** Releases only if this holder still owns the lock (never another holder's lock). */
5
+ release(): Promise<boolean>;
6
+ }
7
+ export interface DistributedLock {
8
+ /** Returns a handle, or `undefined` when another holder has the lock. */
9
+ tryAcquire(key: string, ttlMs: number): Promise<LockHandle | undefined>;
10
+ }
11
+ /**
12
+ * A single-Redis lease lock for temporary coordination (e.g. one maintenance job at a time).
13
+ * It is a lease, not a correctness guarantee across Redis failover: work guarded by it must
14
+ * still be idempotent. Durable state stays in PostgreSQL.
15
+ */
16
+ export declare function createRedisLock(client: Redis, options?: {
17
+ readonly prefix?: string;
18
+ }): DistributedLock;
19
+ /** Readiness check: PING with a timeout. Never throws. */
20
+ export declare function redisHealth(client: Redis, timeoutMs?: number): Promise<{
21
+ readonly ok: boolean;
22
+ readonly latencyMs: number;
23
+ readonly error?: string;
24
+ }>;
25
+ //# sourceMappingURL=lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sFAAsF;IACtF,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;CACzE;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,eAAe,CAc1G;AAED,0DAA0D;AAC1D,wBAAsB,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,SAAO,GAAG,OAAO,CAAC;IAAE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAQzJ"}
package/dist/lock.js ADDED
@@ -0,0 +1,35 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ const RELEASE = `if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end`;
3
+ /**
4
+ * A single-Redis lease lock for temporary coordination (e.g. one maintenance job at a time).
5
+ * It is a lease, not a correctness guarantee across Redis failover: work guarded by it must
6
+ * still be idempotent. Durable state stays in PostgreSQL.
7
+ */
8
+ export function createRedisLock(client, options = {}) {
9
+ const prefix = options.prefix ?? 'aicopilot:lock';
10
+ return {
11
+ async tryAcquire(key, ttlMs) {
12
+ const token = randomUUID();
13
+ const full = `${prefix}:${key}`;
14
+ const ok = await client.set(full, token, 'PX', ttlMs, 'NX');
15
+ if (ok !== 'OK')
16
+ return undefined;
17
+ return {
18
+ key,
19
+ release: async () => (await client.eval(RELEASE, 1, full, token)) === 1,
20
+ };
21
+ },
22
+ };
23
+ }
24
+ /** Readiness check: PING with a timeout. Never throws. */
25
+ export async function redisHealth(client, timeoutMs = 1000) {
26
+ const started = performance.now();
27
+ try {
28
+ await Promise.race([client.ping(), new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeoutMs).unref())]);
29
+ return { ok: true, latencyMs: Math.round(performance.now() - started) };
30
+ }
31
+ catch (error) {
32
+ return { ok: false, latencyMs: Math.round(performance.now() - started), error: error instanceof Error && error.message === 'timeout' ? 'redis timeout' : 'redis unreachable' };
33
+ }
34
+ }
35
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAczC,MAAM,OAAO,GAAG,mGAAmG,CAAC;AAEpH;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAAa,EAAE,UAAwC,EAAE;IACvF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,gBAAgB,CAAC;IAClD,OAAO;QACL,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK;YACzB,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC;YAChC,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;YAC5D,IAAI,EAAE,KAAK,IAAI;gBAAE,OAAO,SAAS,CAAC;YAClC,OAAO;gBACL,GAAG;gBACH,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC;aACxE,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAa,EAAE,SAAS,GAAG,IAAI;IAC/D,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QACnI,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;IAC1E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC;IACjL,CAAC;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Redis } from 'ioredis';
2
+ export interface DistributedRateLimitRule {
3
+ readonly limit: number;
4
+ readonly windowMs: number;
5
+ }
6
+ export interface DistributedRateLimitDecision {
7
+ readonly allowed: boolean;
8
+ readonly remaining: number;
9
+ /** Milliseconds until the current window resets. */
10
+ readonly resetMs: number;
11
+ readonly limit: number;
12
+ }
13
+ /** Async rate limiter shared by every server instance (Section 76). */
14
+ export interface DistributedRateLimiter {
15
+ consume(key: string, rule: DistributedRateLimitRule, cost?: number): Promise<DistributedRateLimitDecision>;
16
+ }
17
+ export declare function createRedisRateLimiter(client: Redis, options?: {
18
+ readonly prefix?: string;
19
+ }): DistributedRateLimiter;
20
+ /** In-process equivalent for development/tests and single-instance deployments. */
21
+ export declare function createMemoryRateLimiter(now?: () => number): DistributedRateLimiter;
22
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,uEAAuE;AACvE,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,wBAAwB,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;CAC5G;AA6BD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,sBAAsB,CAUxH;AAED,mFAAmF;AACnF,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,MAAiB,GAAG,sBAAsB,CAc5F"}
@@ -0,0 +1,56 @@
1
+ /*
2
+ * Fixed-window counter, evaluated atomically in Redis. The window bucket uses the Redis
3
+ * server's clock (TIME), so instances with skewed clocks still share one window. A rejected
4
+ * request does not consume capacity.
5
+ */
6
+ const SCRIPT = `
7
+ local now = redis.call('TIME')
8
+ local nowMs = tonumber(now[1]) * 1000 + math.floor(tonumber(now[2]) / 1000)
9
+ local windowMs = tonumber(ARGV[2])
10
+ local bucket = math.floor(nowMs / windowMs)
11
+ local key = KEYS[1] .. ':' .. bucket
12
+ local cost = tonumber(ARGV[1])
13
+ local limit = tonumber(ARGV[3])
14
+ local current = tonumber(redis.call('GET', key) or '0')
15
+ local resetMs = (bucket + 1) * windowMs - nowMs
16
+ if current + cost > limit then
17
+ return {0, current, resetMs}
18
+ end
19
+ current = redis.call('INCRBY', key, cost)
20
+ if current == cost then
21
+ redis.call('PEXPIRE', key, windowMs + 1000)
22
+ end
23
+ return {1, current, resetMs}
24
+ `;
25
+ const KEY = /^[A-Za-z0-9._:|@-]{1,256}$/;
26
+ export function createRedisRateLimiter(client, options = {}) {
27
+ const prefix = options.prefix ?? 'aicopilot:rl';
28
+ return {
29
+ async consume(key, rule, cost = 1) {
30
+ if (!KEY.test(key))
31
+ throw new Error('Invalid rate-limit key.');
32
+ if (!Number.isInteger(rule.limit) || rule.limit < 0 || !Number.isInteger(rule.windowMs) || rule.windowMs < 1)
33
+ throw new Error('Invalid rate-limit rule.');
34
+ const [allowed, count, resetMs] = (await client.eval(SCRIPT, 1, `${prefix}:${key}`, String(cost), String(rule.windowMs), String(rule.limit)));
35
+ return { allowed: allowed === 1, remaining: Math.max(rule.limit - count, 0), resetMs, limit: rule.limit };
36
+ },
37
+ };
38
+ }
39
+ /** In-process equivalent for development/tests and single-instance deployments. */
40
+ export function createMemoryRateLimiter(now = Date.now) {
41
+ const windows = new Map();
42
+ return {
43
+ consume(key, rule, cost = 1) {
44
+ const time = now();
45
+ const bucket = Math.floor(time / rule.windowMs);
46
+ const entry = windows.get(key);
47
+ const count = entry?.bucket === bucket ? entry.count : 0;
48
+ const resetMs = (bucket + 1) * rule.windowMs - time;
49
+ if (count + cost > rule.limit)
50
+ return Promise.resolve({ allowed: false, remaining: Math.max(rule.limit - count, 0), resetMs, limit: rule.limit });
51
+ windows.set(key, { bucket, count: count + cost });
52
+ return Promise.resolve({ allowed: true, remaining: Math.max(rule.limit - count - cost, 0), resetMs, limit: rule.limit });
53
+ },
54
+ };
55
+ }
56
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAoBA;;;;GAIG;AACH,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;CAkBd,CAAC;AAEF,MAAM,GAAG,GAAG,4BAA4B,CAAC;AAEzC,MAAM,UAAU,sBAAsB,CAAC,MAAa,EAAE,UAAwC,EAAE;IAC9F,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,cAAc,CAAC;IAChD,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC;YAC/B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;YAC/D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;YAC1J,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,MAAM,IAAI,GAAG,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAA6B,CAAC;YAC1K,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QAC5G,CAAC;KACF,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,uBAAuB,CAAC,MAAoB,IAAI,CAAC,GAAG;IAClE,MAAM,OAAO,GAAG,IAAI,GAAG,EAA6C,CAAC;IACrE,OAAO;QACL,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC;YACzB,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC;YACnB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;YAChD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC/B,MAAM,KAAK,GAAG,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACzD,MAAM,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACpD,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC,KAAK;gBAAE,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YAClJ,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,CAAC,CAAC;YAClD,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,GAAG,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3H,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@gixcopilot/redis",
3
+ "version": "0.1.0",
4
+ "description": "Redis coordination for multi-instance deployments: distributed rate limiting, lease locks and health checks.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/AfoudaSWE/gix_ai_copilot_nx.git",
9
+ "directory": "packages/redis"
10
+ },
11
+ "homepage": "https://github.com/AfoudaSWE/gix_ai_copilot_nx/tree/main/packages/redis#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/AfoudaSWE/gix_ai_copilot_nx/issues"
14
+ },
15
+ "type": "module",
16
+ "sideEffects": false,
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ },
24
+ "./package.json": "./package.json"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "!dist/**/*.spec.*",
29
+ "!dist/**/*.spec-helper.*",
30
+ "!dist/**/*.integration.spec.*",
31
+ "!dist/**/*.tsbuildinfo"
32
+ ],
33
+ "engines": {
34
+ "node": ">=22.12.0"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "dependencies": {
40
+ "ioredis": "5.11.1"
41
+ },
42
+ "devDependencies": {
43
+ "@testcontainers/redis": "^12.1.0",
44
+ "testcontainers": "^12.1.0",
45
+ "vitest": "5.0.1"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc -b tsconfig.json",
49
+ "typecheck": "tsc -b tsconfig.json",
50
+ "test": "vitest run",
51
+ "lint": "eslint src"
52
+ }
53
+ }