Skip to content

Commit 69f650c

Browse files
committed
Final: Library Working
1 parent 00851ec commit 69f650c

10 files changed

+171
-131
lines changed

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Son Nguyen
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+20-47
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ 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-
- [Building & Publishing](#building--publishing)
4443
- [Contributing](#contributing)
4544
- [License](#license)
4645

@@ -89,10 +88,10 @@ Example:
8988
import { enableGhostCache } from "ghost-cache";
9089

9190
enableGhostCache({
92-
ttl: 120000, // Cache entries expire in 2 minutes
93-
persistent: true, // Enable persistent caching
94-
maxEntries: 200, // Allow up to 200 in-memory entries
95-
storage: "localStorage" // Use localStorage for persistence
91+
ttl: 120000, // Cache entries expire in 2 minutes
92+
persistent: true, // Enable persistent caching
93+
maxEntries: 200, // Allow up to 200 in-memory entries
94+
storage: "localStorage", // Use localStorage for persistence
9695
});
9796
```
9897

@@ -110,8 +109,8 @@ enableGhostCache();
110109

111110
// Regular fetch calls remain unchanged
112111
fetch("https://pokeapi.co/api/v2/pokemon/ditto")
113-
.then(res => res.json())
114-
.then(data => console.log("Fetched data:", data));
112+
.then((res) => res.json())
113+
.then((data) => console.log("Fetched data:", data));
115114

116115
// Disable GhostCache to restore original fetch behavior
117116
// disableGhostCache();
@@ -133,8 +132,9 @@ const api = axios.create({ baseURL: "https://pokeapi.co/api/v2" });
133132
registerAxios(api);
134133

135134
// Make requests using Axios. Subsequent calls will be served from the cache.
136-
api.get("/pokemon/ditto")
137-
.then(response => console.log("Axios fetched:", response.data));
135+
api
136+
.get("/pokemon/ditto")
137+
.then((response) => console.log("Axios fetched:", response.data));
138138
```
139139

140140
### Manual Cache API
@@ -163,7 +163,7 @@ Enable persistent caching using the browser's localStorage:
163163
```ts
164164
enableGhostCache({
165165
persistent: true,
166-
storage: "localStorage"
166+
storage: "localStorage",
167167
});
168168
```
169169

@@ -174,7 +174,7 @@ Use sessionStorage for caching that lasts only for the browser session:
174174
```ts
175175
enableGhostCache({
176176
persistent: true,
177-
storage: "sessionStorage"
177+
storage: "sessionStorage",
178178
});
179179
```
180180

@@ -185,7 +185,7 @@ Use IndexedDB for structured, persistent storage:
185185
```ts
186186
enableGhostCache({
187187
persistent: true,
188-
storage: "indexedDB"
188+
storage: "indexedDB",
189189
});
190190
```
191191

@@ -202,7 +202,7 @@ await redisClient.connect();
202202

203203
enableGhostCache({
204204
persistent: true,
205-
storage: new RedisAdapter(redisClient)
205+
storage: new RedisAdapter(redisClient),
206206
});
207207
```
208208

@@ -226,7 +226,7 @@ const App: React.FC = () => {
226226

227227
useEffect(() => {
228228
fetch("https://pokeapi.co/api/v2/pokemon/ditto")
229-
.then(res => res.json())
229+
.then((res) => res.json())
230230
.then(setPokemon);
231231
}, []);
232232

@@ -262,11 +262,12 @@ const api = axios.create({ baseURL: "https://pokeapi.co/api/v2" });
262262
registerAxios(api);
263263

264264
// Use Axios to make requests
265-
api.get("/pokemon/ditto")
266-
.then(response => {
265+
api
266+
.get("/pokemon/ditto")
267+
.then((response) => {
267268
console.log("Node.js Axios fetched:", response.data);
268269
})
269-
.catch(error => {
270+
.catch((error) => {
270271
console.error("Error:", error);
271272
});
272273
```
@@ -313,33 +314,7 @@ GhostCache comes with a comprehensive Jest test suite. To run tests:
313314

314315
Tests use the Pokémon API (`https://pokeapi.co/api/v2/pokemon/ditto`) to verify that caching works for both `fetch()` and Axios requests.
315316

316-
---
317-
318-
## Building & Publishing
319-
320-
### Building
321-
322-
Compile the TypeScript source:
323-
324-
```bash
325-
npm run build
326-
```
327-
328-
### Publishing
329-
330-
1. Login to npm:
331-
332-
```bash
333-
npm login
334-
```
335-
336-
2. Publish the package:
337-
338-
```bash
339-
npm publish --access public
340-
```
341-
342-
---
317+
> Note: There may be path/import issues. If you encounter any, please check the test files and adjust the import paths accordingly.
343318
344319
## Contributing
345320

@@ -361,10 +336,8 @@ For major changes, please open an issue first to discuss what you would like to
361336

362337
GhostCache is released under the MIT License.
363338

364-
---
365-
366339
## Final Remarks
367340

368341
GhostCache is designed to be a simple yet powerful tool for improving the performance of your web applications by reducing unnecessary network calls. With support for multiple storage adapters and both `fetch()` and Axios, it adapts to a wide range of project needs.
369342

370-
Happy caching!
343+
Happy caching! 🎉

__tests__/ghostCache.test.js

+89-58
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,103 @@
11
"use strict";
2-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
2+
var __awaiter =
3+
(this && this.__awaiter) ||
4+
function (thisArg, _arguments, P, generator) {
5+
function adopt(value) {
6+
return value instanceof P
7+
? value
8+
: new P(function (resolve) {
9+
resolve(value);
10+
});
11+
}
412
return new (P || (P = Promise))(function (resolve, reject) {
5-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8-
step((generator = generator.apply(thisArg, _arguments || [])).next());
13+
function fulfilled(value) {
14+
try {
15+
step(generator.next(value));
16+
} catch (e) {
17+
reject(e);
18+
}
19+
}
20+
function rejected(value) {
21+
try {
22+
step(generator["throw"](value));
23+
} catch (e) {
24+
reject(e);
25+
}
26+
}
27+
function step(result) {
28+
result.done
29+
? resolve(result.value)
30+
: adopt(result.value).then(fulfilled, rejected);
31+
}
32+
step((generator = generator.apply(thisArg, _arguments || [])).next());
933
});
10-
};
11-
var __importDefault = (this && this.__importDefault) || function (mod) {
12-
return (mod && mod.__esModule) ? mod : { "default": mod };
13-
};
34+
};
35+
var __importDefault =
36+
(this && this.__importDefault) ||
37+
function (mod) {
38+
return mod && mod.__esModule ? mod : { default: mod };
39+
};
1440
Object.defineProperty(exports, "__esModule", { value: true });
1541
const src_1 = require("../src");
1642
const axios_1 = __importDefault(require("axios"));
1743
jest.setTimeout(10000);
1844
describe("GhostCache - API Caching Tests", () => {
19-
const POKEMON_API = "https://pokeapi.co/api/v2/pokemon/ditto";
20-
beforeAll(() => {
21-
// Enable caching with a TTL of 5 seconds, no persistent storage for testing
22-
(0, src_1.enableGhostCache)({ ttl: 5000, persistent: false });
23-
});
24-
afterEach(() => {
25-
(0, src_1.clearGhostCache)();
26-
});
27-
afterAll(() => {
28-
(0, src_1.disableGhostCache)();
29-
});
30-
it("fetches data from the Pokémon API via fetch", () => __awaiter(void 0, void 0, void 0, function* () {
31-
const response = yield fetch(POKEMON_API);
32-
const data = yield response.json();
33-
expect(data).toHaveProperty("name", "ditto");
45+
const POKEMON_API = "https://pokeapi.co/api/v2/pokemon/ditto";
46+
beforeAll(() => {
47+
// Enable caching with a TTL of 5 seconds, no persistent storage for testing
48+
(0, src_1.enableGhostCache)({ ttl: 5000, persistent: false });
49+
});
50+
afterEach(() => {
51+
(0, src_1.clearGhostCache)();
52+
});
53+
afterAll(() => {
54+
(0, src_1.disableGhostCache)();
55+
});
56+
it("fetches data from the Pokémon API via fetch", () =>
57+
__awaiter(void 0, void 0, void 0, function* () {
58+
const response = yield fetch(POKEMON_API);
59+
const data = yield response.json();
60+
expect(data).toHaveProperty("name", "ditto");
3461
}));
35-
it("returns cached data on subsequent fetch calls", () => __awaiter(void 0, void 0, void 0, function* () {
36-
const startTime = Date.now();
37-
const response1 = yield fetch(POKEMON_API);
38-
const data1 = yield response1.json();
39-
const networkTime = Date.now() - startTime;
40-
expect(data1).toHaveProperty("name", "ditto");
41-
const cacheStartTime = Date.now();
42-
const response2 = yield fetch(POKEMON_API);
43-
const data2 = yield response2.json();
44-
const cacheTime = Date.now() - cacheStartTime;
45-
expect(data2).toHaveProperty("name", "ditto");
46-
expect(cacheTime).toBeLessThan(networkTime);
62+
it("returns cached data on subsequent fetch calls", () =>
63+
__awaiter(void 0, void 0, void 0, function* () {
64+
const startTime = Date.now();
65+
const response1 = yield fetch(POKEMON_API);
66+
const data1 = yield response1.json();
67+
const networkTime = Date.now() - startTime;
68+
expect(data1).toHaveProperty("name", "ditto");
69+
const cacheStartTime = Date.now();
70+
const response2 = yield fetch(POKEMON_API);
71+
const data2 = yield response2.json();
72+
const cacheTime = Date.now() - cacheStartTime;
73+
expect(data2).toHaveProperty("name", "ditto");
74+
expect(cacheTime).toBeLessThan(networkTime);
4775
}));
48-
it("expires cache after TTL for fetch", () => __awaiter(void 0, void 0, void 0, function* () {
49-
const response1 = yield fetch(POKEMON_API);
50-
const data1 = yield response1.json();
51-
expect(data1).toHaveProperty("name", "ditto");
52-
// Wait 6 seconds (beyond the 5-second TTL)
53-
yield new Promise(resolve => setTimeout(resolve, 6000));
54-
const response2 = yield fetch(POKEMON_API);
55-
const data2 = yield response2.json();
56-
expect(data2).toHaveProperty("name", "ditto");
76+
it("expires cache after TTL for fetch", () =>
77+
__awaiter(void 0, void 0, void 0, function* () {
78+
const response1 = yield fetch(POKEMON_API);
79+
const data1 = yield response1.json();
80+
expect(data1).toHaveProperty("name", "ditto");
81+
// Wait 6 seconds (beyond the 5-second TTL)
82+
yield new Promise((resolve) => setTimeout(resolve, 6000));
83+
const response2 = yield fetch(POKEMON_API);
84+
const data2 = yield response2.json();
85+
expect(data2).toHaveProperty("name", "ditto");
5786
}));
58-
it("allows manual cache set and get", () => __awaiter(void 0, void 0, void 0, function* () {
59-
yield (0, src_1.setCache)("test-key", { foo: "bar" });
60-
const cached = yield (0, src_1.getCache)("test-key");
61-
expect(cached).toEqual({ foo: "bar" });
87+
it("allows manual cache set and get", () =>
88+
__awaiter(void 0, void 0, void 0, function* () {
89+
yield (0, src_1.setCache)("test-key", { foo: "bar" });
90+
const cached = yield (0, src_1.getCache)("test-key");
91+
expect(cached).toEqual({ foo: "bar" });
6292
}));
63-
it("works with Axios", () => __awaiter(void 0, void 0, void 0, function* () {
64-
const api = axios_1.default.create();
65-
(0, src_1.registerAxios)(api);
66-
const response1 = yield api.get(POKEMON_API);
67-
expect(response1.data).toHaveProperty("name", "ditto");
68-
// Second request should come from cache
69-
const response2 = yield api.get(POKEMON_API);
70-
expect(response2.data).toHaveProperty("name", "ditto");
93+
it("works with Axios", () =>
94+
__awaiter(void 0, void 0, void 0, function* () {
95+
const api = axios_1.default.create();
96+
(0, src_1.registerAxios)(api);
97+
const response1 = yield api.get(POKEMON_API);
98+
expect(response1.data).toHaveProperty("name", "ditto");
99+
// Second request should come from cache
100+
const response2 = yield api.get(POKEMON_API);
101+
expect(response2.data).toHaveProperty("name", "ditto");
71102
}));
72103
});

__tests__/ghostCache.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
disableGhostCache,
55
getCache,
66
setCache,
7-
registerAxios
7+
registerAxios,
88
} from "../src/index.js";
99
import axios from "axios";
1010

@@ -53,7 +53,7 @@ describe("GhostCache - API Caching Tests", () => {
5353
expect(d1).toHaveProperty("name", "ditto");
5454

5555
// Wait 6 seconds to exceed the TTL of 5 seconds.
56-
await new Promise(resolve => setTimeout(resolve, 6000));
56+
await new Promise((resolve) => setTimeout(resolve, 6000));
5757

5858
const r2 = await fetch(POKEMON_API);
5959
const d2 = await r2.json();

index.d.ts

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// index.d.ts
2-
31
export interface IStorageAdapter {
42
getItem(key: string): Promise<string | null>;
53
setItem(key: string, value: string): Promise<void>;

jest.config.cjs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// jest.config.cjs
21
module.exports = {
32
preset: "ts-jest/presets/default-esm",
43
testEnvironment: "node",

package-lock.json

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)