agent-smith/dist-in/cache.js
2026-02-26 19:41:09 +01:00

101 lines
7.6 KiB
JavaScript

import { getCache } from './commons/cache/index.js';
import { appEvents } from './events.js';
import pino from 'pino';
import path from 'path';
const logFile = path.join(process.cwd(), 'logs', 'cache.json');
const fileTransport = pino.transport({
target: 'pino/file',
options: { destination: logFile, mkdir: true }
});
const logger = pino({
level: process.env.PINO_LOG_LEVEL || 'info',
base: { product: 'cache' },
timestamp: pino.stdTimeFunctions.isoTime,
}, pino.multistream([
{ stream: fileTransport, level: 'info' }
]));
export class AppCache {
static instance;
// Dependencies: key -> [dependencies]
// Defines what each type DEPENDS ON.
// If 'categories' changes, any type that has 'categories' in its dependency list must be invalidated.
static DEPENDENCIES = {
'posts': ['categories', 'pictures'], // posts depend on categories and pictures
'pages': ['categories', 'pictures', 'translations'],
'categories': ['types'],
'translations': [], // widget/category translations (wt:* keys)
'feed': ['posts', 'pages', 'categories'],
'auth': [] // No dependencies, standalone
};
constructor() { }
static getInstance() {
if (!AppCache.instance) {
AppCache.instance = new AppCache();
}
return AppCache.instance;
}
async get(type) {
const cache = getCache();
const val = await cache.get(type);
return val;
}
async set(type, data, ttl) {
const cache = getCache();
await cache.set(type, data, ttl);
}
/**
* Silent cache invalidation — clears cache for the given type and
* cascades to dependents. Does NOT emit SSE events.
* Use `notify()` in route handlers for explicit SSE.
*/
async invalidate(type) {
const cache = getCache();
if (type === 'feed') {
await cache.flush('*-feed*');
await cache.flush('home-feed*');
}
else if (type === 'translations') {
await cache.flush('wt:*');
await cache.flush('page-details-*');
}
else {
await cache.del(type);
}
// Find types that depend on this type
const dependents = Object.keys(AppCache.DEPENDENCIES).filter(key => AppCache.DEPENDENCIES[key].includes(type));
logger.info({ type, dependents }, 'Cache invalidated');
if (dependents.length > 0) {
await Promise.all(dependents.map(dep => this.invalidate(dep)));
}
}
/**
* Flush cache entries by pattern. Silent — no SSE.
*/
async flush(pattern) {
const cache = getCache();
await cache.flush(pattern);
logger.info({ pattern: pattern || 'all' }, 'Cache flushed');
}
/**
* Emit exactly 1 SSE event to notify clients of a change.
* Call this in route handlers AFTER cache invalidation.
*
* @param type - Entity type (e.g. 'post', 'page', 'category', 'picture')
* @param id - Entity ID (null for list-level / system changes)
* @param action - The mutation that occurred
*/
notify(type, id, action) {
logger.info({ type, id, action }, 'Cache notify');
appEvents.emitUpdate(type, action, { id }, 'cache');
}
inspect() {
const cache = getCache();
return {
info: cache.info(),
dependencies: AppCache.DEPENDENCIES,
entries: cache.entries(),
};
}
}
export const appCache = AppCache.getInstance();
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FjaGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY2FjaGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFeEMsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQ3hCLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUV4QixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxNQUFNLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFFL0QsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUNqQyxNQUFNLEVBQUUsV0FBVztJQUNuQixPQUFPLEVBQUUsRUFBRSxXQUFXLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUU7Q0FDakQsQ0FBQyxDQUFDO0FBRUgsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUNmO0lBQ0ksS0FBSyxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxJQUFJLE1BQU07SUFDM0MsSUFBSSxFQUFFLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRTtJQUMxQixTQUFTLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLE9BQU87Q0FDM0MsRUFDRCxJQUFJLENBQUMsV0FBVyxDQUFDO0lBQ2IsRUFBRSxNQUFNLEVBQUUsYUFBYSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUU7Q0FDM0MsQ0FBQyxDQUNMLENBQUM7QUFHRixNQUFNLE9BQU8sUUFBUTtJQUNULE1BQU0sQ0FBQyxRQUFRLENBQVc7SUFFbEMsc0NBQXNDO0lBQ3RDLHFDQUFxQztJQUNyQyxzR0FBc0c7SUFDOUYsTUFBTSxDQUFDLFlBQVksR0FBNkI7UUFDcEQsT0FBTyxFQUFFLENBQUMsWUFBWSxFQUFFLFVBQVUsQ0FBQyxFQUFFLDBD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