fixed build libs
This commit is contained in:
parent
b03231b849
commit
42baadae38
26 changed files with 981 additions and 541 deletions
18
libs/core/cache/src/cache-factory.ts
vendored
18
libs/core/cache/src/cache-factory.ts
vendored
|
|
@ -1,4 +1,4 @@
|
|||
import { NamespacedCache } from './namespaced-cache';
|
||||
import { NamespacedCache, CacheAdapter } from './namespaced-cache';
|
||||
import { RedisCache } from './redis-cache';
|
||||
import type { CacheProvider, ICache } from './types';
|
||||
|
||||
|
|
@ -27,18 +27,18 @@ export class CacheFactory {
|
|||
export function createNamespacedCache(
|
||||
cache: CacheProvider | ICache | null | undefined,
|
||||
namespace: string
|
||||
): ICache {
|
||||
): CacheProvider {
|
||||
if (!cache) {
|
||||
return createNullCache();
|
||||
return new CacheAdapter(createNullCache());
|
||||
}
|
||||
|
||||
// Check if it's already an ICache
|
||||
if ('type' in cache) {
|
||||
return new NamespacedCache(cache as ICache, namespace);
|
||||
// Check if it's already a CacheProvider
|
||||
if ('getStats' in cache && 'health' in cache) {
|
||||
return new NamespacedCache(cache as CacheProvider, namespace);
|
||||
}
|
||||
|
||||
// Legacy CacheProvider support
|
||||
return createNullCache();
|
||||
// It's an ICache, wrap it first
|
||||
return new NamespacedCache(new CacheAdapter(cache as ICache), namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -70,4 +70,4 @@ function createNullCache(): ICache {
|
|||
disconnect: async () => {},
|
||||
isConnected: () => true,
|
||||
};
|
||||
}
|
||||
}
|
||||
132
libs/core/cache/src/namespaced-cache.ts
vendored
132
libs/core/cache/src/namespaced-cache.ts
vendored
|
|
@ -4,24 +4,22 @@ import type { CacheProvider, ICache } from './types';
|
|||
* A cache wrapper that automatically prefixes all keys with a namespace
|
||||
* Used to provide isolated cache spaces for different services
|
||||
*/
|
||||
export class NamespacedCache implements ICache {
|
||||
export class NamespacedCache implements CacheProvider {
|
||||
private readonly prefix: string;
|
||||
public readonly type: string;
|
||||
|
||||
constructor(
|
||||
private readonly cache: ICache,
|
||||
private readonly cache: CacheProvider,
|
||||
private readonly namespace: string
|
||||
) {
|
||||
this.prefix = `${namespace}:`;
|
||||
this.type = cache.type;
|
||||
}
|
||||
|
||||
async get<T = any>(key: string): Promise<T | null> {
|
||||
return this.cache.get(`${this.prefix}${key}`);
|
||||
}
|
||||
|
||||
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
|
||||
return this.cache.set(`${this.prefix}${key}`, value, ttl);
|
||||
async set<T>(key: string, value: T, options?: number | { ttl?: number }): Promise<T | null> {
|
||||
return this.cache.set(`${this.prefix}${key}`, value, options);
|
||||
}
|
||||
|
||||
async del(key: string): Promise<void> {
|
||||
|
|
@ -32,10 +30,6 @@ export class NamespacedCache implements ICache {
|
|||
return this.cache.exists(`${this.prefix}${key}`);
|
||||
}
|
||||
|
||||
async ttl(key: string): Promise<number> {
|
||||
return this.cache.ttl(`${this.prefix}${key}`);
|
||||
}
|
||||
|
||||
async keys(pattern: string = '*'): Promise<string[]> {
|
||||
const fullPattern = `${this.prefix}${pattern}`;
|
||||
const keys = await this.cache.keys(fullPattern);
|
||||
|
|
@ -47,50 +41,10 @@ export class NamespacedCache implements ICache {
|
|||
// Clear only keys with this namespace prefix
|
||||
const keys = await this.cache.keys(`${this.prefix}*`);
|
||||
if (keys.length > 0) {
|
||||
await Promise.all(keys.map(key => this.cache.del(key)));
|
||||
await Promise.all(keys.map(key => this.cache.del(key.substring(this.prefix.length))));
|
||||
}
|
||||
}
|
||||
|
||||
async mget<T>(keys: string[]): Promise<(T | null)[]> {
|
||||
const prefixedKeys = keys.map(k => `${this.prefix}${k}`);
|
||||
return this.cache.mget(prefixedKeys);
|
||||
}
|
||||
|
||||
async mset<T>(items: Record<string, T>, ttl?: number): Promise<void> {
|
||||
const prefixedItems: Record<string, T> = {};
|
||||
for (const [key, value] of Object.entries(items)) {
|
||||
prefixedItems[`${this.prefix}${key}`] = value;
|
||||
}
|
||||
return this.cache.mset(prefixedItems, ttl);
|
||||
}
|
||||
|
||||
async mdel(keys: string[]): Promise<void> {
|
||||
const prefixedKeys = keys.map(k => `${this.prefix}${k}`);
|
||||
return this.cache.mdel(prefixedKeys);
|
||||
}
|
||||
|
||||
async size(): Promise<number> {
|
||||
const keys = await this.keys('*');
|
||||
return keys.length;
|
||||
}
|
||||
|
||||
async flush(): Promise<void> {
|
||||
return this.clear();
|
||||
}
|
||||
|
||||
async ping(): Promise<boolean> {
|
||||
return this.cache.ping();
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {
|
||||
// Namespaced cache doesn't own the connection, so we don't disconnect
|
||||
// The underlying cache instance should be disconnected by its owner
|
||||
}
|
||||
|
||||
isConnected(): boolean {
|
||||
return this.cache.isConnected();
|
||||
}
|
||||
|
||||
getNamespace(): string {
|
||||
return this.namespace;
|
||||
}
|
||||
|
|
@ -98,4 +52,80 @@ export class NamespacedCache implements ICache {
|
|||
getFullPrefix(): string {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
// CacheProvider methods
|
||||
getStats() {
|
||||
return this.cache.getStats();
|
||||
}
|
||||
|
||||
async health(): Promise<boolean> {
|
||||
return this.cache.health();
|
||||
}
|
||||
|
||||
async waitForReady(timeout?: number): Promise<void> {
|
||||
return this.cache.waitForReady(timeout);
|
||||
}
|
||||
|
||||
isReady(): boolean {
|
||||
return this.cache.isReady();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter to convert ICache to CacheProvider
|
||||
*/
|
||||
export class CacheAdapter implements CacheProvider {
|
||||
constructor(private readonly cache: ICache) {}
|
||||
|
||||
async get<T = any>(key: string): Promise<T | null> {
|
||||
return this.cache.get(key);
|
||||
}
|
||||
|
||||
async set<T>(key: string, value: T, options?: number | { ttl?: number }): Promise<T | null> {
|
||||
const ttl = typeof options === 'number' ? options : options?.ttl;
|
||||
await this.cache.set(key, value, ttl);
|
||||
return null;
|
||||
}
|
||||
|
||||
async del(key: string): Promise<void> {
|
||||
return this.cache.del(key);
|
||||
}
|
||||
|
||||
async exists(key: string): Promise<boolean> {
|
||||
return this.cache.exists(key);
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
return this.cache.clear();
|
||||
}
|
||||
|
||||
async keys(pattern: string): Promise<string[]> {
|
||||
return this.cache.keys(pattern);
|
||||
}
|
||||
|
||||
getStats() {
|
||||
return {
|
||||
hits: 0,
|
||||
misses: 0,
|
||||
errors: 0,
|
||||
hitRate: 0,
|
||||
total: 0,
|
||||
uptime: process.uptime(),
|
||||
};
|
||||
}
|
||||
|
||||
async health(): Promise<boolean> {
|
||||
return this.cache.ping();
|
||||
}
|
||||
|
||||
async waitForReady(): Promise<void> {
|
||||
// ICache doesn't have waitForReady, so just check connection
|
||||
if (!this.cache.isConnected()) {
|
||||
throw new Error('Cache not connected');
|
||||
}
|
||||
}
|
||||
|
||||
isReady(): boolean {
|
||||
return this.cache.isConnected();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { beforeEach, describe, expect, it, mock } from 'bun:test';
|
||||
import { CacheFactory, createNamespacedCache } from './cache-factory';
|
||||
import { generateKey } from './key-generator';
|
||||
import type { ICache } from './types';
|
||||
import { CacheFactory, createNamespacedCache } from '../src/cache-factory';
|
||||
import { generateKey } from '../src/key-generator';
|
||||
import type { ICache } from '../src/types';
|
||||
|
||||
describe('CacheFactory', () => {
|
||||
it('should create null cache when no config provided', () => {
|
||||
|
|
@ -49,7 +49,8 @@ describe('NamespacedCache', () => {
|
|||
it('should create namespaced cache', () => {
|
||||
const nsCache = createNamespacedCache(mockCache, 'sub-namespace');
|
||||
expect(nsCache).toBeDefined();
|
||||
expect(nsCache.type).toBe('mock');
|
||||
expect(nsCache).toHaveProperty('get');
|
||||
expect(nsCache).toHaveProperty('set');
|
||||
});
|
||||
|
||||
it('should prefix keys with namespace', async () => {
|
||||
|
|
@ -58,10 +59,12 @@ describe('NamespacedCache', () => {
|
|||
expect(mockCache.set).toHaveBeenCalledWith('test:key', 'value', undefined);
|
||||
});
|
||||
|
||||
it('should handle null cache gracefully', () => {
|
||||
it('should handle null cache gracefully', async () => {
|
||||
const nsCache = createNamespacedCache(null, 'test');
|
||||
expect(nsCache).toBeDefined();
|
||||
expect(nsCache.type).toBe('null');
|
||||
// Should return null for get operations
|
||||
const value = await nsCache.get('key');
|
||||
expect(value).toBeNull();
|
||||
});
|
||||
|
||||
it('should prefix multiple operations', async () => {
|
||||
Loading…
Add table
Add a link
Reference in a new issue