added first cache
This commit is contained in:
parent
eee6135867
commit
3fc123eca3
9 changed files with 210 additions and 9 deletions
21
libs/cache/package.json
vendored
Normal file
21
libs/cache/package.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "@stock-bot/cache",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Caching library for Redis and in-memory providers",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"dev": "tsc --watch"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ioredis": "^5.3.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.10.0",
|
||||||
|
"typescript": "^5.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bun-types": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
26
libs/cache/src/index.ts
vendored
Normal file
26
libs/cache/src/index.ts
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { RedisCache } from './providers/redis-cache';
|
||||||
|
import { MemoryCache } from './providers/memory-cache';
|
||||||
|
import type { CacheProvider, CacheOptions } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for creating cache providers.
|
||||||
|
*
|
||||||
|
* @param type 'redis' | 'memory'
|
||||||
|
* @param options configuration for the cache
|
||||||
|
*/
|
||||||
|
export function createCache(
|
||||||
|
type: 'redis' | 'memory',
|
||||||
|
options: CacheOptions = {}
|
||||||
|
): CacheProvider {
|
||||||
|
if (type === 'redis') {
|
||||||
|
return new RedisCache(options);
|
||||||
|
}
|
||||||
|
return new MemoryCache(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
CacheProvider,
|
||||||
|
CacheOptions,
|
||||||
|
RedisCache,
|
||||||
|
MemoryCache
|
||||||
|
};
|
||||||
48
libs/cache/src/providers/memory-cache.ts
vendored
Normal file
48
libs/cache/src/providers/memory-cache.ts
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { CacheProvider } from '../types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple in-memory cache provider.
|
||||||
|
*/
|
||||||
|
export class MemoryCache implements CacheProvider {
|
||||||
|
private store = new Map<string, any>();
|
||||||
|
private defaultTTL: number;
|
||||||
|
private keyPrefix: string;
|
||||||
|
|
||||||
|
constructor(options: { ttl?: number; keyPrefix?: string } = {}) {
|
||||||
|
this.defaultTTL = options.ttl ?? 3600;
|
||||||
|
this.keyPrefix = options.keyPrefix ?? 'cache:';
|
||||||
|
}
|
||||||
|
|
||||||
|
private getKey(key: string): string {
|
||||||
|
return `${this.keyPrefix}${key}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async get<T>(key: string): Promise<T | null> {
|
||||||
|
const fullKey = this.getKey(key);
|
||||||
|
const entry = this.store.get(fullKey);
|
||||||
|
if (!entry) return null;
|
||||||
|
if (entry.expiry < Date.now()) {
|
||||||
|
this.store.delete(fullKey);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return entry.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
|
||||||
|
const fullKey = this.getKey(key);
|
||||||
|
const expiry = Date.now() + 1000 * (ttl ?? this.defaultTTL);
|
||||||
|
this.store.set(fullKey, { value, expiry });
|
||||||
|
}
|
||||||
|
|
||||||
|
async del(key: string): Promise<void> {
|
||||||
|
this.store.delete(this.getKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
async exists(key: string): Promise<boolean> {
|
||||||
|
return (await this.get(key)) !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear(): Promise<void> {
|
||||||
|
this.store.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
59
libs/cache/src/providers/redis-cache.ts
vendored
Normal file
59
libs/cache/src/providers/redis-cache.ts
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import Redis, { RedisOptions } from 'ioredis';
|
||||||
|
import { CacheProvider, CacheOptions } from '../types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redis-based cache provider implementing CacheProvider interface.
|
||||||
|
*/
|
||||||
|
export class RedisCache implements CacheProvider {
|
||||||
|
private redis: Redis;
|
||||||
|
private defaultTTL: number;
|
||||||
|
private keyPrefix: string;
|
||||||
|
|
||||||
|
constructor(options: CacheOptions = {}) {
|
||||||
|
if (options.redisUrl) {
|
||||||
|
this.redis = new Redis(options.redisUrl);
|
||||||
|
} else {
|
||||||
|
this.redis = new Redis(options.redisOptions as RedisOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.defaultTTL = options.ttl ?? 3600; // default 1 hour
|
||||||
|
this.keyPrefix = options.keyPrefix ?? 'cache:';
|
||||||
|
}
|
||||||
|
|
||||||
|
private getKey(key: string): string {
|
||||||
|
return `${this.keyPrefix}${key}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async get<T>(key: string): Promise<T | null> {
|
||||||
|
const fullKey = this.getKey(key);
|
||||||
|
const val = await this.redis.get(fullKey);
|
||||||
|
if (val === null) return null;
|
||||||
|
try {
|
||||||
|
return JSON.parse(val) as T;
|
||||||
|
} catch {
|
||||||
|
return (val as unknown) as T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
|
||||||
|
const fullKey = this.getKey(key);
|
||||||
|
const str = typeof value === 'string' ? (value as unknown as string) : JSON.stringify(value);
|
||||||
|
const expiry = ttl ?? this.defaultTTL;
|
||||||
|
await this.redis.set(fullKey, str, 'EX', expiry);
|
||||||
|
}
|
||||||
|
|
||||||
|
async del(key: string): Promise<void> {
|
||||||
|
await this.redis.del(this.getKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
async exists(key: string): Promise<boolean> {
|
||||||
|
const exists = await this.redis.exists(this.getKey(key));
|
||||||
|
return exists === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear(): Promise<void> {
|
||||||
|
const pattern = `${this.keyPrefix}*`;
|
||||||
|
const keys = await this.redis.keys(pattern);
|
||||||
|
if (keys.length) await this.redis.del(...keys);
|
||||||
|
}
|
||||||
|
}
|
||||||
34
libs/cache/src/types.ts
vendored
Normal file
34
libs/cache/src/types.ts
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import type { RedisOptions as IORedisOptions } from 'ioredis';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for a generic cache provider.
|
||||||
|
*/
|
||||||
|
export interface CacheProvider {
|
||||||
|
get<T>(key: string): Promise<T | null>;
|
||||||
|
set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
||||||
|
del(key: string): Promise<void>;
|
||||||
|
exists(key: string): Promise<boolean>;
|
||||||
|
clear(): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for configuring the cache provider.
|
||||||
|
*/
|
||||||
|
export interface CacheOptions {
|
||||||
|
/**
|
||||||
|
* Full Redis connection string (e.g., redis://localhost:6379)
|
||||||
|
*/
|
||||||
|
redisUrl?: string;
|
||||||
|
/**
|
||||||
|
* Raw ioredis connection options if not using a URL.
|
||||||
|
*/
|
||||||
|
redisOptions?: IORedisOptions;
|
||||||
|
/**
|
||||||
|
* Default time-to-live for cache entries (in seconds).
|
||||||
|
*/
|
||||||
|
ttl?: number;
|
||||||
|
/**
|
||||||
|
* Prefix to use for all cache keys.
|
||||||
|
*/
|
||||||
|
keyPrefix?: string;
|
||||||
|
}
|
||||||
10
libs/cache/tsconfig.json
vendored
Normal file
10
libs/cache/tsconfig.json
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"declaration": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
||||||
|
}
|
||||||
18
package.json
18
package.json
|
|
@ -6,7 +6,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "turbo run dev",
|
"dev": "turbo run dev",
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
"build:libs": "pwsh ./scripts/build-libs.ps1",
|
"build:libs": "powershell ./scripts/build-libs.ps1",
|
||||||
"test": "turbo run test",
|
"test": "turbo run test",
|
||||||
"test:watch": "bun test --watch",
|
"test:watch": "bun test --watch",
|
||||||
"test:coverage": "bun test --coverage",
|
"test:coverage": "bun test --coverage",
|
||||||
|
|
@ -19,14 +19,14 @@
|
||||||
"clean": "turbo run clean",
|
"clean": "turbo run clean",
|
||||||
"start": "turbo run start",
|
"start": "turbo run start",
|
||||||
"backtest": "turbo run backtest",
|
"backtest": "turbo run backtest",
|
||||||
"docker:start": "pwsh ./scripts/docker.ps1 start",
|
"docker:start": "powershell ./scripts/docker.ps1 start",
|
||||||
"docker:stop": "pwsh ./scripts/docker.ps1 stop",
|
"docker:stop": "powershell ./scripts/docker.ps1 stop",
|
||||||
"docker:restart": "pwsh ./scripts/docker.ps1 restart",
|
"docker:restart": "powershell ./scripts/docker.ps1 restart",
|
||||||
"docker:status": "pwsh ./scripts/docker.ps1 status",
|
"docker:status": "powershell ./scripts/docker.ps1 status",
|
||||||
"docker:logs": "pwsh ./scripts/docker.ps1 logs",
|
"docker:logs": "powershell ./scripts/docker.ps1 logs",
|
||||||
"docker:reset": "pwsh ./scripts/docker.ps1 reset",
|
"docker:reset": "powershell ./scripts/docker.ps1 reset",
|
||||||
"docker:admin": "pwsh ./scripts/docker.ps1 admin",
|
"docker:admin": "powershell ./scripts/docker.ps1 admin",
|
||||||
"docker:monitoring": "pwsh ./scripts/docker.ps1 monitoring",
|
"docker:monitoring": "powershell ./scripts/docker.ps1 monitoring",
|
||||||
"infra:up": "docker-compose up -d dragonfly postgres questdb mongodb",
|
"infra:up": "docker-compose up -d dragonfly postgres questdb mongodb",
|
||||||
"infra:down": "docker-compose down",
|
"infra:down": "docker-compose down",
|
||||||
"infra:reset": "docker-compose down -v && docker-compose up -d dragonfly postgres questdb mongodb",
|
"infra:reset": "docker-compose down -v && docker-compose up -d dragonfly postgres questdb mongodb",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ $libs = @(
|
||||||
"logger", # Logging utilities - depends on types
|
"logger", # Logging utilities - depends on types
|
||||||
"config", # Configuration - depends on types
|
"config", # Configuration - depends on types
|
||||||
"utils", # Utilities - depends on types and config
|
"utils", # Utilities - depends on types and config
|
||||||
|
"cache", # Cache - depends on types and logger
|
||||||
"http-client", # HTTP client - depends on types, config, logger
|
"http-client", # HTTP client - depends on types, config, logger
|
||||||
"postgres-client", # PostgreSQL client - depends on types, config, logger
|
"postgres-client", # PostgreSQL client - depends on types, config, logger
|
||||||
"mongodb-client", # MongoDB client - depends on types, config, logger
|
"mongodb-client", # MongoDB client - depends on types, config, logger
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@
|
||||||
{ "path": "./libs/postgres-client" },
|
{ "path": "./libs/postgres-client" },
|
||||||
{ "path": "./libs/questdb-client" },
|
{ "path": "./libs/questdb-client" },
|
||||||
{ "path": "./libs/types" },
|
{ "path": "./libs/types" },
|
||||||
|
{ "path": "./libs/cache" },
|
||||||
|
{ "path": "./libs/logger" },
|
||||||
{ "path": "./libs/utils" },
|
{ "path": "./libs/utils" },
|
||||||
{ "path": "./libs/event-bus" },
|
{ "path": "./libs/event-bus" },
|
||||||
{ "path": "./libs/data-frame" },
|
{ "path": "./libs/data-frame" },
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue