@neezco/cache 0.1.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.
@@ -0,0 +1,145 @@
1
+ # Examples
2
+
3
+ ## API Response Caching
4
+
5
+ Cache API responses so you don't hammer your backend with repeated requests:
6
+
7
+ ```javascript
8
+ const cache = new LocalTtlCache();
9
+
10
+ async function getUser(userId) {
11
+ // Check cache first
12
+ const cached = cache.get(`user:${userId}`);
13
+ if (cached) return cached;
14
+
15
+ // Not in cache or expired, fetch it
16
+ const user = await fetch(`/api/users/${userId}`).then(r => r.json());
17
+
18
+ // Store for 5 minutes, but serve stale for 1 more minute while refreshing
19
+ cache.set(`user:${userId}`, user, {
20
+ ttl: 5 * 60 * 1000,
21
+ staleWindow: 1 * 60 * 1000,
22
+ tags: `user:${userId}`, // Tag it for easy invalidation
23
+ });
24
+
25
+ return user;
26
+ }
27
+
28
+ // When a user updates their profile
29
+ function handleUserUpdate(userId) {
30
+ cache.invalidateTag(`user:${userId}`); // Instantly clear their cache
31
+ }
32
+ ```
33
+
34
+ ## Database Query Caching
35
+
36
+ Speed up repeated database queries:
37
+
38
+ ```javascript
39
+ const cache = new LocalTtlCache({
40
+ defaultTtl: 2 * 60 * 1000, // 2 minutes
41
+ });
42
+
43
+ async function getProducts() {
44
+ const cached = cache.get("products:list");
45
+ if (cached) return cached;
46
+
47
+ const products = await db.query("SELECT * FROM products");
48
+ cache.set("products:list", products, { tags: "products" });
49
+ return products;
50
+ }
51
+
52
+ async function deleteProduct(id) {
53
+ await db.query("DELETE FROM products WHERE id = ?", [id]);
54
+ cache.invalidateTag("products"); // Clear all product caches
55
+ }
56
+
57
+ async function updateProduct(id, data) {
58
+ await db.query("UPDATE products SET ? WHERE id = ?", [data, id]);
59
+ cache.invalidateTag("products"); // Clear all product caches
60
+ }
61
+ ```
62
+
63
+ ## Graceful Stale Data Serving
64
+
65
+ Keep serving old data while fetching fresh data in the background:
66
+
67
+ ```javascript
68
+ const cache = new LocalTtlCache();
69
+
70
+ async function getWeatherWithBackground() {
71
+ // Fresh for 5 minutes, but we'll serve it for 2 more minutes even if expired
72
+ cache.set(
73
+ "weather:NYC",
74
+ { temp: 72, city: "NYC", fetched: Date.now() },
75
+ {
76
+ ttl: 5 * 60 * 1000,
77
+ staleWindow: 2 * 60 * 1000, // Keep serving for 2 extra minutes while fetching
78
+ },
79
+ );
80
+ }
81
+
82
+ // NEXT skipStale, full entry
83
+ // This is perfect for UIs where slight data staleness is acceptable
84
+ // You can serve old data instantly while refreshing in the background
85
+ async function fetchWeatherIfNeeded(city) {
86
+ let weather = cache.get(`weather:${city}`);
87
+
88
+ // Stale? Fetch fresh data in the background
89
+ if (!weather) {
90
+ weather = await fetchWeatherAPI(city);
91
+ getWeatherWithBackground(city, weather);
92
+ }
93
+
94
+ return weather;
95
+ }
96
+ ```
97
+
98
+ ## Tag-Based Invalidation
99
+
100
+ Group related data and clear it all at once:
101
+
102
+ ```javascript
103
+ const cache = new LocalTtlCache();
104
+
105
+ // Store user data with a 'user:456' tag
106
+ cache.set("user:456:name", "Bob", { tags: ["user:456"] });
107
+ cache.set("user:456:email", "bob@example.com", { tags: ["user:456"] });
108
+ cache.set("user:456:preferences", { theme: "dark" }, { tags: ["user:456"] });
109
+
110
+ // User updates their profile - invalidate everything at once
111
+ cache.invalidateTag("user:456");
112
+
113
+ // All three are now expired
114
+ console.log(cache.get("user:456:name")); // undefined
115
+ console.log(cache.get("user:456:email")); // undefined
116
+ console.log(cache.get("user:456:preferences")); // undefined
117
+ ```
118
+
119
+ ## Multiple Tags Per Entry
120
+
121
+ An entry can have multiple tags for flexible invalidation:
122
+
123
+ ```javascript
124
+ cache.set("user:789:profile", profileData, {
125
+ ttl: 10 * 60 * 1000,
126
+ tags: ["user:789", "profiles", "public-data"], // Multiple tags
127
+ });
128
+
129
+ // Invalidate just the user's data
130
+ cache.invalidateTag("user:789");
131
+
132
+ // Or invalidate all profiles
133
+ cache.invalidateTag("profiles");
134
+
135
+ // Or invalidate public data across the entire app
136
+ cache.invalidateTag("public-data");
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Navigation
142
+
143
+ - **[Getting Started](./getting-started.md)** - Back to basics
144
+ - **[API Reference](./api-reference.md)** - All methods explained
145
+ - **[Configuration](./configuration.md)** - Customize your cache
@@ -0,0 +1,86 @@
1
+ # Getting Started with Short‑Live
2
+
3
+ ## Installation
4
+
5
+ ```bash
6
+ npm install @neezco/cache
7
+ # or
8
+ yarn add @neezco/cache
9
+ # or
10
+ pnpm add @neezco/cache
11
+ ```
12
+
13
+ ## Recommended Setup Pattern
14
+
15
+ For better TypeScript autocomplete, define a dedicated cache module:
16
+
17
+ ```typescript
18
+ import LocalTtlCache from "@neezco/cache";
19
+
20
+ const cacheInstance = new LocalTtlCache({
21
+ defaultTtl: 5 * 60 * 1000,
22
+ maxSize: 1_000,
23
+ });
24
+
25
+ export const cache = cacheInstance;
26
+ ```
27
+
28
+ ## Domain‑oriented singleton caches
29
+
30
+ Instead of a single global cache, you can maintain **one singleton per domain** to avoid key collisions and keep responsibilities clear:
31
+
32
+ ```typescript
33
+ import LocalTtlCache from "@neezco/cache";
34
+
35
+ // User‑related data
36
+ export const usersCache = new LocalTtlCache({
37
+ defaultTtl: 5 * 60 * 1000,
38
+ maxSize: 1_000,
39
+ });
40
+
41
+ // Asset metadata
42
+ export const assetsCache = new LocalTtlCache({
43
+ defaultTtl: 10 * 60 * 1000,
44
+ maxSize: 2_000,
45
+ });
46
+ ```
47
+
48
+ ## Minimal Usage Example
49
+
50
+ ```typescript
51
+ import { cache } from "./path-to-your-cache-instance";
52
+ // or
53
+ // import { usersCache } from "./path-to-your-cache-instance";
54
+ // import { assetsCache } from "./path-to-your-cache-instance";
55
+
56
+ cache.set("user:123", "cached-data", {
57
+ ttl: 5 * 60 * 1000, // fresh for 5 minutes
58
+ staleWindow: 2 * 60 * 1000, // may be served stale for 2 more minutes
59
+ tags: ["user", "user:123"], // tags for later invalidation
60
+ });
61
+
62
+ console.log("After set:", cache.get("user:123")); // fresh value
63
+
64
+ setTimeout(
65
+ () => {
66
+ console.log(
67
+ "After TTL but within stale window:",
68
+ cache.get("user:123"), // stale value
69
+ );
70
+
71
+ cache.invalidateTag("user:123");
72
+
73
+ console.log(
74
+ "After tag invalidation:",
75
+ cache.get("user:123"), // undefined
76
+ );
77
+ },
78
+ 6 * 60 * 1000,
79
+ ); // 6 minutes
80
+ ```
81
+
82
+ ## Next Steps
83
+
84
+ - **[Examples](./examples.md)** - Real-world use cases
85
+ - **[API Reference](./api-reference.md)** – Complete method documentation with edge cases
86
+ - **[Configuration](./configuration.md)** – All options explained in detail
package/package.json ADDED
@@ -0,0 +1,93 @@
1
+ {
2
+ "version": "0.1.0",
3
+ "name": "@neezco/cache",
4
+ "type": "module",
5
+ "module": "./dist/browser/index.js",
6
+ "main": "./dist/node/index.cjs",
7
+ "types": "./dist/node/index.d.cts",
8
+ "author": {
9
+ "name": "Daniel Hernández Ochoa",
10
+ "url": "https://github.com/DanhezCode"
11
+ },
12
+ "license": "MIT",
13
+ "homepage": "https://github.com/neezco/cache#readme",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/neezco/cache.git"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/neezco/cache/issues"
20
+ },
21
+ "exports": {
22
+ ".": {
23
+ "require": "./dist/node/index.cjs",
24
+ "import": "./dist/browser/index.js"
25
+ },
26
+ "./package.json": "./package.json"
27
+ },
28
+ "scripts": {
29
+ "bench": "node --max-old-space-size=4096 bench/minimal-bench.js",
30
+ "bench:watch": "node --watch-path dist --max-old-space-size=4096 bench/minimal-bench.js",
31
+ "bench:dev": "pnpm run build && concurrently \"pnpm run build:watch\" \"pnpm run bench:watch\"",
32
+ "bench:start": "pnpm run build && pnpm run bench",
33
+ "build": "tsdown",
34
+ "build:watch": "chokidar \"src/**/*\" -c \"pnpm run build\"",
35
+ "lint": "eslint . --ext .ts",
36
+ "lint:fix": "eslint . --ext .ts --fix",
37
+ "typecheck": "tsc --noEmit",
38
+ "format:prettier": "prettier --write .",
39
+ "format:all": "pnpm lint:fix && pnpm format:prettier",
40
+ "check": "concurrently \"pnpm format:all\" \"pnpm typecheck\"",
41
+ "check:all": "concurrently \"pnpm format:all\" \"pnpm typecheck\" \"pnpm test\"",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest --watch",
44
+ "test:coverage": "vitest run --coverage",
45
+ "prepare": "husky"
46
+ },
47
+ "files": [
48
+ "dist",
49
+ "docs",
50
+ "CHANGELOG.md",
51
+ "README.md",
52
+ "LICENSE"
53
+ ],
54
+ "description": "A simple and efficient in-memory cache for JavaScript and TypeScript projects, designed to provide fast access to frequently used data while minimizing memory usage.",
55
+ "keywords": [
56
+ "cache",
57
+ "in-memory cache",
58
+ "javascript cache",
59
+ "typescript cache",
60
+ "node.js cache",
61
+ "browser cache",
62
+ "ttl cache",
63
+ "cache library",
64
+ "stale-while-revalidate"
65
+ ],
66
+ "devDependencies": {
67
+ "@commitlint/cli": "20.3.0",
68
+ "@commitlint/config-conventional": "20.3.0",
69
+ "@eslint/js": "9.39.2",
70
+ "@types/bun": "latest",
71
+ "@types/node": "25.0.6",
72
+ "@vitest/coverage-v8": "4.0.17",
73
+ "chokidar-cli": "3.0.0",
74
+ "concurrently": "9.2.1",
75
+ "eslint": "9.39.2",
76
+ "eslint-import-resolver-typescript": "4.4.4",
77
+ "eslint-plugin-import": "2.32.0",
78
+ "eslint-plugin-react": "7.37.5",
79
+ "globals": "17.0.0",
80
+ "husky": "9.1.7",
81
+ "prettier": "3.7.4",
82
+ "tsdown": "0.19.0",
83
+ "typescript-eslint": "8.51.0",
84
+ "vitest": "4.0.17"
85
+ },
86
+ "peerDependencies": {
87
+ "typescript": "5"
88
+ },
89
+ "engines": {
90
+ "node": ">=24.12.0",
91
+ "pnpm": ">=10.27.0"
92
+ }
93
+ }