flamecache 0.0.1 → 1.0.0
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 +37 -5
- package/dist/index.js +27 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +28 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/flamecache)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](https://bundlephobia.com/package/flamecache)
|
|
6
7
|
[](https://github.com/iamanishroy/flamecache/actions)
|
|
7
8
|
|
|
8
9
|
> A Redis-like caching layer for Firebase Realtime Database with automatic expiration, counters, and batch operations.
|
|
@@ -12,7 +13,7 @@ Flamecache is a lightweight, robust caching solution that brings Redis-style ope
|
|
|
12
13
|
## ✨ Features
|
|
13
14
|
|
|
14
15
|
- 🚀 **Redis-like API** - Familiar `get`, `set`, `incr`, `decr`, `expire` operations
|
|
15
|
-
- ⚡ **Fast & Lightweight** - Only
|
|
16
|
+
- ⚡ **Fast & Lightweight** - Only ~1KB (minified), no heavy dependencies
|
|
16
17
|
- 🔄 **Auto-expiration** - Built-in TTL support with automatic cleanup
|
|
17
18
|
- 📊 **Counters** - Atomic increment/decrement operations
|
|
18
19
|
- 📦 **Batch Operations** - Multi-get, multi-set, multi-delete
|
|
@@ -44,6 +45,7 @@ const cache = createCache({
|
|
|
44
45
|
databaseURL: 'https://your-project.firebaseio.com',
|
|
45
46
|
projectId: 'your-project-id',
|
|
46
47
|
},
|
|
48
|
+
rootPath: 'my-cache', // Root path in Firebase (default: 'flamecache')
|
|
47
49
|
ttl: 3600, // Default TTL: 1 hour
|
|
48
50
|
});
|
|
49
51
|
|
|
@@ -126,6 +128,14 @@ Clear all cache entries.
|
|
|
126
128
|
await cache.clear();
|
|
127
129
|
```
|
|
128
130
|
|
|
131
|
+
#### `disconnect(): Promise<void>`
|
|
132
|
+
|
|
133
|
+
Close the connection to Firebase. Crucial for Node.js scripts to exit cleanly.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
await cache.disconnect();
|
|
137
|
+
```
|
|
138
|
+
|
|
129
139
|
### Counter Operations
|
|
130
140
|
|
|
131
141
|
#### `incr(key: string, by?: number): Promise<number>`
|
|
@@ -166,7 +176,7 @@ Get remaining time-to-live in seconds.
|
|
|
166
176
|
- Returns remaining seconds otherwise
|
|
167
177
|
|
|
168
178
|
```typescript
|
|
169
|
-
const remaining = await cache.
|
|
179
|
+
const remaining = await cache.getTtl('session:abc');
|
|
170
180
|
if (remaining > 0 && remaining < 300) {
|
|
171
181
|
console.log('Session expiring soon!');
|
|
172
182
|
}
|
|
@@ -391,6 +401,28 @@ async function getAnalytics(pageIds: string[]) {
|
|
|
391
401
|
}
|
|
392
402
|
```
|
|
393
403
|
|
|
404
|
+
## 📊 Performance
|
|
405
|
+
|
|
406
|
+
Here are the benchmark results running against a real Firebase Realtime Database instance (50 iterations per operation):
|
|
407
|
+
|
|
408
|
+
| Operation | Avg Latency | Throughput |
|
|
409
|
+
| :--------------------- | :--------------- | :---------------- |
|
|
410
|
+
| **GET** (Cache Hit) | ~110ms | 9 ops/sec |
|
|
411
|
+
| **SET** (Write) | ~120ms | 8 ops/sec |
|
|
412
|
+
| **INCR** (Atomic) | ~230ms | 4 ops/sec |
|
|
413
|
+
| **MSET** (Batch Write) | **~2.4ms / key** | **415+ keys/sec** |
|
|
414
|
+
| **MGET** (Batch Read) | **~3.6ms / key** | **275+ keys/sec** |
|
|
415
|
+
|
|
416
|
+
> All benchmarks include real-world network latency to Firebase. Batch operations leverage parallelization and atomic multi-path updates for maximum efficiency.
|
|
417
|
+
|
|
418
|
+
You can run these benchmarks yourself:
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
npm run example benchmark
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
See the [benchmark script](https://github.com/iamanishroy/flamecache/blob/main/examples/benchmark.ts) for details.
|
|
425
|
+
|
|
394
426
|
## 🔧 Configuration
|
|
395
427
|
|
|
396
428
|
```typescript
|
|
@@ -483,7 +515,7 @@ const response = await cache.wrap<ApiResponse>('api:users', fetchUsers);
|
|
|
483
515
|
```json
|
|
484
516
|
{
|
|
485
517
|
"rules": {
|
|
486
|
-
"
|
|
518
|
+
"flamecache": {
|
|
487
519
|
".read": true,
|
|
488
520
|
".write": true
|
|
489
521
|
}
|
|
@@ -496,7 +528,7 @@ const response = await cache.wrap<ApiResponse>('api:users', fetchUsers);
|
|
|
496
528
|
```json
|
|
497
529
|
{
|
|
498
530
|
"rules": {
|
|
499
|
-
"
|
|
531
|
+
"flamecache": {
|
|
500
532
|
".read": "auth != null",
|
|
501
533
|
".write": "auth != null"
|
|
502
534
|
}
|
|
@@ -509,7 +541,7 @@ const response = await cache.wrap<ApiResponse>('api:users', fetchUsers);
|
|
|
509
541
|
```json
|
|
510
542
|
{
|
|
511
543
|
"rules": {
|
|
512
|
-
"
|
|
544
|
+
"flamecache": {
|
|
513
545
|
"users": {
|
|
514
546
|
"$uid": {
|
|
515
547
|
".read": "$uid === auth.uid",
|
package/dist/index.js
CHANGED
|
@@ -81,12 +81,35 @@ var FirebaseCache = class {
|
|
|
81
81
|
return Promise.all(keys.map((k) => this.get(k)));
|
|
82
82
|
}
|
|
83
83
|
async mset(entries, ttl) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
try {
|
|
85
|
+
const seconds = ttl !== void 0 ? ttl : this.ttl;
|
|
86
|
+
const exp = seconds > 0 ? Date.now() + seconds * 1e3 : null;
|
|
87
|
+
const updates = {};
|
|
88
|
+
for (const [key, data] of Object.entries(entries)) {
|
|
89
|
+
updates[key] = {
|
|
90
|
+
data,
|
|
91
|
+
exp
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
await database.update(database.ref(this.db, this.root), updates);
|
|
95
|
+
this.log("mset:", Object.keys(entries).length, "keys");
|
|
96
|
+
} catch (err) {
|
|
97
|
+
this.log("error mset:", err);
|
|
98
|
+
throw err;
|
|
99
|
+
}
|
|
87
100
|
}
|
|
88
101
|
async mdel(keys) {
|
|
89
|
-
|
|
102
|
+
try {
|
|
103
|
+
const updates = {};
|
|
104
|
+
for (const key of keys) {
|
|
105
|
+
updates[key] = null;
|
|
106
|
+
}
|
|
107
|
+
await database.update(database.ref(this.db, this.root), updates);
|
|
108
|
+
this.log("mdel:", keys.length, "keys");
|
|
109
|
+
} catch (err) {
|
|
110
|
+
this.log("error mdel:", err);
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
90
113
|
}
|
|
91
114
|
async clear() {
|
|
92
115
|
try {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cache.ts"],"names":["app","getApps","initializeApp","getDatabase","get","ref","set","remove","goOffline","update","increment"],"mappings":"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","file":"index.js","sourcesContent":["import { initializeApp, getApps } from 'firebase/app';\nimport { \n getDatabase, \n ref, \n get, \n set, \n remove, \n goOffline,\n update,\n increment,\n Database \n} from 'firebase/database';\nimport { CacheConfig, CacheEntry } from './types';\n\nexport class FirebaseCache {\n private db: Database;\n private root: string;\n private ttl: number;\n private debug: boolean;\n\n constructor(config: CacheConfig) {\n let app;\n const existingApp = getApps().find(a => a.name === (config.rootPath || '[DEFAULT]'));\n if (existingApp) {\n app = existingApp;\n } else {\n app = initializeApp(config.firebase, config.rootPath || '[DEFAULT]');\n }\n \n this.db = getDatabase(app);\n this.root = config.rootPath || 'flamecache';\n this.ttl = config.ttl || 3600;\n this.debug = config.debug || false;\n }\n\n private path(key: string): string {\n return `${this.root}/${key}`;\n }\n\n private log(...args: any[]): void {\n if (this.debug) console.log('[Flamecache]', ...args);\n }\n\n async get<T = any>(key: string): Promise<T | null> {\n try {\n const snapshot = await get(ref(this.db, this.path(key)));\n if (!snapshot.exists()) {\n this.log('miss:', key);\n return null;\n }\n\n const entry: CacheEntry<T> = snapshot.val();\n \n if (entry.exp && Date.now() > entry.exp) {\n this.log('expired:', key);\n await this.del(key);\n return null;\n }\n\n this.log('hit:', key);\n return entry.data;\n } catch (err) {\n this.log('error get:', key, err);\n return null;\n }\n }\n\n async set<T = any>(key: string, data: T, ttl?: number): Promise<void> {\n try {\n const seconds = ttl !== undefined ? ttl : this.ttl;\n const entry: CacheEntry<T> = {\n data,\n exp: seconds > 0 ? Date.now() + (seconds * 1000) : null\n };\n \n await set(ref(this.db, this.path(key)), entry);\n this.log('set:', key, `(${seconds}s)`);\n } catch (err) {\n this.log('error set:', key, err);\n throw err;\n }\n }\n\n async del(key: string): Promise<void> {\n try {\n await remove(ref(this.db, this.path(key)));\n this.log('del:', key);\n } catch (err) {\n this.log('error del:', key, err);\n }\n }\n\n async has(key: string): Promise<boolean> {\n return (await this.get(key)) !== null;\n }\n\n async wrap<T = any>(\n key: string,\n fetchFn: () => Promise<T>,\n ttl?: number\n ): Promise<T> {\n const cached = await this.get<T>(key);\n if (cached !== null) return cached;\n\n this.log('fetch:', key);\n const data = await fetchFn();\n await this.set(key, data, ttl);\n return data;\n }\n\n async mget<T = any>(keys: string[]): Promise<(T | null)[]> {\n return Promise.all(keys.map(k => this.get<T>(k)));\n }\n\n async mset(entries: Record<string, any>, ttl?: number): Promise<void> {\n await Promise.all(\n Object.entries(entries).map(([k, v]) => this.set(k, v, ttl))\n );\n }\n\n async mdel(keys: string[]): Promise<void> {\n await Promise.all(keys.map(k => this.del(k)));\n }\n\n async clear(): Promise<void> {\n try {\n await remove(ref(this.db, this.root));\n this.log('cleared all');\n } catch (err) {\n this.log('error clear:', err);\n throw err;\n }\n }\n\n async disconnect(): Promise<void> {\n await goOffline(this.db);\n this.log('disconnected');\n }\n\n async touch(key: string, ttl?: number): Promise<boolean> {\n const data = await this.get(key);\n if (data === null) return false;\n await this.set(key, data, ttl);\n return true;\n }\n\n async pull<T = any>(key: string): Promise<T | null> {\n const data = await this.get<T>(key);\n if (data !== null) await this.del(key);\n return data;\n }\n\n async incr(key: string, by: number = 1): Promise<number> {\n const op = by >= 0 ? 'incr' : 'decr';\n try {\n const path = this.path(key);\n const dbRef = ref(this.db, path);\n \n await update(dbRef, {\n 'data': increment(by),\n 'exp': this.ttl > 0 ? Date.now() + (this.ttl * 1000) : null\n });\n\n const newValue = await this.get<number>(key);\n const sign = by >= 0 ? '+' : '';\n this.log(`${op}:`, key, `${sign}${by} (atomic)`);\n return newValue ?? 0;\n } catch (err) {\n this.log(`error ${op}:`, key, err);\n throw err;\n }\n }\n\n async decr(key: string, by: number = 1): Promise<number> {\n return this.incr(key, -by);\n }\n\n async expire(key: string, ttl: number): Promise<boolean> {\n try {\n const snapshot = await get(ref(this.db, this.path(key)));\n if (!snapshot.exists()) {\n this.log('expire failed - key not found:', key);\n return false;\n }\n\n const entry: CacheEntry<any> = snapshot.val();\n entry.exp = ttl > 0 ? Date.now() + (ttl * 1000) : null;\n \n await set(ref(this.db, this.path(key)), entry);\n this.log('expire:', key, `(${ttl}s)`);\n return true;\n } catch (err) {\n this.log('error expire:', key, err);\n return false;\n }\n }\n\n async getTtl(key: string): Promise<number> {\n try {\n const snapshot = await get(ref(this.db, this.path(key)));\n if (!snapshot.exists()) return 0;\n\n const entry: CacheEntry<any> = snapshot.val();\n \n if (!entry.exp) return -1;\n \n const remaining = Math.max(0, Math.floor((entry.exp - Date.now()) / 1000));\n return remaining;\n } catch (err) {\n this.log('error ttl:', key, err);\n return 0;\n }\n }\n}\n\nexport function createCache(config: CacheConfig): FirebaseCache {\n return new FirebaseCache(config);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/cache.ts"],"names":["app","getApps","initializeApp","getDatabase","get","ref","set","remove","update","goOffline","increment"],"mappings":"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","file":"index.js","sourcesContent":["import { initializeApp, getApps } from 'firebase/app';\nimport { \n getDatabase, \n ref, \n get, \n set, \n remove, \n goOffline,\n update,\n increment,\n Database \n} from 'firebase/database';\nimport { CacheConfig, CacheEntry } from './types';\n\nexport class FirebaseCache {\n private db: Database;\n private root: string;\n private ttl: number;\n private debug: boolean;\n\n constructor(config: CacheConfig) {\n let app;\n const existingApp = getApps().find(a => a.name === (config.rootPath || '[DEFAULT]'));\n if (existingApp) {\n app = existingApp;\n } else {\n app = initializeApp(config.firebase, config.rootPath || '[DEFAULT]');\n }\n \n this.db = getDatabase(app);\n this.root = config.rootPath || 'flamecache';\n this.ttl = config.ttl || 3600;\n this.debug = config.debug || false;\n }\n\n private path(key: string): string {\n return `${this.root}/${key}`;\n }\n\n private log(...args: any[]): void {\n if (this.debug) console.log('[Flamecache]', ...args);\n }\n\n async get<T = any>(key: string): Promise<T | null> {\n try {\n const snapshot = await get(ref(this.db, this.path(key)));\n if (!snapshot.exists()) {\n this.log('miss:', key);\n return null;\n }\n\n const entry: CacheEntry<T> = snapshot.val();\n \n if (entry.exp && Date.now() > entry.exp) {\n this.log('expired:', key);\n await this.del(key);\n return null;\n }\n\n this.log('hit:', key);\n return entry.data;\n } catch (err) {\n this.log('error get:', key, err);\n return null;\n }\n }\n\n async set<T = any>(key: string, data: T, ttl?: number): Promise<void> {\n try {\n const seconds = ttl !== undefined ? ttl : this.ttl;\n const entry: CacheEntry<T> = {\n data,\n exp: seconds > 0 ? Date.now() + (seconds * 1000) : null\n };\n \n await set(ref(this.db, this.path(key)), entry);\n this.log('set:', key, `(${seconds}s)`);\n } catch (err) {\n this.log('error set:', key, err);\n throw err;\n }\n }\n\n async del(key: string): Promise<void> {\n try {\n await remove(ref(this.db, this.path(key)));\n this.log('del:', key);\n } catch (err) {\n this.log('error del:', key, err);\n }\n }\n\n async has(key: string): Promise<boolean> {\n return (await this.get(key)) !== null;\n }\n\n async wrap<T = any>(\n key: string,\n fetchFn: () => Promise<T>,\n ttl?: number\n ): Promise<T> {\n const cached = await this.get<T>(key);\n if (cached !== null) return cached;\n\n this.log('fetch:', key);\n const data = await fetchFn();\n await this.set(key, data, ttl);\n return data;\n }\n\n async mget<T = any>(keys: string[]): Promise<(T | null)[]> {\n return Promise.all(keys.map(k => this.get<T>(k)));\n }\n\n async mset(entries: Record<string, any>, ttl?: number): Promise<void> {\n try {\n const seconds = ttl !== undefined ? ttl : this.ttl;\n const exp = seconds > 0 ? Date.now() + (seconds * 1000) : null;\n const updates: Record<string, CacheEntry<any>> = {};\n \n for (const [key, data] of Object.entries(entries)) {\n updates[key] = {\n data,\n exp\n };\n }\n \n await update(ref(this.db, this.root), updates);\n this.log('mset:', Object.keys(entries).length, 'keys');\n } catch (err) {\n this.log('error mset:', err);\n throw err;\n }\n }\n\n async mdel(keys: string[]): Promise<void> {\n try {\n const updates: Record<string, null> = {};\n for (const key of keys) {\n updates[key] = null;\n }\n \n await update(ref(this.db, this.root), updates);\n this.log('mdel:', keys.length, 'keys');\n } catch (err) {\n this.log('error mdel:', err);\n throw err;\n }\n }\n\n async clear(): Promise<void> {\n try {\n await remove(ref(this.db, this.root));\n this.log('cleared all');\n } catch (err) {\n this.log('error clear:', err);\n throw err;\n }\n }\n\n async disconnect(): Promise<void> {\n await goOffline(this.db);\n this.log('disconnected');\n }\n\n async touch(key: string, ttl?: number): Promise<boolean> {\n const data = await this.get(key);\n if (data === null) return false;\n await this.set(key, data, ttl);\n return true;\n }\n\n async pull<T = any>(key: string): Promise<T | null> {\n const data = await this.get<T>(key);\n if (data !== null) await this.del(key);\n return data;\n }\n\n async incr(key: string, by: number = 1): Promise<number> {\n const op = by >= 0 ? 'incr' : 'decr';\n try {\n const path = this.path(key);\n const dbRef = ref(this.db, path);\n \n await update(dbRef, {\n 'data': increment(by),\n 'exp': this.ttl > 0 ? Date.now() + (this.ttl * 1000) : null\n });\n\n const newValue = await this.get<number>(key);\n const sign = by >= 0 ? '+' : '';\n this.log(`${op}:`, key, `${sign}${by} (atomic)`);\n return newValue ?? 0;\n } catch (err) {\n this.log(`error ${op}:`, key, err);\n throw err;\n }\n }\n\n async decr(key: string, by: number = 1): Promise<number> {\n return this.incr(key, -by);\n }\n\n async expire(key: string, ttl: number): Promise<boolean> {\n try {\n const snapshot = await get(ref(this.db, this.path(key)));\n if (!snapshot.exists()) {\n this.log('expire failed - key not found:', key);\n return false;\n }\n\n const entry: CacheEntry<any> = snapshot.val();\n entry.exp = ttl > 0 ? Date.now() + (ttl * 1000) : null;\n \n await set(ref(this.db, this.path(key)), entry);\n this.log('expire:', key, `(${ttl}s)`);\n return true;\n } catch (err) {\n this.log('error expire:', key, err);\n return false;\n }\n }\n\n async getTtl(key: string): Promise<number> {\n try {\n const snapshot = await get(ref(this.db, this.path(key)));\n if (!snapshot.exists()) return 0;\n\n const entry: CacheEntry<any> = snapshot.val();\n \n if (!entry.exp) return -1;\n \n const remaining = Math.max(0, Math.floor((entry.exp - Date.now()) / 1000));\n return remaining;\n } catch (err) {\n this.log('error ttl:', key, err);\n return 0;\n }\n }\n}\n\nexport function createCache(config: CacheConfig): FirebaseCache {\n return new FirebaseCache(config);\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getApps, initializeApp } from 'firebase/app';
|
|
2
|
-
import { getDatabase, get, ref, set, remove,
|
|
2
|
+
import { getDatabase, get, ref, set, remove, update, goOffline, increment } from 'firebase/database';
|
|
3
3
|
|
|
4
4
|
// src/cache.ts
|
|
5
5
|
var FirebaseCache = class {
|
|
@@ -79,12 +79,35 @@ var FirebaseCache = class {
|
|
|
79
79
|
return Promise.all(keys.map((k) => this.get(k)));
|
|
80
80
|
}
|
|
81
81
|
async mset(entries, ttl) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
try {
|
|
83
|
+
const seconds = ttl !== void 0 ? ttl : this.ttl;
|
|
84
|
+
const exp = seconds > 0 ? Date.now() + seconds * 1e3 : null;
|
|
85
|
+
const updates = {};
|
|
86
|
+
for (const [key, data] of Object.entries(entries)) {
|
|
87
|
+
updates[key] = {
|
|
88
|
+
data,
|
|
89
|
+
exp
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
await update(ref(this.db, this.root), updates);
|
|
93
|
+
this.log("mset:", Object.keys(entries).length, "keys");
|
|
94
|
+
} catch (err) {
|
|
95
|
+
this.log("error mset:", err);
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
85
98
|
}
|
|
86
99
|
async mdel(keys) {
|
|
87
|
-
|
|
100
|
+
try {
|
|
101
|
+
const updates = {};
|
|
102
|
+
for (const key of keys) {
|
|
103
|
+
updates[key] = null;
|
|
104
|
+
}
|
|
105
|
+
await update(ref(this.db, this.root), updates);
|
|
106
|
+
this.log("mdel:", keys.length, "keys");
|
|
107
|
+
} catch (err) {
|
|
108
|
+
this.log("error mdel:", err);
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
88
111
|
}
|
|
89
112
|
async clear() {
|
|
90
113
|
try {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cache.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { initializeApp, getApps } from 'firebase/app';\nimport { \n getDatabase, \n ref, \n get, \n set, \n remove, \n goOffline,\n update,\n increment,\n Database \n} from 'firebase/database';\nimport { CacheConfig, CacheEntry } from './types';\n\nexport class FirebaseCache {\n private db: Database;\n private root: string;\n private ttl: number;\n private debug: boolean;\n\n constructor(config: CacheConfig) {\n let app;\n const existingApp = getApps().find(a => a.name === (config.rootPath || '[DEFAULT]'));\n if (existingApp) {\n app = existingApp;\n } else {\n app = initializeApp(config.firebase, config.rootPath || '[DEFAULT]');\n }\n \n this.db = getDatabase(app);\n this.root = config.rootPath || 'flamecache';\n this.ttl = config.ttl || 3600;\n this.debug = config.debug || false;\n }\n\n private path(key: string): string {\n return `${this.root}/${key}`;\n }\n\n private log(...args: any[]): void {\n if (this.debug) console.log('[Flamecache]', ...args);\n }\n\n async get<T = any>(key: string): Promise<T | null> {\n try {\n const snapshot = await get(ref(this.db, this.path(key)));\n if (!snapshot.exists()) {\n this.log('miss:', key);\n return null;\n }\n\n const entry: CacheEntry<T> = snapshot.val();\n \n if (entry.exp && Date.now() > entry.exp) {\n this.log('expired:', key);\n await this.del(key);\n return null;\n }\n\n this.log('hit:', key);\n return entry.data;\n } catch (err) {\n this.log('error get:', key, err);\n return null;\n }\n }\n\n async set<T = any>(key: string, data: T, ttl?: number): Promise<void> {\n try {\n const seconds = ttl !== undefined ? ttl : this.ttl;\n const entry: CacheEntry<T> = {\n data,\n exp: seconds > 0 ? Date.now() + (seconds * 1000) : null\n };\n \n await set(ref(this.db, this.path(key)), entry);\n this.log('set:', key, `(${seconds}s)`);\n } catch (err) {\n this.log('error set:', key, err);\n throw err;\n }\n }\n\n async del(key: string): Promise<void> {\n try {\n await remove(ref(this.db, this.path(key)));\n this.log('del:', key);\n } catch (err) {\n this.log('error del:', key, err);\n }\n }\n\n async has(key: string): Promise<boolean> {\n return (await this.get(key)) !== null;\n }\n\n async wrap<T = any>(\n key: string,\n fetchFn: () => Promise<T>,\n ttl?: number\n ): Promise<T> {\n const cached = await this.get<T>(key);\n if (cached !== null) return cached;\n\n this.log('fetch:', key);\n const data = await fetchFn();\n await this.set(key, data, ttl);\n return data;\n }\n\n async mget<T = any>(keys: string[]): Promise<(T | null)[]> {\n return Promise.all(keys.map(k => this.get<T>(k)));\n }\n\n async mset(entries: Record<string, any>, ttl?: number): Promise<void> {\n await Promise.all(\n Object.entries(entries).map(([k, v]) => this.set(k, v, ttl))\n );\n }\n\n async mdel(keys: string[]): Promise<void> {\n await Promise.all(keys.map(k => this.del(k)));\n }\n\n async clear(): Promise<void> {\n try {\n await remove(ref(this.db, this.root));\n this.log('cleared all');\n } catch (err) {\n this.log('error clear:', err);\n throw err;\n }\n }\n\n async disconnect(): Promise<void> {\n await goOffline(this.db);\n this.log('disconnected');\n }\n\n async touch(key: string, ttl?: number): Promise<boolean> {\n const data = await this.get(key);\n if (data === null) return false;\n await this.set(key, data, ttl);\n return true;\n }\n\n async pull<T = any>(key: string): Promise<T | null> {\n const data = await this.get<T>(key);\n if (data !== null) await this.del(key);\n return data;\n }\n\n async incr(key: string, by: number = 1): Promise<number> {\n const op = by >= 0 ? 'incr' : 'decr';\n try {\n const path = this.path(key);\n const dbRef = ref(this.db, path);\n \n await update(dbRef, {\n 'data': increment(by),\n 'exp': this.ttl > 0 ? Date.now() + (this.ttl * 1000) : null\n });\n\n const newValue = await this.get<number>(key);\n const sign = by >= 0 ? '+' : '';\n this.log(`${op}:`, key, `${sign}${by} (atomic)`);\n return newValue ?? 0;\n } catch (err) {\n this.log(`error ${op}:`, key, err);\n throw err;\n }\n }\n\n async decr(key: string, by: number = 1): Promise<number> {\n return this.incr(key, -by);\n }\n\n async expire(key: string, ttl: number): Promise<boolean> {\n try {\n const snapshot = await get(ref(this.db, this.path(key)));\n if (!snapshot.exists()) {\n this.log('expire failed - key not found:', key);\n return false;\n }\n\n const entry: CacheEntry<any> = snapshot.val();\n entry.exp = ttl > 0 ? Date.now() + (ttl * 1000) : null;\n \n await set(ref(this.db, this.path(key)), entry);\n this.log('expire:', key, `(${ttl}s)`);\n return true;\n } catch (err) {\n this.log('error expire:', key, err);\n return false;\n }\n }\n\n async getTtl(key: string): Promise<number> {\n try {\n const snapshot = await get(ref(this.db, this.path(key)));\n if (!snapshot.exists()) return 0;\n\n const entry: CacheEntry<any> = snapshot.val();\n \n if (!entry.exp) return -1;\n \n const remaining = Math.max(0, Math.floor((entry.exp - Date.now()) / 1000));\n return remaining;\n } catch (err) {\n this.log('error ttl:', key, err);\n return 0;\n }\n }\n}\n\nexport function createCache(config: CacheConfig): FirebaseCache {\n return new FirebaseCache(config);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/cache.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { initializeApp, getApps } from 'firebase/app';\nimport { \n getDatabase, \n ref, \n get, \n set, \n remove, \n goOffline,\n update,\n increment,\n Database \n} from 'firebase/database';\nimport { CacheConfig, CacheEntry } from './types';\n\nexport class FirebaseCache {\n private db: Database;\n private root: string;\n private ttl: number;\n private debug: boolean;\n\n constructor(config: CacheConfig) {\n let app;\n const existingApp = getApps().find(a => a.name === (config.rootPath || '[DEFAULT]'));\n if (existingApp) {\n app = existingApp;\n } else {\n app = initializeApp(config.firebase, config.rootPath || '[DEFAULT]');\n }\n \n this.db = getDatabase(app);\n this.root = config.rootPath || 'flamecache';\n this.ttl = config.ttl || 3600;\n this.debug = config.debug || false;\n }\n\n private path(key: string): string {\n return `${this.root}/${key}`;\n }\n\n private log(...args: any[]): void {\n if (this.debug) console.log('[Flamecache]', ...args);\n }\n\n async get<T = any>(key: string): Promise<T | null> {\n try {\n const snapshot = await get(ref(this.db, this.path(key)));\n if (!snapshot.exists()) {\n this.log('miss:', key);\n return null;\n }\n\n const entry: CacheEntry<T> = snapshot.val();\n \n if (entry.exp && Date.now() > entry.exp) {\n this.log('expired:', key);\n await this.del(key);\n return null;\n }\n\n this.log('hit:', key);\n return entry.data;\n } catch (err) {\n this.log('error get:', key, err);\n return null;\n }\n }\n\n async set<T = any>(key: string, data: T, ttl?: number): Promise<void> {\n try {\n const seconds = ttl !== undefined ? ttl : this.ttl;\n const entry: CacheEntry<T> = {\n data,\n exp: seconds > 0 ? Date.now() + (seconds * 1000) : null\n };\n \n await set(ref(this.db, this.path(key)), entry);\n this.log('set:', key, `(${seconds}s)`);\n } catch (err) {\n this.log('error set:', key, err);\n throw err;\n }\n }\n\n async del(key: string): Promise<void> {\n try {\n await remove(ref(this.db, this.path(key)));\n this.log('del:', key);\n } catch (err) {\n this.log('error del:', key, err);\n }\n }\n\n async has(key: string): Promise<boolean> {\n return (await this.get(key)) !== null;\n }\n\n async wrap<T = any>(\n key: string,\n fetchFn: () => Promise<T>,\n ttl?: number\n ): Promise<T> {\n const cached = await this.get<T>(key);\n if (cached !== null) return cached;\n\n this.log('fetch:', key);\n const data = await fetchFn();\n await this.set(key, data, ttl);\n return data;\n }\n\n async mget<T = any>(keys: string[]): Promise<(T | null)[]> {\n return Promise.all(keys.map(k => this.get<T>(k)));\n }\n\n async mset(entries: Record<string, any>, ttl?: number): Promise<void> {\n try {\n const seconds = ttl !== undefined ? ttl : this.ttl;\n const exp = seconds > 0 ? Date.now() + (seconds * 1000) : null;\n const updates: Record<string, CacheEntry<any>> = {};\n \n for (const [key, data] of Object.entries(entries)) {\n updates[key] = {\n data,\n exp\n };\n }\n \n await update(ref(this.db, this.root), updates);\n this.log('mset:', Object.keys(entries).length, 'keys');\n } catch (err) {\n this.log('error mset:', err);\n throw err;\n }\n }\n\n async mdel(keys: string[]): Promise<void> {\n try {\n const updates: Record<string, null> = {};\n for (const key of keys) {\n updates[key] = null;\n }\n \n await update(ref(this.db, this.root), updates);\n this.log('mdel:', keys.length, 'keys');\n } catch (err) {\n this.log('error mdel:', err);\n throw err;\n }\n }\n\n async clear(): Promise<void> {\n try {\n await remove(ref(this.db, this.root));\n this.log('cleared all');\n } catch (err) {\n this.log('error clear:', err);\n throw err;\n }\n }\n\n async disconnect(): Promise<void> {\n await goOffline(this.db);\n this.log('disconnected');\n }\n\n async touch(key: string, ttl?: number): Promise<boolean> {\n const data = await this.get(key);\n if (data === null) return false;\n await this.set(key, data, ttl);\n return true;\n }\n\n async pull<T = any>(key: string): Promise<T | null> {\n const data = await this.get<T>(key);\n if (data !== null) await this.del(key);\n return data;\n }\n\n async incr(key: string, by: number = 1): Promise<number> {\n const op = by >= 0 ? 'incr' : 'decr';\n try {\n const path = this.path(key);\n const dbRef = ref(this.db, path);\n \n await update(dbRef, {\n 'data': increment(by),\n 'exp': this.ttl > 0 ? Date.now() + (this.ttl * 1000) : null\n });\n\n const newValue = await this.get<number>(key);\n const sign = by >= 0 ? '+' : '';\n this.log(`${op}:`, key, `${sign}${by} (atomic)`);\n return newValue ?? 0;\n } catch (err) {\n this.log(`error ${op}:`, key, err);\n throw err;\n }\n }\n\n async decr(key: string, by: number = 1): Promise<number> {\n return this.incr(key, -by);\n }\n\n async expire(key: string, ttl: number): Promise<boolean> {\n try {\n const snapshot = await get(ref(this.db, this.path(key)));\n if (!snapshot.exists()) {\n this.log('expire failed - key not found:', key);\n return false;\n }\n\n const entry: CacheEntry<any> = snapshot.val();\n entry.exp = ttl > 0 ? Date.now() + (ttl * 1000) : null;\n \n await set(ref(this.db, this.path(key)), entry);\n this.log('expire:', key, `(${ttl}s)`);\n return true;\n } catch (err) {\n this.log('error expire:', key, err);\n return false;\n }\n }\n\n async getTtl(key: string): Promise<number> {\n try {\n const snapshot = await get(ref(this.db, this.path(key)));\n if (!snapshot.exists()) return 0;\n\n const entry: CacheEntry<any> = snapshot.val();\n \n if (!entry.exp) return -1;\n \n const remaining = Math.max(0, Math.floor((entry.exp - Date.now()) / 1000));\n return remaining;\n } catch (err) {\n this.log('error ttl:', key, err);\n return 0;\n }\n }\n}\n\nexport function createCache(config: CacheConfig): FirebaseCache {\n return new FirebaseCache(config);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flamecache",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Simple and robust caching layer using Firebase Realtime Database",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"firebase",
|
|
34
34
|
"cache",
|
|
35
35
|
"caching",
|
|
36
|
-
"redis
|
|
36
|
+
"redis",
|
|
37
37
|
"realtime-database"
|
|
38
38
|
],
|
|
39
39
|
"author": "Anish Roy",
|