@stacksjs/cache 0.70.87 → 0.70.88

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/cache",
3
3
  "type": "module",
4
- "version": "0.70.87",
4
+ "version": "0.70.88",
5
5
  "description": "Caching the Stacks way.",
6
6
  "author": "Chris Breuer",
7
7
  "contributors": [
@@ -54,7 +54,7 @@
54
54
  "prepublishOnly": "bun run build"
55
55
  },
56
56
  "devDependencies": {
57
- "@stacksjs/config": "0.70.87",
57
+ "@stacksjs/config": "0.70.88",
58
58
  "@stacksjs/ts-cache": "^0.1.4"
59
59
  }
60
60
  }
@@ -1,142 +0,0 @@
1
- import { CacheManager, MemoryDriver, RedisDriver } from '@stacksjs/ts-cache';
2
- import { SingleStoreCacheStore } from './singlestore';
3
- import type { CacheDriver, CacheStats } from '@stacksjs/types';
4
- import type { SingleStoreCacheOptions } from './singlestore';
5
- // Re-export the SingleStore store + its options for advanced usage
6
- export type { SingleStoreCacheOptions } from './singlestore';
7
- /**
8
- * Create a memory cache driver
9
- */
10
- export declare function createMemoryCache(options?: MemoryOptions): CacheDriver;
11
- /**
12
- * Create a Redis cache driver
13
- */
14
- export declare function createRedisCache(options?: RedisOptions): CacheDriver;
15
- /**
16
- * Create a SingleStore cache driver.
17
- *
18
- * Wraps a SingleStore-backed store in the same `StacksCache` façade the memory
19
- * and Redis drivers use, so it inherits the stampede-protection + tag logic.
20
- * The store implements the `CacheManager` surface `StacksCache` consumes; the
21
- * cast bridges the structural gap (it is not a literal `CacheManager` subclass).
22
- */
23
- export declare function createSingleStoreCache(options?: SingleStoreCacheOptions): CacheDriver;
24
- /**
25
- * Create a cache driver based on the specified type
26
- */
27
- export declare function createCache(driver: 'memory', options?: MemoryOptions): CacheDriver;
28
- export declare function createCache(driver: 'redis', options?: RedisOptions): CacheDriver;
29
- export declare function createCache(driver: 'singlestore', options?: SingleStoreCacheOptions): CacheDriver;
30
- /**
31
- * Default memory cache instance
32
- */
33
- export declare const memory: CacheDriver;
34
- /**
35
- * Default cache instance (memory)
36
- */
37
- export declare const cache: CacheDriver;
38
- /**
39
- * Memory cache options
40
- */
41
- export declare interface MemoryOptions {
42
- stdTTL?: number
43
- checkPeriod?: number
44
- maxKeys?: number
45
- useClones?: boolean
46
- prefix?: string
47
- }
48
- /**
49
- * Redis cache options
50
- */
51
- export declare interface RedisOptions {
52
- url?: string
53
- host?: string
54
- port?: number
55
- username?: string
56
- password?: string
57
- database?: number
58
- tls?: boolean
59
- stdTTL?: number
60
- prefix?: string
61
- }
62
- /**
63
- * Stacks Cache Wrapper
64
- *
65
- * Wraps ts-cache's CacheManager to provide a consistent API
66
- * that matches the Stacks CacheDriver interface.
67
- */
68
- export declare class StacksCache implements CacheDriver {
69
- constructor(manager: CacheManager, opts?: { inflightTimeoutMs?: number });
70
- get<T>(key: string): Promise<T | undefined>;
71
- mget<T>(keys: string[]): Promise<Record<string, T>>;
72
- set<T>(key: string, value: T, ttl?: number): Promise<boolean>;
73
- mset<T>(entries: Array<{ key: string, value: T, ttl?: number }>): Promise<boolean>;
74
- setForever<T>(key: string, value: T): Promise<boolean>;
75
- getOrSet<T>(key: string, fetcher: () => T | Promise<T>, ttl?: number): Promise<T>;
76
- remember<T>(key: string, ttl: number, callback: () => T | Promise<T>): Promise<T>;
77
- rememberForever<T>(key: string, callback: () => T | Promise<T>): Promise<T>;
78
- del(keys: string | string[]): Promise<number>;
79
- has(key: string): Promise<boolean>;
80
- missing(key: string): Promise<boolean>;
81
- remove(key: string): Promise<void>;
82
- deleteMany(keys: string[]): Promise<number>;
83
- clear(): Promise<void>;
84
- flush(): Promise<void>;
85
- keys(pattern?: string): Promise<string[]>;
86
- getTtl(key: string): Promise<number | undefined>;
87
- ttl(key: string, ttl: number): Promise<boolean>;
88
- take<T>(key: string): Promise<T | undefined>;
89
- getStats(): Promise<CacheStats>;
90
- close(): Promise<void>;
91
- disconnect(): Promise<void>;
92
- get cacheManager(): CacheManager;
93
- tags(tags: readonly string[]): TaggedCache;
94
- }
95
- /**
96
- * Tag-scoped wrapper around `StacksCache`. Records every write under
97
- * the named tag(s) so `flush()` can invalidate cascade-style without the
98
- * caller having to track keys themselves.
99
- *
100
- * The tag index is stored as `string[]` on disk (so it survives the
101
- * Redis driver's serializer), but operated on in memory as a `Set` so
102
- * each write is `O(1)` instead of `O(N)` over the existing index.
103
- *
104
- * `flush()` uses a per-tag in-process mutex so concurrent flushes don't
105
- * race-and-lose keys: without it, two parallel flushes could both read
106
- * the index, both delete, and the second flush would succeed against
107
- * the now-empty index — leaving any keys added between the two reads
108
- * orphaned.
109
- */
110
- export declare class TaggedCache {
111
- constructor(cache: StacksCache, tags: readonly string[]);
112
- put<T>(key: string, value: T, ttl?: number): Promise<boolean>;
113
- set<T>(key: string, value: T, ttl?: number): Promise<boolean>;
114
- setForever<T>(key: string, value: T): Promise<boolean>;
115
- remember<T>(key: string, ttl: number, callback: () => T | Promise<T>): Promise<T>;
116
- rememberForever<T>(key: string, callback: () => T | Promise<T>): Promise<T>;
117
- get<T>(key: string): Promise<T | undefined>;
118
- has(key: string): Promise<boolean>;
119
- flush(): Promise<number>;
120
- }
121
- export { SingleStoreCacheStore } from './singlestore';
122
- // Re-export ts-cache utilities for advanced usage
123
- export {
124
- CacheManager,
125
- MemoryDriver,
126
- RedisDriver,
127
- } from '@stacksjs/ts-cache';
128
- // Re-export patterns and utilities
129
- export {
130
- // Patterns
131
- CacheAsidePattern,
132
- MultiLevelPattern,
133
- RefreshAheadPattern,
134
- WriteThroughPattern,
135
- // Utilities
136
- BatchOperations,
137
- CacheInvalidation,
138
- CacheLock,
139
- CircuitBreaker,
140
- memoize,
141
- RateLimiter,
142
- } from '@stacksjs/ts-cache';
@@ -1,35 +0,0 @@
1
- export declare interface SingleStoreCacheOptions {
2
- host?: string
3
- port?: number
4
- username?: string
5
- password?: string
6
- database?: string
7
- table?: string
8
- ssl?: boolean
9
- prefix?: string
10
- stdTTL?: number
11
- }
12
- declare interface CacheStatsShape {
13
- hits: number
14
- misses: number
15
- keys: number
16
- ksize: number
17
- vsize: number
18
- }
19
- export declare class SingleStoreCacheStore {
20
- constructor(options?: SingleStoreCacheOptions);
21
- get<T>(key: string): Promise<T | undefined>;
22
- mget<T>(keys: string[]): Promise<Record<string, T>>;
23
- set<T>(key: string, value: T, ttl?: number): Promise<boolean>;
24
- mset<T>(entries: Array<{ key: string, value: T, ttl?: number }>): Promise<boolean>;
25
- fetch<T>(key: string, fn: () => T | Promise<T>, ttl?: number): Promise<T>;
26
- del(keys: string | string[]): Promise<number>;
27
- has(key: string): Promise<boolean>;
28
- flush(): Promise<void>;
29
- keys(pattern?: string): Promise<string[]>;
30
- getTtl(key: string): Promise<number | undefined>;
31
- ttl(key: string, ttl: number): Promise<boolean>;
32
- take<T>(key: string): Promise<T | undefined>;
33
- getStats(): Promise<CacheStatsShape>;
34
- close(): Promise<void>;
35
- }
package/dist/index.d.ts DELETED
@@ -1,25 +0,0 @@
1
- /**
2
- * @stacksjs/cache
3
- *
4
- * A high-performance, type-safe caching library powered by ts-cache.
5
- *
6
- * @example
7
- * ```ts
8
- * import { cache, createCache, createMemoryCache, createRedisCache } from '@stacksjs/cache'
9
- *
10
- * // Use the default memory cache
11
- * await cache.set('key', 'value', 60) // 60 second TTL
12
- * const value = await cache.get('key')
13
- *
14
- * // Create a custom Redis cache
15
- * const redisCache = createRedisCache({
16
- * host: 'localhost',
17
- * port: 6379,
18
- * prefix: 'myapp',
19
- * })
20
- *
21
- * // Use the factory function
22
- * const customCache = createCache('memory', { maxKeys: 1000 })
23
- * ```
24
- */
25
- export * from './drivers/index';
package/dist/index.js DELETED
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{createCache as V}from"@stacksjs/ts-cache";var{SQL:Y}=globalThis.Bun;function Z(R){return`\`${R.replace(/`/g,"``")}\``}class L{sql;table;qualified;prefix;stdTTL;ready=null;hits=0;misses=0;constructor(R={}){let{host:U="127.0.0.1",port:z=3306,username:B="root",password:D="",database:E="stacks",table:H="stacks_cache",ssl:N=!1}=R;this.table=H,this.qualified=Z(H),this.prefix=R.prefix??"",this.stdTTL=R.stdTTL??0;let W=D?`${B}:${encodeURIComponent(D)}`:B,O=N?"?ssl=true":"";this.sql=new Y(`mysql://${W}@${U}:${z}/${E}${O}`)}async ensureReady(){if(!this.ready)this.ready=this.sql.unsafe(`CREATE ROWSTORE TABLE IF NOT EXISTS ${this.qualified} ( cache_key VARCHAR(255) NOT NULL, value LONGTEXT NOT NULL, expires_at BIGINT NULL, PRIMARY KEY (cache_key) )`).then(()=>{return});await this.ready}k(R){return this.prefix?`${this.prefix}:${R}`:R}nowMs(){return Date.now()}expiryFor(R){let U=R??this.stdTTL;if(!U||U<=0)return null;return this.nowMs()+U*1000}async get(R){await this.ensureReady();let z=(await this.sql.unsafe(`SELECT value, expires_at FROM ${this.qualified} WHERE cache_key = ? LIMIT 1`,[this.k(R)]))[0];if(!z){this.misses++;return}if(z.expires_at!=null&&z.expires_at<=this.nowMs()){await this.del(R),this.misses++;return}return this.hits++,JSON.parse(z.value)}async mget(R){let U={};for(let z of R){let B=await this.get(z);if(B!==void 0)U[z]=B}return U}async set(R,U,z){await this.ensureReady();let B=this.expiryFor(z);return await this.sql.unsafe(`INSERT INTO ${this.qualified} (cache_key, value, expires_at) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value), expires_at = VALUES(expires_at)`,[this.k(R),JSON.stringify(U),B]),!0}async mset(R){for(let U of R)await this.set(U.key,U.value,U.ttl);return!0}async fetch(R,U,z){let B=await this.get(R);if(B!==void 0)return B;let D=await U();return await this.set(R,D,z),D}async del(R){await this.ensureReady();let U=Array.isArray(R)?R:[R];if(U.length===0)return 0;let z=U.map(()=>"?").join(", "),D=(await this.sql.unsafe(`DELETE FROM ${this.qualified} WHERE cache_key IN (${z})`,U.map((E)=>this.k(E)))).affectedRows;return typeof D==="number"?D:U.length}async has(R){return await this.get(R)!==void 0}async flush(){if(await this.ensureReady(),this.prefix)await this.sql.unsafe(`DELETE FROM ${this.qualified} WHERE cache_key LIKE ?`,[`${this.prefix}:%`]);else await this.sql.unsafe(`TRUNCATE TABLE ${this.qualified}`)}async keys(R){await this.ensureReady();let U=`${this.prefix?`${this.prefix}:`:""}${R?R.replace(/\*/g,"%"):"%"}`,z=await this.sql.unsafe(`SELECT cache_key FROM ${this.qualified} WHERE cache_key LIKE ? AND (expires_at IS NULL OR expires_at > ?)`,[U,this.nowMs()]),B=this.prefix?this.prefix.length+1:0;return z.map((D)=>B?D.cache_key.slice(B):D.cache_key)}async getTtl(R){await this.ensureReady();let z=(await this.sql.unsafe(`SELECT expires_at FROM ${this.qualified} WHERE cache_key = ? LIMIT 1`,[this.k(R)]))[0];if(!z)return;return z.expires_at==null?0:z.expires_at}async ttl(R,U){await this.ensureReady();let z=this.expiryFor(U);return((await this.sql.unsafe(`UPDATE ${this.qualified} SET expires_at = ? WHERE cache_key = ?`,[z,this.k(R)])).affectedRows??0)>0}async take(R){let U=await this.get(R);if(U!==void 0)await this.del(R);return U}async getStats(){await this.ensureReady();let R=await this.sql.unsafe(`SELECT COUNT(*) AS n FROM ${this.qualified} WHERE expires_at IS NULL OR expires_at > ?`,[this.nowMs()]);return{hits:this.hits,misses:this.misses,keys:Number(R[0]?.n??0),ksize:0,vsize:0}}async close(){await this.sql.end()}}import{CacheManager as m,MemoryDriver as h,RedisDriver as v}from"@stacksjs/ts-cache";import{CacheAsidePattern as d,MultiLevelPattern as u,RefreshAheadPattern as c,WriteThroughPattern as p,BatchOperations as g,CacheInvalidation as r,CacheLock as l,CircuitBreaker as n,memoize as i,RateLimiter as y}from"@stacksjs/ts-cache";class J{manager;inflight=new Map;inflightTimeoutMs;constructor(R,U={}){this.manager=R,this.inflightTimeoutMs=U.inflightTimeoutMs??30000}async get(R){return await this.manager.get(R)}async mget(R){return await this.manager.mget(R)}async set(R,U,z){return await this.manager.set(R,U,z)}async mset(R){return await this.manager.mset(R)}async setForever(R,U){return await this.manager.set(R,U,0)}async getOrSet(R,U,z){let B=await this.manager.get(R);if(B!==void 0)return B;let D=this.inflight.get(R);if(D)return await D;let E=(async()=>{try{return await this.manager.fetch(R,U,z)}finally{this.inflight.delete(R)}})(),H=this.inflightTimeoutMs,N=new Promise((W,O)=>{let X=setTimeout(()=>{if(this.inflight.get(R)===E)this.inflight.delete(R);O(Error(`[cache] getOrSet('${R}') timed out after ${H}ms`))},H);E.then(()=>clearTimeout(X),()=>clearTimeout(X))});return this.inflight.set(R,E),await Promise.race([E,N])}async remember(R,U,z){return await this.getOrSet(R,z,U)}async rememberForever(R,U){return this.getOrSet(R,U,0)}async del(R){return await this.manager.del(R)}async has(R){return await this.manager.has(R)}async missing(R){return!await this.manager.has(R)}async remove(R){await this.manager.del(R)}async deleteMany(R){return await this.manager.del(R)}async clear(){await this.manager.flush()}async flush(){await this.manager.flush()}async keys(R){return await this.manager.keys(R)}async getTtl(R){return await this.manager.getTtl(R)}async ttl(R,U){return await this.manager.ttl(R,U)}async take(R){return await this.manager.take(R)}async getStats(){let R=await this.manager.getStats(),U=R.hits+R.misses;return{hits:R.hits,misses:R.misses,keys:R.keys,size:R.ksize+R.vsize,hitRate:U>0?R.hits/U:0}}async close(){await this.manager.close()}async disconnect(){await this.manager.close()}get cacheManager(){return this.manager}tags(R){return new G(this,R)}}class G{cache;tags;static TAG_PREFIX="__stacks_tag__:";static flushLocks=new Map;constructor(R,U){this.cache=R;this.tags=U;if(!U||U.length===0)throw Error("cache.tags(...) requires at least one tag")}tagKey(R){return`${G.TAG_PREFIX}${R}`}async indexKey(R){for(let U of this.tags){let z=this.tagKey(U),B=await this.cache.get(z)??[],D=new Set(B);if(!D.has(R))D.add(R),await this.cache.setForever(z,[...D])}}async put(R,U,z){let B=await this.cache.set(R,U,z);if(B)await this.indexKey(R);return B}async set(R,U,z){return this.put(R,U,z)}async setForever(R,U){let z=await this.cache.setForever(R,U);if(z)await this.indexKey(R);return z}async remember(R,U,z){let B=await this.cache.remember(R,U,z);return await this.indexKey(R),B}async rememberForever(R,U){let z=await this.cache.rememberForever(R,U);return await this.indexKey(R),z}async get(R){return this.cache.get(R)}async has(R){return this.cache.has(R)}async flush(){let R=0;for(let U of this.tags){let z=this.tagKey(U),B=G.flushLocks.get(z),D=B?B.catch(()=>0).then(()=>this.doFlushOne(z)):this.doFlushOne(z);G.flushLocks.set(z,D);try{R+=await D}finally{if(G.flushLocks.get(z)===D)G.flushLocks.delete(z)}}return R}async doFlushOne(R){let U=await this.cache.get(R)??[],z=0;if(U.length>0)z=await this.cache.del(U);return await this.cache.del(R),z}}function _(R={}){let U=V({driver:"memory",stdTTL:R.stdTTL??0,checkPeriod:R.checkPeriod??600,maxKeys:R.maxKeys??-1,useClones:R.useClones??!0,prefix:R.prefix});return new J(U)}function $(R={}){let U=V({driver:"redis",url:R.url,host:R.host??"localhost",port:R.port??6379,password:R.password,database:R.database??0,tls:R.tls??!1,stdTTL:R.stdTTL??0,prefix:R.prefix});return new J(U)}function f(R={}){let U=new L(R);return new J(U)}function w(R,U){if(R==="redis")return $(U);if(R==="singlestore")return f(U);return _(U)}var A=V({driver:"memory",stdTTL:0,checkPeriod:600,maxKeys:-1,useClones:!0}),I=new J(A),b=I;export{I as memory,i as memoize,f as createSingleStoreCache,$ as createRedisCache,_ as createMemoryCache,w as createCache,b as cache,p as WriteThroughPattern,G as TaggedCache,J as StacksCache,L as SingleStoreCacheStore,c as RefreshAheadPattern,v as RedisDriver,y as RateLimiter,u as MultiLevelPattern,h as MemoryDriver,n as CircuitBreaker,m as CacheManager,l as CacheLock,r as CacheInvalidation,d as CacheAsidePattern,g as BatchOperations};