clhq-cache-module 1.1.0-alpha.90
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 +47 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/redis-cache-options.interface.d.ts +33 -0
- package/dist/interfaces/redis-cache-options.interface.d.ts.map +1 -0
- package/dist/interfaces/redis-cache-options.interface.js +3 -0
- package/dist/interfaces/redis-cache-options.interface.js.map +1 -0
- package/dist/redis-cache.constants.d.ts +2 -0
- package/dist/redis-cache.constants.d.ts.map +1 -0
- package/dist/redis-cache.constants.js +5 -0
- package/dist/redis-cache.constants.js.map +1 -0
- package/dist/redis-cache.module.d.ts +6 -0
- package/dist/redis-cache.module.d.ts.map +1 -0
- package/dist/redis-cache.module.js +79 -0
- package/dist/redis-cache.module.js.map +1 -0
- package/dist/redis-cache.service.d.ts +22 -0
- package/dist/redis-cache.service.d.ts.map +1 -0
- package/dist/redis-cache.service.js +197 -0
- package/dist/redis-cache.service.js.map +1 -0
- package/dist/valkey-proxy-client.d.ts +10 -0
- package/dist/valkey-proxy-client.d.ts.map +1 -0
- package/dist/valkey-proxy-client.js +48 -0
- package/dist/valkey-proxy-client.js.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Clippy Cache Module
|
|
2
|
+
|
|
3
|
+
Reusable NestJS module that wires Redis backed caching (via `cache-manager`) for all Clippy API services.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Async Redis store bootstrap with automatic fallback to in-memory cache for local development.
|
|
8
|
+
- Helper service with `get`, `set`, `del`, `delMany`, and `wrap` helpers.
|
|
9
|
+
- Configurable through environment variables or module options.
|
|
10
|
+
|
|
11
|
+
## Environment Variables
|
|
12
|
+
|
|
13
|
+
| Variable | Description | Default |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `REDIS_HOST` | Redis endpoint hostname | `undefined` |
|
|
16
|
+
| `REDIS_PORT` | Redis endpoint port | `6379` |
|
|
17
|
+
| `REDIS_URL` | Full connection URL. Takes precedence over host/port | `undefined` |
|
|
18
|
+
| `REDIS_USERNAME` | ACL user | `undefined` |
|
|
19
|
+
| `REDIS_PASSWORD` | Auth token | `undefined` |
|
|
20
|
+
| `REDIS_DB` | Database index | `0` |
|
|
21
|
+
| `REDIS_TLS_ENABLED` | Enable TLS | `true` |
|
|
22
|
+
| `REDIS_CACHE_TTL_SECONDS` | Default TTL for cache entries | `300` |
|
|
23
|
+
| `REDIS_CACHE_MAX_ITEMS` | Max number of cache entries | `1000` |
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { RedisCacheModule, RedisCacheService } from 'clhq-cache-module';
|
|
29
|
+
|
|
30
|
+
@Module({
|
|
31
|
+
imports: [RedisCacheModule.register()],
|
|
32
|
+
providers: [SampleService],
|
|
33
|
+
})
|
|
34
|
+
export class SampleModule {}
|
|
35
|
+
|
|
36
|
+
@Injectable()
|
|
37
|
+
export class SampleService {
|
|
38
|
+
constructor(private readonly cache: RedisCacheService) {}
|
|
39
|
+
|
|
40
|
+
async getValue(id: string) {
|
|
41
|
+
return this.cache.wrap(`sample:${id}`, async () => {
|
|
42
|
+
return this.repository.findById(id);
|
|
43
|
+
}, 600);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,4CAA4C,CAAC;AAC3D,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./interfaces/redis-cache-options.interface"), exports);
|
|
18
|
+
__exportStar(require("./redis-cache.module"), exports);
|
|
19
|
+
__exportStar(require("./redis-cache.service"), exports);
|
|
20
|
+
__exportStar(require("./valkey-proxy-client"), exports);
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6EAA2D;AAC3D,uDAAqC;AACrC,wDAAsC;AACtC,wDAAsC","sourcesContent":["export * from './interfaces/redis-cache-options.interface';\nexport * from './redis-cache.module';\nexport * from './redis-cache.service';\nexport * from './valkey-proxy-client';\n\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface RedisCacheModuleOptions {
|
|
2
|
+
host?: string;
|
|
3
|
+
port?: number;
|
|
4
|
+
url?: string;
|
|
5
|
+
username?: string;
|
|
6
|
+
password?: string;
|
|
7
|
+
db?: number;
|
|
8
|
+
ttlMs?: number;
|
|
9
|
+
max?: number;
|
|
10
|
+
clusterNodes?: Array<{
|
|
11
|
+
host: string;
|
|
12
|
+
port?: number;
|
|
13
|
+
}>;
|
|
14
|
+
tlsEnabled?: boolean;
|
|
15
|
+
isGlobal?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface InternalRedisCacheOptions extends RedisCacheModuleOptions {
|
|
18
|
+
resolvedHost?: string;
|
|
19
|
+
resolvedPort?: number;
|
|
20
|
+
resolvedUrl?: string;
|
|
21
|
+
resolvedUsername?: string;
|
|
22
|
+
resolvedPassword?: string;
|
|
23
|
+
resolvedDb?: number;
|
|
24
|
+
resolvedTtlMs?: number;
|
|
25
|
+
resolvedMax?: number;
|
|
26
|
+
resolvedTlsEnabled?: boolean;
|
|
27
|
+
resolvedIsGlobal?: boolean;
|
|
28
|
+
resolvedClusterNodes?: Array<{
|
|
29
|
+
host: string;
|
|
30
|
+
port?: number;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=redis-cache-options.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-cache-options.interface.d.ts","sourceRoot":"/","sources":["interfaces/redis-cache-options.interface.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,uBAAuB;IAKtC,IAAI,CAAC,EAAE,MAAM,CAAC;IAKd,IAAI,CAAC,EAAE,MAAM,CAAC;IAId,GAAG,CAAC,EAAE,MAAM,CAAC;IAIb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAIlB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAIlB,EAAE,CAAC,EAAE,MAAM,CAAC;IAIZ,KAAK,CAAC,EAAE,MAAM,CAAC;IAIf,GAAG,CAAC,EAAE,MAAM,CAAC;IAKb,YAAY,CAAC,EAAE,KAAK,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IAIH,UAAU,CAAC,EAAE,OAAO,CAAC;IAIrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,yBAA0B,SAAQ,uBAAuB;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,oBAAoB,CAAC,EAAE,KAAK,CAAC;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;CACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-cache-options.interface.js","sourceRoot":"/","sources":["interfaces/redis-cache-options.interface.ts"],"names":[],"mappings":"","sourcesContent":["export interface RedisCacheModuleOptions {\n /**\n * Hostname for the Redis endpoint.\n * When omitted the module falls back to REDIS_HOST env variable.\n */\n host?: string;\n /**\n * Port for the Redis endpoint.\n * Defaults to REDIS_PORT env variable or 6379.\n */\n port?: number;\n /**\n * Full connection url. Takes precedence over host/port when provided.\n */\n url?: string;\n /**\n * Optional username for Redis ACL based auth.\n */\n username?: string;\n /**\n * Optional password/token for Redis auth.\n */\n password?: string;\n /**\n * Database index to use. Defaults to 0.\n */\n db?: number;\n /**\n * TTL for cached entries in milliseconds. Defaults to 300_000 (5 minutes).\n */\n ttlMs?: number;\n /**\n * Max number of items to keep in local memory cache.\n */\n max?: number;\n /**\n * Optional Valkey/Redis cluster nodes in host[:port] format.\n * Takes precedence over single host/port when provided.\n */\n clusterNodes?: Array<{\n host: string;\n port?: number;\n }>;\n /**\n * Enable TLS when connecting to Redis.\n */\n tlsEnabled?: boolean;\n /**\n * Whether to register the CacheModule globally.\n */\n isGlobal?: boolean;\n}\n\nexport interface InternalRedisCacheOptions extends RedisCacheModuleOptions {\n resolvedHost?: string;\n resolvedPort?: number;\n resolvedUrl?: string;\n resolvedUsername?: string;\n resolvedPassword?: string;\n resolvedDb?: number;\n resolvedTtlMs?: number;\n resolvedMax?: number;\n resolvedTlsEnabled?: boolean;\n resolvedIsGlobal?: boolean;\n resolvedClusterNodes?: Array<{\n host: string;\n port?: number;\n }>;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-cache.constants.d.ts","sourceRoot":"/","sources":["redis-cache.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,eAAgC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-cache.constants.js","sourceRoot":"/","sources":["redis-cache.constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,mBAAmB,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC","sourcesContent":["export const REDIS_CACHE_OPTIONS = Symbol('REDIS_CACHE_OPTIONS');\n\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DynamicModule } from '@nestjs/common';
|
|
2
|
+
import { RedisCacheModuleOptions } from './interfaces/redis-cache-options.interface';
|
|
3
|
+
export declare class RedisCacheModule {
|
|
4
|
+
static register(options?: RedisCacheModuleOptions): DynamicModule;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=redis-cache.module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-cache.module.d.ts","sourceRoot":"/","sources":["redis-cache.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAkB,MAAM,gBAAgB,CAAC;AAE/D,OAAO,EAEL,uBAAuB,EACxB,MAAM,4CAA4C,CAAC;AAGpD,qBAEa,gBAAgB;IAC3B,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAE,uBAA4B,GAAG,aAAa;CAatE"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var RedisCacheModule_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.RedisCacheModule = void 0;
|
|
11
|
+
const common_1 = require("@nestjs/common");
|
|
12
|
+
const redis_cache_service_1 = require("./redis-cache.service");
|
|
13
|
+
const redis_cache_constants_1 = require("./redis-cache.constants");
|
|
14
|
+
let RedisCacheModule = RedisCacheModule_1 = class RedisCacheModule {
|
|
15
|
+
static register(options = {}) {
|
|
16
|
+
const resolvedOptions = resolveOptions(options);
|
|
17
|
+
const cacheOptionsProvider = {
|
|
18
|
+
provide: redis_cache_constants_1.REDIS_CACHE_OPTIONS,
|
|
19
|
+
useValue: resolvedOptions,
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
module: RedisCacheModule_1,
|
|
23
|
+
providers: [cacheOptionsProvider, redis_cache_service_1.RedisCacheService],
|
|
24
|
+
exports: [redis_cache_service_1.RedisCacheService],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
exports.RedisCacheModule = RedisCacheModule;
|
|
29
|
+
exports.RedisCacheModule = RedisCacheModule = RedisCacheModule_1 = __decorate([
|
|
30
|
+
(0, common_1.Global)(),
|
|
31
|
+
(0, common_1.Module)({})
|
|
32
|
+
], RedisCacheModule);
|
|
33
|
+
function resolveOptions(options) {
|
|
34
|
+
return {
|
|
35
|
+
...options,
|
|
36
|
+
resolvedClusterNodes: options.clusterNodes ??
|
|
37
|
+
parseClusterNodes(process.env.VALKEY_CLUSTER_ENDPOINTS ??
|
|
38
|
+
process.env.REDIS_CLUSTER_ENDPOINTS ??
|
|
39
|
+
''),
|
|
40
|
+
resolvedHost: options.host,
|
|
41
|
+
resolvedPort: options.port ?? 6379,
|
|
42
|
+
resolvedUrl: options.url,
|
|
43
|
+
resolvedUsername: options.username,
|
|
44
|
+
resolvedPassword: options.password,
|
|
45
|
+
resolvedDb: options.db ?? 0,
|
|
46
|
+
resolvedTtlMs: Number(options.ttlMs ?? process.env.REDIS_CACHE_TTL_MS ?? 300000),
|
|
47
|
+
resolvedMax: Number(options.max ?? process.env.REDIS_CACHE_MAX_ITEMS ?? 1000),
|
|
48
|
+
resolvedTlsEnabled: parseBoolean(options.tlsEnabled ?? process.env.REDIS_TLS_ENABLED ?? 'true'),
|
|
49
|
+
resolvedIsGlobal: options.isGlobal ?? true,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function parseBoolean(value) {
|
|
53
|
+
if (typeof value === 'boolean') {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
if (typeof value === 'string') {
|
|
57
|
+
return ['true', '1', 'yes'].includes(value.toLowerCase());
|
|
58
|
+
}
|
|
59
|
+
return Boolean(value);
|
|
60
|
+
}
|
|
61
|
+
function parseClusterNodes(value) {
|
|
62
|
+
if (!value) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const nodes = value
|
|
66
|
+
.split(',')
|
|
67
|
+
.map((token) => token.trim())
|
|
68
|
+
.filter(Boolean)
|
|
69
|
+
.map((token) => {
|
|
70
|
+
const [host, port] = token.split(':');
|
|
71
|
+
return {
|
|
72
|
+
host,
|
|
73
|
+
port: port ? Number(port) : undefined,
|
|
74
|
+
};
|
|
75
|
+
})
|
|
76
|
+
.filter((node) => node.host);
|
|
77
|
+
return nodes.length ? nodes : undefined;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=redis-cache.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-cache.module.js","sourceRoot":"/","sources":["redis-cache.module.ts"],"names":[],"mappings":"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","sourcesContent":["import { DynamicModule, Global, Module } from '@nestjs/common';\nimport { RedisCacheService } from './redis-cache.service';\nimport {\n InternalRedisCacheOptions,\n RedisCacheModuleOptions,\n} from './interfaces/redis-cache-options.interface';\nimport { REDIS_CACHE_OPTIONS } from './redis-cache.constants';\n\n@Global()\n@Module({})\nexport class RedisCacheModule {\n static register(options: RedisCacheModuleOptions = {}): DynamicModule {\n const resolvedOptions = resolveOptions(options);\n const cacheOptionsProvider = {\n provide: REDIS_CACHE_OPTIONS,\n useValue: resolvedOptions,\n };\n\n return {\n module: RedisCacheModule,\n providers: [cacheOptionsProvider, RedisCacheService],\n exports: [RedisCacheService],\n };\n }\n}\n\nfunction resolveOptions(\n options: RedisCacheModuleOptions,\n): InternalRedisCacheOptions {\n return {\n ...options,\n resolvedClusterNodes:\n options.clusterNodes ??\n parseClusterNodes(\n process.env.VALKEY_CLUSTER_ENDPOINTS ??\n process.env.REDIS_CLUSTER_ENDPOINTS ??\n '',\n ),\n resolvedHost: options.host,\n resolvedPort: options.port ?? 6379,\n resolvedUrl: options.url,\n resolvedUsername: options.username,\n resolvedPassword: options.password,\n resolvedDb: options.db ?? 0,\n resolvedTtlMs: Number(\n options.ttlMs ?? process.env.REDIS_CACHE_TTL_MS ?? 300000,\n ),\n resolvedMax: Number(\n options.max ?? process.env.REDIS_CACHE_MAX_ITEMS ?? 1000,\n ),\n resolvedTlsEnabled: parseBoolean(\n options.tlsEnabled ?? process.env.REDIS_TLS_ENABLED ?? 'true',\n ),\n resolvedIsGlobal: options.isGlobal ?? true,\n };\n}\n\nfunction parseBoolean(value: unknown): boolean {\n if (typeof value === 'boolean') {\n return value;\n }\n if (typeof value === 'string') {\n return ['true', '1', 'yes'].includes(value.toLowerCase());\n }\n return Boolean(value);\n}\n\nfunction parseClusterNodes(\n value?: string,\n): Array<{ host: string; port?: number }> | undefined {\n if (!value) {\n return undefined;\n }\n const nodes = value\n .split(',')\n .map((token) => token.trim())\n .filter(Boolean)\n .map((token) => {\n const [host, port] = token.split(':');\n return {\n host,\n port: port ? Number(port) : undefined,\n };\n })\n .filter((node) => node.host);\n return nodes.length ? nodes : undefined;\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { OnModuleDestroy } from '@nestjs/common';
|
|
2
|
+
import { InternalRedisCacheOptions } from './interfaces/redis-cache-options.interface';
|
|
3
|
+
export declare class RedisCacheService implements OnModuleDestroy {
|
|
4
|
+
private readonly options;
|
|
5
|
+
private readonly logger;
|
|
6
|
+
private readonly redisClient?;
|
|
7
|
+
private readonly proxyClient?;
|
|
8
|
+
private readonly memoryCache;
|
|
9
|
+
constructor(options: InternalRedisCacheOptions);
|
|
10
|
+
onModuleDestroy(): Promise<void>;
|
|
11
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
12
|
+
set<T>(key: string, value: T, ttlMs?: number): Promise<void>;
|
|
13
|
+
del(key: string): Promise<void>;
|
|
14
|
+
delMany(keys: string[]): Promise<void>;
|
|
15
|
+
wrap<T>(key: string, fn: () => Promise<T>, ttlMs?: number): Promise<T>;
|
|
16
|
+
private createRedisClient;
|
|
17
|
+
private attachLogging;
|
|
18
|
+
private serialize;
|
|
19
|
+
private deserialize;
|
|
20
|
+
private enforceMemoryLimit;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=redis-cache.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-cache.service.d.ts","sourceRoot":"/","sources":["redis-cache.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAG7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AAUvF,qBACa,iBAAkB,YAAW,eAAe;IAQrD,OAAO,CAAC,QAAQ,CAAC,OAAO;IAP1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;IAC7D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAkB;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuC;gBAIhD,OAAO,EAAE,yBAAyB;IA2B/C,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAahC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAwB3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB5D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB/B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBtC,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAe5E,OAAO,CAAC,iBAAiB;IAyCzB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,kBAAkB;CAS3B"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
17
|
+
var RedisCacheService_1;
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.RedisCacheService = void 0;
|
|
20
|
+
const common_1 = require("@nestjs/common");
|
|
21
|
+
const ioredis_1 = __importDefault(require("ioredis"));
|
|
22
|
+
const redis_cache_constants_1 = require("./redis-cache.constants");
|
|
23
|
+
const valkey_proxy_client_1 = require("./valkey-proxy-client");
|
|
24
|
+
let RedisCacheService = RedisCacheService_1 = class RedisCacheService {
|
|
25
|
+
constructor(options) {
|
|
26
|
+
this.options = options;
|
|
27
|
+
this.logger = new common_1.Logger(RedisCacheService_1.name);
|
|
28
|
+
this.memoryCache = new Map();
|
|
29
|
+
if (process.env.VALKEY_PROXY_FUNCTION_NAME) {
|
|
30
|
+
try {
|
|
31
|
+
this.proxyClient = new valkey_proxy_client_1.ValkeyProxyClient();
|
|
32
|
+
this.logger.log('Using Valkey Lambda Proxy (no VPC needed!)');
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
this.logger.warn(`Failed to initialize Valkey proxy: ${error.message}. Falling back to in-memory cache.`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else if (options.resolvedUrl ||
|
|
39
|
+
options.resolvedHost ||
|
|
40
|
+
(options.resolvedClusterNodes && options.resolvedClusterNodes.length > 0)) {
|
|
41
|
+
this.redisClient = this.createRedisClient(options);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.logger.warn('Redis connection details missing. Falling back to in-memory cache.');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async onModuleDestroy() {
|
|
48
|
+
if (this.redisClient) {
|
|
49
|
+
if ('quit' in this.redisClient &&
|
|
50
|
+
typeof this.redisClient.quit === 'function') {
|
|
51
|
+
await this.redisClient.quit();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.redisClient.disconnect();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async get(key) {
|
|
59
|
+
if (this.proxyClient) {
|
|
60
|
+
return await this.proxyClient.get(key);
|
|
61
|
+
}
|
|
62
|
+
if (this.redisClient) {
|
|
63
|
+
const payload = await this.redisClient.get(key);
|
|
64
|
+
return payload ? this.deserialize(payload) : undefined;
|
|
65
|
+
}
|
|
66
|
+
const entry = this.memoryCache.get(key);
|
|
67
|
+
if (!entry)
|
|
68
|
+
return undefined;
|
|
69
|
+
if (entry.expiresAt && entry.expiresAt <= Date.now()) {
|
|
70
|
+
this.memoryCache.delete(key);
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
return entry.value;
|
|
74
|
+
}
|
|
75
|
+
async set(key, value, ttlMs) {
|
|
76
|
+
const ttl = ttlMs ?? this.options.resolvedTtlMs;
|
|
77
|
+
if (this.proxyClient) {
|
|
78
|
+
await this.proxyClient.set(key, value, ttl);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (this.redisClient) {
|
|
82
|
+
const serialized = this.serialize(value);
|
|
83
|
+
if (ttl) {
|
|
84
|
+
await this.redisClient.set(key, serialized, 'PX', ttl);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
await this.redisClient.set(key, serialized);
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const expiresAt = ttl ? Date.now() + ttl : undefined;
|
|
92
|
+
this.memoryCache.set(key, { value, expiresAt });
|
|
93
|
+
this.enforceMemoryLimit();
|
|
94
|
+
}
|
|
95
|
+
async del(key) {
|
|
96
|
+
if (this.proxyClient) {
|
|
97
|
+
await this.proxyClient.del(key);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (this.redisClient) {
|
|
101
|
+
await this.redisClient.del(key);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
this.memoryCache.delete(key);
|
|
105
|
+
}
|
|
106
|
+
async delMany(keys) {
|
|
107
|
+
if (this.proxyClient) {
|
|
108
|
+
await this.proxyClient.delMany(keys);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (this.redisClient) {
|
|
112
|
+
if (keys.length > 0) {
|
|
113
|
+
await this.redisClient.del(...keys);
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
keys.forEach((key) => this.memoryCache.delete(key));
|
|
118
|
+
}
|
|
119
|
+
async wrap(key, fn, ttlMs) {
|
|
120
|
+
const cached = await this.get(key);
|
|
121
|
+
if (cached !== undefined) {
|
|
122
|
+
return cached;
|
|
123
|
+
}
|
|
124
|
+
const value = await fn();
|
|
125
|
+
if (value !== undefined) {
|
|
126
|
+
await this.set(key, value, ttlMs);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
this.logger.warn(`Computed cache value for key ${key} is undefined`);
|
|
130
|
+
}
|
|
131
|
+
return value;
|
|
132
|
+
}
|
|
133
|
+
createRedisClient(options) {
|
|
134
|
+
const clusterNodes = options.resolvedClusterNodes;
|
|
135
|
+
if (clusterNodes && clusterNodes.length > 0) {
|
|
136
|
+
const nodes = clusterNodes.map((node) => ({
|
|
137
|
+
host: node.host,
|
|
138
|
+
port: node.port ?? options.resolvedPort ?? 6379,
|
|
139
|
+
}));
|
|
140
|
+
const client = new ioredis_1.default.Cluster(nodes, {
|
|
141
|
+
dnsLookup: (address, callback) => callback(null, address),
|
|
142
|
+
redisOptions: {
|
|
143
|
+
username: options.resolvedUsername,
|
|
144
|
+
password: options.resolvedPassword,
|
|
145
|
+
db: options.resolvedDb,
|
|
146
|
+
tls: options.resolvedTlsEnabled ? {} : undefined,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
this.attachLogging(client);
|
|
150
|
+
return client;
|
|
151
|
+
}
|
|
152
|
+
const client = options.resolvedUrl
|
|
153
|
+
? new ioredis_1.default(options.resolvedUrl, options.resolvedTlsEnabled ? { tls: {} } : undefined)
|
|
154
|
+
: new ioredis_1.default({
|
|
155
|
+
host: options.resolvedHost,
|
|
156
|
+
port: options.resolvedPort,
|
|
157
|
+
username: options.resolvedUsername,
|
|
158
|
+
password: options.resolvedPassword,
|
|
159
|
+
db: options.resolvedDb,
|
|
160
|
+
tls: options.resolvedTlsEnabled ? {} : undefined,
|
|
161
|
+
});
|
|
162
|
+
this.attachLogging(client);
|
|
163
|
+
return client;
|
|
164
|
+
}
|
|
165
|
+
attachLogging(client) {
|
|
166
|
+
client.on('error', (err) => this.logger.error(`Redis cache connection error: ${err.message}`, err.stack));
|
|
167
|
+
client.on('ready', () => this.logger.log('Redis/Valkey cache connection ready'));
|
|
168
|
+
}
|
|
169
|
+
serialize(value) {
|
|
170
|
+
return JSON.stringify(value);
|
|
171
|
+
}
|
|
172
|
+
deserialize(payload) {
|
|
173
|
+
try {
|
|
174
|
+
return JSON.parse(payload);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
this.logger.error(`Failed to deserialize cached value: ${error.message}`);
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
enforceMemoryLimit() {
|
|
182
|
+
if (this.memoryCache.size <= this.options.resolvedMax) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const oldestKey = this.memoryCache.keys().next().value;
|
|
186
|
+
if (oldestKey) {
|
|
187
|
+
this.memoryCache.delete(oldestKey);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
exports.RedisCacheService = RedisCacheService;
|
|
192
|
+
exports.RedisCacheService = RedisCacheService = RedisCacheService_1 = __decorate([
|
|
193
|
+
(0, common_1.Injectable)(),
|
|
194
|
+
__param(0, (0, common_1.Inject)(redis_cache_constants_1.REDIS_CACHE_OPTIONS)),
|
|
195
|
+
__metadata("design:paramtypes", [Object])
|
|
196
|
+
], RedisCacheService);
|
|
197
|
+
//# sourceMappingURL=redis-cache.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-cache.service.js","sourceRoot":"/","sources":["redis-cache.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Inject, Injectable, Logger, OnModuleDestroy } from '@nestjs/common';\nimport Redis, { Cluster } from 'ioredis';\nimport { REDIS_CACHE_OPTIONS } from './redis-cache.constants';\nimport { InternalRedisCacheOptions } from './interfaces/redis-cache-options.interface';\nimport { ValkeyProxyClient } from './valkey-proxy-client';\n\ntype MemoryCacheEntry = {\n value: unknown;\n expiresAt?: number;\n};\n\ntype RedisLikeClient = Redis | Cluster;\n\n@Injectable()\nexport class RedisCacheService implements OnModuleDestroy {\n private readonly logger = new Logger(RedisCacheService.name);\n private readonly redisClient?: RedisLikeClient;\n private readonly proxyClient?: ValkeyProxyClient;\n private readonly memoryCache = new Map<string, MemoryCacheEntry>();\n\n constructor(\n @Inject(REDIS_CACHE_OPTIONS)\n private readonly options: InternalRedisCacheOptions,\n ) {\n // Check if we should use Lambda proxy (for VPC-less access to Valkey)\n if (process.env.VALKEY_PROXY_FUNCTION_NAME) {\n try {\n this.proxyClient = new ValkeyProxyClient();\n this.logger.log('Using Valkey Lambda Proxy (no VPC needed!)');\n } catch (error) {\n this.logger.warn(\n `Failed to initialize Valkey proxy: ${error.message}. Falling back to in-memory cache.`,\n );\n }\n }\n // Otherwise use direct connection (requires VPC)\n else if (\n options.resolvedUrl ||\n options.resolvedHost ||\n (options.resolvedClusterNodes && options.resolvedClusterNodes.length > 0)\n ) {\n this.redisClient = this.createRedisClient(options);\n } else {\n this.logger.warn(\n 'Redis connection details missing. Falling back to in-memory cache.',\n );\n }\n }\n\n async onModuleDestroy(): Promise<void> {\n if (this.redisClient) {\n if (\n 'quit' in this.redisClient &&\n typeof this.redisClient.quit === 'function'\n ) {\n await this.redisClient.quit();\n } else {\n this.redisClient.disconnect();\n }\n }\n }\n\n async get<T>(key: string): Promise<T | undefined> {\n // Use Lambda proxy if available\n if (this.proxyClient) {\n return await this.proxyClient.get<T>(key);\n }\n\n // Use direct Redis connection\n if (this.redisClient) {\n const payload = await this.redisClient.get(key);\n return payload ? this.deserialize<T>(payload) : undefined;\n }\n\n // Fall back to in-memory cache\n const entry = this.memoryCache.get(key);\n if (!entry) return undefined;\n\n if (entry.expiresAt && entry.expiresAt <= Date.now()) {\n this.memoryCache.delete(key);\n return undefined;\n }\n\n return entry.value as T;\n }\n\n async set<T>(key: string, value: T, ttlMs?: number): Promise<void> {\n const ttl = ttlMs ?? this.options.resolvedTtlMs;\n\n // Use Lambda proxy if available\n if (this.proxyClient) {\n await this.proxyClient.set(key, value, ttl);\n return;\n }\n\n // Use direct Redis connection\n if (this.redisClient) {\n const serialized = this.serialize(value);\n if (ttl) {\n await this.redisClient.set(key, serialized, 'PX', ttl);\n } else {\n await this.redisClient.set(key, serialized);\n }\n return;\n }\n\n const expiresAt = ttl ? Date.now() + ttl : undefined;\n this.memoryCache.set(key, { value, expiresAt });\n this.enforceMemoryLimit();\n }\n\n async del(key: string): Promise<void> {\n // Use Lambda proxy if available\n if (this.proxyClient) {\n await this.proxyClient.del(key);\n return;\n }\n\n // Use direct Redis connection\n if (this.redisClient) {\n await this.redisClient.del(key);\n return;\n }\n\n // Fall back to in-memory cache\n this.memoryCache.delete(key);\n }\n\n async delMany(keys: string[]): Promise<void> {\n // Use Lambda proxy if available\n if (this.proxyClient) {\n await this.proxyClient.delMany(keys);\n return;\n }\n\n // Use direct Redis connection\n if (this.redisClient) {\n if (keys.length > 0) {\n await this.redisClient.del(...keys);\n }\n return;\n }\n\n // Fall back to in-memory cache\n keys.forEach((key) => this.memoryCache.delete(key));\n }\n\n async wrap<T>(key: string, fn: () => Promise<T>, ttlMs?: number): Promise<T> {\n const cached = await this.get<T>(key);\n if (cached !== undefined) {\n return cached;\n }\n\n const value = await fn();\n if (value !== undefined) {\n await this.set(key, value, ttlMs);\n } else {\n this.logger.warn(`Computed cache value for key ${key} is undefined`);\n }\n return value;\n }\n\n private createRedisClient(\n options: InternalRedisCacheOptions,\n ): RedisLikeClient {\n const clusterNodes = options.resolvedClusterNodes;\n if (clusterNodes && clusterNodes.length > 0) {\n const nodes = clusterNodes.map((node) => ({\n host: node.host,\n port: node.port ?? options.resolvedPort ?? 6379,\n }));\n const client = new Redis.Cluster(nodes, {\n dnsLookup: (address, callback) => callback(null, address),\n redisOptions: {\n username: options.resolvedUsername,\n password: options.resolvedPassword,\n db: options.resolvedDb,\n tls: options.resolvedTlsEnabled ? {} : undefined,\n },\n });\n this.attachLogging(client);\n return client;\n }\n\n const client = options.resolvedUrl\n ? new Redis(\n options.resolvedUrl,\n options.resolvedTlsEnabled ? { tls: {} } : undefined,\n )\n : new Redis({\n host: options.resolvedHost,\n port: options.resolvedPort,\n username: options.resolvedUsername,\n password: options.resolvedPassword,\n db: options.resolvedDb,\n tls: options.resolvedTlsEnabled ? {} : undefined,\n });\n\n this.attachLogging(client);\n\n return client;\n }\n\n private attachLogging(client: RedisLikeClient): void {\n client.on('error', (err: Error) =>\n this.logger.error(\n `Redis cache connection error: ${err.message}`,\n err.stack,\n ),\n );\n\n client.on('ready', () =>\n this.logger.log('Redis/Valkey cache connection ready'),\n );\n }\n\n private serialize(value: unknown): string {\n return JSON.stringify(value);\n }\n\n private deserialize<T>(payload: string): T | undefined {\n try {\n return JSON.parse(payload) as T;\n } catch (error) {\n this.logger.error(\n `Failed to deserialize cached value: ${(error as Error).message}`,\n );\n return undefined;\n }\n }\n\n private enforceMemoryLimit(): void {\n if (this.memoryCache.size <= this.options.resolvedMax) {\n return;\n }\n const oldestKey = this.memoryCache.keys().next().value;\n if (oldestKey) {\n this.memoryCache.delete(oldestKey);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class ValkeyProxyClient {
|
|
2
|
+
private functionName;
|
|
3
|
+
constructor();
|
|
4
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
5
|
+
set<T>(key: string, value: T, ttlMs?: number): Promise<void>;
|
|
6
|
+
del(key: string): Promise<void>;
|
|
7
|
+
delMany(keys: string[]): Promise<void>;
|
|
8
|
+
private invoke;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=valkey-proxy-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"valkey-proxy-client.d.ts","sourceRoot":"/","sources":["valkey-proxy-client.ts"],"names":[],"mappings":"AAoBA,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,YAAY,CAAS;;IASvB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAK3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAI9B,MAAM;CAsBrB"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ValkeyProxyClient = void 0;
|
|
4
|
+
const client_lambda_1 = require("@aws-sdk/client-lambda");
|
|
5
|
+
const lambdaClient = new client_lambda_1.Lambda({
|
|
6
|
+
region: process.env.CLIPPY_AWS_DEFAULT_REGION || 'ap-southeast-2',
|
|
7
|
+
});
|
|
8
|
+
class ValkeyProxyClient {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.functionName = process.env.VALKEY_PROXY_FUNCTION_NAME;
|
|
11
|
+
if (!this.functionName) {
|
|
12
|
+
throw new Error('VALKEY_PROXY_FUNCTION_NAME environment variable not set');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async get(key) {
|
|
16
|
+
const response = await this.invoke({ operation: 'get', key });
|
|
17
|
+
return response.value;
|
|
18
|
+
}
|
|
19
|
+
async set(key, value, ttlMs) {
|
|
20
|
+
await this.invoke({ operation: 'set', key, value, ttlMs });
|
|
21
|
+
}
|
|
22
|
+
async del(key) {
|
|
23
|
+
await this.invoke({ operation: 'del', key });
|
|
24
|
+
}
|
|
25
|
+
async delMany(keys) {
|
|
26
|
+
await this.invoke({ operation: 'delMany', keys });
|
|
27
|
+
}
|
|
28
|
+
async invoke(payload) {
|
|
29
|
+
try {
|
|
30
|
+
const result = await lambdaClient.invoke({
|
|
31
|
+
FunctionName: this.functionName,
|
|
32
|
+
InvocationType: 'RequestResponse',
|
|
33
|
+
Payload: Buffer.from(JSON.stringify(payload)),
|
|
34
|
+
});
|
|
35
|
+
const responsePayload = JSON.parse(new TextDecoder().decode(result.Payload));
|
|
36
|
+
if (!responsePayload.success) {
|
|
37
|
+
throw new Error(`Valkey proxy error: ${responsePayload.error}`);
|
|
38
|
+
}
|
|
39
|
+
return responsePayload;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error('Failed to invoke valkey-proxy:', error);
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.ValkeyProxyClient = ValkeyProxyClient;
|
|
48
|
+
//# sourceMappingURL=valkey-proxy-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"valkey-proxy-client.js","sourceRoot":"/","sources":["valkey-proxy-client.ts"],"names":[],"mappings":";;;AAAA,0DAAgD;AAEhD,MAAM,YAAY,GAAG,IAAI,sBAAM,CAAC;IAC9B,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,gBAAgB;CAClE,CAAC,CAAC;AAgBH,MAAa,iBAAiB;IAG5B;QACE,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC;QAC3D,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,GAAW;QACtB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9D,OAAO,QAAQ,CAAC,KAAU,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,GAAW,EAAE,KAAQ,EAAE,KAAc;QAChD,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAc;QAC1B,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,OAAqB;QACxC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC;gBACvC,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,cAAc,EAAE,iBAAiB;gBACjC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;aAC9C,CAAC,CAAC;YAEH,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAChC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CACxB,CAAC;YAEnB,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,uBAAuB,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC;YAClE,CAAC;YAED,OAAO,eAAe,CAAC;QACzB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;YACvD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF;AAjDD,8CAiDC","sourcesContent":["import { Lambda } from '@aws-sdk/client-lambda';\n\nconst lambdaClient = new Lambda({\n region: process.env.CLIPPY_AWS_DEFAULT_REGION || 'ap-southeast-2',\n});\n\ninterface CacheRequest {\n operation: 'get' | 'set' | 'del' | 'delMany';\n key?: string;\n keys?: string[];\n value?: unknown;\n ttlMs?: number;\n}\n\ninterface CacheResponse {\n success: boolean;\n value?: unknown;\n error?: string;\n}\n\nexport class ValkeyProxyClient {\n private functionName: string;\n\n constructor() {\n this.functionName = process.env.VALKEY_PROXY_FUNCTION_NAME;\n if (!this.functionName) {\n throw new Error('VALKEY_PROXY_FUNCTION_NAME environment variable not set');\n }\n }\n\n async get<T>(key: string): Promise<T | undefined> {\n const response = await this.invoke({ operation: 'get', key });\n return response.value as T;\n }\n\n async set<T>(key: string, value: T, ttlMs?: number): Promise<void> {\n await this.invoke({ operation: 'set', key, value, ttlMs });\n }\n\n async del(key: string): Promise<void> {\n await this.invoke({ operation: 'del', key });\n }\n\n async delMany(keys: string[]): Promise<void> {\n await this.invoke({ operation: 'delMany', keys });\n }\n\n private async invoke(payload: CacheRequest): Promise<CacheResponse> {\n try {\n const result = await lambdaClient.invoke({\n FunctionName: this.functionName,\n InvocationType: 'RequestResponse',\n Payload: Buffer.from(JSON.stringify(payload)),\n });\n\n const responsePayload = JSON.parse(\n new TextDecoder().decode(result.Payload),\n ) as CacheResponse;\n\n if (!responsePayload.success) {\n throw new Error(`Valkey proxy error: ${responsePayload.error}`);\n }\n\n return responsePayload;\n } catch (error) {\n console.error('Failed to invoke valkey-proxy:', error);\n throw error;\n }\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clhq-cache-module",
|
|
3
|
+
"version": "1.1.0-alpha.90",
|
|
4
|
+
"description": "Reusable Redis cache module for NestJS services in the Clippy stack.",
|
|
5
|
+
"author": "Clippy Engineering",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/reacthub-pricematch/clhq-api-monorepo"
|
|
14
|
+
},
|
|
15
|
+
"registry": "https://registry.npmjs.org/",
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"registry": "https://registry.npmjs.org/",
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/reacthub-pricematch/clhq-api-monorepo/issues"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "yarn exec tsc -p tsconfig.json",
|
|
26
|
+
"clean:dist": "rimraf dist",
|
|
27
|
+
"clean:all": "yarn run clean:dist",
|
|
28
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
29
|
+
"lint": "eslint \"src/**/*.ts\" --fix",
|
|
30
|
+
"version:upgrade:alpha": "standard-version --prerelease alpha",
|
|
31
|
+
"release": "npm publish --tag alpha",
|
|
32
|
+
"release:alpha": "npm publish --tag alpha",
|
|
33
|
+
"release:prealpha": "",
|
|
34
|
+
"clean:build": "rimraf .build",
|
|
35
|
+
"clean:webpack": "rimraf .webpack",
|
|
36
|
+
"clean:serverless": "rimraf .serverless",
|
|
37
|
+
"sls:deploy": "sls deploy",
|
|
38
|
+
"sls:deploy:dev": "env-cmd -f ../../.env.dev sls deploy --stage dev",
|
|
39
|
+
"sls:remove:dev": "env-cmd -f ../../.env.dev sls remove --stage dev",
|
|
40
|
+
"sls:offline": "sls offline",
|
|
41
|
+
"sls:package": "sls package",
|
|
42
|
+
"sls:package2": "npm run clean:all&& npm run sls:package",
|
|
43
|
+
"sls:remove": "sls remove"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@nestjs/common": ">=11.0.0"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"ioredis": "^5.4.1"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@nestjs/common": "^11.1.5",
|
|
53
|
+
"@nestjs/platform-express": "^11.1.5",
|
|
54
|
+
"class-transformer": "0.5.1",
|
|
55
|
+
"class-validator": "0.14.2",
|
|
56
|
+
"prettier": "^3.6.1",
|
|
57
|
+
"rimraf": "^6.0.1",
|
|
58
|
+
"standard-version": "^9.5.0",
|
|
59
|
+
"typescript": "^5.8.3"
|
|
60
|
+
}
|
|
61
|
+
}
|