Skip to content

Commit e72fb97

Browse files
committed
Final: Library Working
1 parent 16e1506 commit e72fb97

File tree

4 files changed

+79
-39
lines changed

4 files changed

+79
-39
lines changed

README.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Currently available as a Node.js package on NPM. **NPM Page:** [https://www.npmj
4040
- [Node.js Example](#nodejs-example)
4141
- [API Reference](#api-reference)
4242
- [Testing](#testing)
43+
- [Demo Script](#demo-script)
4344
- [Contributing](#contributing)
4445
- [License](#license)
4546

@@ -152,8 +153,6 @@ const profile = await getCache("user-profile");
152153
console.log("Manual cache:", profile);
153154
```
154155

155-
---
156-
157156
## Storage Adapters
158157

159158
### localStorage
@@ -206,8 +205,6 @@ enableGhostCache({
206205
});
207206
```
208207

209-
---
210-
211208
## Advanced Examples
212209

213210
### React Application Example
@@ -272,8 +269,6 @@ api
272269
});
273270
```
274271

275-
---
276-
277272
## API Reference
278273

279274
- **enableGhostCache(options?: GhostCacheOptions): void**
@@ -294,8 +289,6 @@ api
294289
- **registerAxios(instance: AxiosInstance): void**
295290
Registers an Axios instance so that its requests are intercepted and cached.
296291

297-
---
298-
299292
## Testing
300293

301294
GhostCache comes with a comprehensive Jest test suite. To run tests:
@@ -316,22 +309,32 @@ Tests use the Pokémon API (`https://pokeapi.co/api/v2/pokemon/ditto`) to verify
316309

317310
> Note: There may be path/import issues. If you encounter any, please check the test files and adjust the import paths accordingly.
318311
312+
## Demo Script
313+
314+
To run a demo script that showcases GhostCache in action, use the following command:
315+
316+
```bash
317+
npm run demo
318+
```
319+
320+
This will execute a script that demonstrates caching with both `fetch()` and Axios. Monitor the console for logs indicating cache hits and misses.
321+
319322
## Contributing
320323

321324
Contributions are welcome! Please follow these steps:
322325

323326
1. **Fork the Repository**
324327
2. **Create a Feature Branch**
328+
325329
```bash
326330
git checkout -b feature/my-new-feature
327331
```
332+
328333
3. **Commit Your Changes**
329334
4. **Submit a Pull Request**
330335

331336
For major changes, please open an issue first to discuss what you would like to change.
332337

333-
---
334-
335338
## License
336339

337340
GhostCache is released under the MIT License.

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "ghost-cache",
33
"displayName": "GhostCache API Cache",
4-
"version": "1.1.2",
4+
"version": "1.2.3",
55
"description": "A lightweight auto-caching wrapper for fetch() and Axios with multi-storage support (localStorage, sessionStorage, IndexedDB, Redis)",
66
"main": "dist/index.js",
77
"types": "index.d.ts",
@@ -53,7 +53,7 @@
5353
},
5454
"dependencies": {
5555
"axios": "^1.3.0",
56-
"ghost-cache": "^1.1.1",
56+
"ghost-cache": "^1.2.3",
5757
"prettier": "^3.5.3",
5858
"redis": "^4.0.0"
5959
}

src/ghostCache.ts

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ let originalFetch: typeof globalObj.fetch | null = null;
6161
let axiosInstances: AxiosInstance[] = [];
6262

6363
/**
64-
* Picks correct storage adapter
64+
* Picks correct storage adapter based on config.
6565
*/
6666
function determineStorageAdapter(
6767
opt: GhostCacheOptions["storage"],
@@ -78,7 +78,7 @@ function determineStorageAdapter(
7878
}
7979

8080
/**
81-
* Enable GhostCache
81+
* Enable GhostCache: intercept fetch and (optionally) Axios requests.
8282
*/
8383
export function enableGhostCache(options: GhostCacheOptions = {}): void {
8484
// Merge user options into defaults
@@ -89,29 +89,32 @@ export function enableGhostCache(options: GhostCacheOptions = {}): void {
8989
? determineStorageAdapter(defaultConfig.storage)
9090
: null;
9191

92-
// Intercept fetch if not done
92+
// Intercept global fetch if we haven't already
9393
if (!originalFetch) {
9494
originalFetch = globalObj.fetch.bind(globalObj);
9595
globalObj.fetch = async (url: RequestInfo, config?: RequestInit) => {
9696
return handleRequest({ url, config });
9797
};
9898
}
9999

100-
// Setup Axios interceptors
100+
// Set up Axios interceptors
101101
axiosInstances.forEach((instance) => {
102102
instance.interceptors.request.use(
103103
async (config: InternalAxiosRequestConfig) => {
104-
const safeUrl = config.url ?? "";
104+
const safeUrl: string = config.url ?? "";
105105
const cacheKey = JSON.stringify({
106106
url: safeUrl,
107107
params: config.params,
108108
method: config.method,
109109
});
110110

111-
// Check in-memory
111+
// Check in-memory cache
112112
if (inMemoryCache.has(cacheKey)) {
113113
const entry = inMemoryCache.get(cacheKey)!;
114114
if (Date.now() - entry.timestamp < defaultConfig.ttl) {
115+
console.log(
116+
`[GhostCache] Using in-memory cache for request: ${safeUrl}`,
117+
);
115118
return Promise.reject({
116119
__ghostCache__: true,
117120
data: JSON.parse(entry.data),
@@ -122,12 +125,15 @@ export function enableGhostCache(options: GhostCacheOptions = {}): void {
122125
}
123126
}
124127

125-
// Check persistent
128+
// Check persistent storage
126129
if (storageAdapter) {
127130
const stored = await storageAdapter.getItem(cacheKey);
128131
if (stored) {
129132
const parsed = JSON.parse(stored) as CacheEntry;
130133
if (Date.now() - parsed.timestamp < defaultConfig.ttl) {
134+
console.log(
135+
`[GhostCache] Using persistent cache for request: ${safeUrl}`,
136+
);
131137
return Promise.reject({
132138
__ghostCache__: true,
133139
data: JSON.parse(parsed.data),
@@ -146,7 +152,7 @@ export function enableGhostCache(options: GhostCacheOptions = {}): void {
146152

147153
instance.interceptors.response.use(
148154
(res: AxiosResponse) => {
149-
const safeUrl = res.config.url ?? "";
155+
const safeUrl: string = res.config.url ?? "";
150156
const cacheKey = JSON.stringify({
151157
url: safeUrl,
152158
params: res.config.params,
@@ -173,18 +179,23 @@ export function enableGhostCache(options: GhostCacheOptions = {}): void {
173179
}
174180

175181
/**
176-
* Internal fetch handler
182+
* Handle fetch() requests with caching.
177183
*/
178184
async function handleRequest(request: {
179185
url: RequestInfo;
180186
config?: RequestInit;
181187
}): Promise<Response> {
182188
const cacheKey = JSON.stringify(request);
183189

184-
// in-memory check
190+
// In-memory cache check
185191
if (inMemoryCache.has(cacheKey)) {
186192
const entry = inMemoryCache.get(cacheKey)!;
187193
if (Date.now() - entry.timestamp < defaultConfig.ttl) {
194+
console.log(
195+
`[GhostCache] (fetch) Using in-memory cache for: ${JSON.stringify(
196+
request,
197+
)}`,
198+
);
188199
return new globalObj.Response(entry.data, {
189200
status: 200,
190201
headers: { "Content-Type": "application/json" },
@@ -194,12 +205,17 @@ async function handleRequest(request: {
194205
}
195206
}
196207

197-
// storage check
208+
// Persistent cache check
198209
if (storageAdapter) {
199210
const stored = await storageAdapter.getItem(cacheKey);
200211
if (stored) {
201212
const entry = JSON.parse(stored) as CacheEntry;
202213
if (Date.now() - entry.timestamp < defaultConfig.ttl) {
214+
console.log(
215+
`[GhostCache] (fetch) Using persistent cache for: ${JSON.stringify(
216+
request,
217+
)}`,
218+
);
203219
return new globalObj.Response(entry.data, {
204220
status: 200,
205221
headers: { "Content-Type": "application/json" },
@@ -210,21 +226,28 @@ async function handleRequest(request: {
210226
}
211227
}
212228

229+
console.log(
230+
`[GhostCache] (fetch) Fetching from network: ${JSON.stringify(request)}`,
231+
);
232+
// Network fetch
213233
if (!originalFetch) {
214234
throw new Error("GhostCache: original fetch is missing.");
215235
}
216-
// network fetch
217236
const response = await originalFetch(request.url, request.config);
218237
const cloned = response.clone();
219238
const textData = await cloned.text();
220239
cacheResponse(cacheKey, textData);
221240
return response;
222241
}
223242

243+
/**
244+
* Save to in-memory cache and persistent storage.
245+
*/
224246
function cacheResponse(cacheKey: string, data: string) {
225-
const entry = { timestamp: Date.now(), data };
247+
const entry: CacheEntry = { timestamp: Date.now(), data };
226248
inMemoryCache.set(cacheKey, entry);
227249

250+
// Enforce maximum cache entries
228251
if (inMemoryCache.size > defaultConfig.maxEntries) {
229252
const firstKey = inMemoryCache.keys().next().value;
230253
if (firstKey) inMemoryCache.delete(firstKey);
@@ -233,52 +256,64 @@ function cacheResponse(cacheKey: string, data: string) {
233256
if (storageAdapter) {
234257
storageAdapter.setItem(cacheKey, JSON.stringify(entry));
235258
}
259+
260+
console.log(`[GhostCache] Cached response for key: ${cacheKey}`);
236261
}
237262

238263
/**
239-
* Manually set a cache
264+
* Manually set a cache entry.
240265
*/
241266
export async function setCache(key: string, value: any): Promise<void> {
242267
const cacheKey = JSON.stringify(key);
243-
const entry = { timestamp: Date.now(), data: JSON.stringify(value) };
268+
const entry: CacheEntry = {
269+
timestamp: Date.now(),
270+
data: JSON.stringify(value),
271+
};
244272
inMemoryCache.set(cacheKey, entry);
245-
246273
if (storageAdapter) {
247274
await storageAdapter.setItem(cacheKey, JSON.stringify(entry));
248275
}
276+
console.log(`[GhostCache] Manually cached key: ${cacheKey}`);
249277
}
250278

251279
/**
252-
* Get a cached item
280+
* Retrieve a cache entry.
253281
*/
254282
export async function getCache<T = any>(key: string): Promise<T | null> {
255283
const cacheKey = JSON.stringify(key);
256284
if (inMemoryCache.has(cacheKey)) {
257285
const entry = inMemoryCache.get(cacheKey)!;
286+
console.log(
287+
`[GhostCache] Retrieved from in-memory cache for key: ${cacheKey}`,
288+
);
258289
return JSON.parse(entry.data) as T;
259290
}
260291
if (storageAdapter) {
261292
const stored = await storageAdapter.getItem(cacheKey);
262293
if (stored) {
263294
const entry = JSON.parse(stored) as CacheEntry;
295+
console.log(
296+
`[GhostCache] Retrieved from persistent cache for key: ${cacheKey}`,
297+
);
264298
return JSON.parse(entry.data) as T;
265299
}
266300
}
267301
return null;
268302
}
269303

270304
/**
271-
* Clear all caches
305+
* Clear all cache entries.
272306
*/
273307
export function clearGhostCache(): void {
274308
inMemoryCache.clear();
275309
if (storageAdapter) {
276310
storageAdapter.clear();
277311
}
312+
console.log("[GhostCache] Cache cleared");
278313
}
279314

280315
/**
281-
* Disable GhostCache
316+
* Disable GhostCache and restore the original fetch.
282317
*/
283318
export function disableGhostCache(): void {
284319
if (originalFetch) {
@@ -287,11 +322,13 @@ export function disableGhostCache(): void {
287322
}
288323
axiosInstances = [];
289324
clearGhostCache();
325+
console.log("[GhostCache] Disabled and restored original fetch");
290326
}
291327

292328
/**
293-
* Register an Axios instance
329+
* Register an Axios instance to intercept its requests.
294330
*/
295331
export function registerAxios(instance: AxiosInstance): void {
296332
axiosInstances.push(instance);
333+
console.log("[GhostCache] Registered an Axios instance");
297334
}

0 commit comments

Comments
 (0)