@mrjasonroy/cache-components-cache-handler 16.0.0-alpha.6 → 16.0.3
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 +138 -361
- package/dist/data-cache/factory.d.ts +75 -0
- package/dist/data-cache/factory.d.ts.map +1 -0
- package/dist/data-cache/factory.js +113 -0
- package/dist/data-cache/factory.js.map +1 -0
- package/dist/helpers/buffer.js +3 -3
- package/dist/helpers/buffer.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -1,437 +1,214 @@
|
|
|
1
1
|
# @mrjasonroy/cache-components-cache-handler
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Cache handler for Next.js 16+ with support for Cache Components and "use cache" directive.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> Why another cache handler? I was originally going to contribute to [fortedigital/nextjs-cache-handler](https://github.com/fortedigital/nextjs-cache-handler) but that project is focused on backwards compatibility and carries a lot of legacy baggage. This repo is a ground-up rewrite that:
|
|
6
|
+
> - Targets **Next.js 16+ only** (Cache Components, `"use cache"`, `cacheLife`, etc.)
|
|
7
|
+
> - Ships with **true E2E coverage** (Playwright + Next 16) for every backend
|
|
8
|
+
> - Keeps the surface area small: zero-config memory handler for dev, Redis/Valkey/ElastiCache factory for prod
|
|
9
|
+
> - Provides an AI-first workflow so contributors (human or agent) can ship confidently
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
- ✅ **TTL support** - Time-based expiration
|
|
13
|
-
- ✅ **Zero dependencies** - Lightweight and fast
|
|
14
|
-
- ✅ **Comprehensive tests** - 31 tests, 100% coverage
|
|
11
|
+
[](https://www.npmjs.com/package/@mrjasonroy/cache-components-cache-handler)
|
|
12
|
+
[](https://opensource.org/licenses/MIT)
|
|
13
|
+
[](https://www.typescriptlang.org/)
|
|
14
|
+
[](https://nextjs.org/)
|
|
15
|
+
[](https://github.com/mrjasonroy/cache-components-cache-handler/tree/main/apps/e2e-test-app)
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
**📦 [View on npm](https://www.npmjs.com/package/@mrjasonroy/cache-components-cache-handler) | 🚀 [Releases](https://github.com/mrjasonroy/cache-components-cache-handler/releases)**
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
npm install @mrjasonroy/cache-components-cache-handler
|
|
20
|
-
# or
|
|
21
|
-
pnpm add @mrjasonroy/cache-components-cache-handler
|
|
22
|
-
# or
|
|
23
|
-
yarn add @mrjasonroy/cache-components-cache-handler
|
|
24
|
-
```
|
|
19
|
+
## What This Does
|
|
25
20
|
|
|
26
|
-
|
|
21
|
+
Implements Next.js 16+ caching APIs:
|
|
22
|
+
- `"use cache"` directive
|
|
23
|
+
- `cacheLife()` - Configure cache lifetime
|
|
24
|
+
- `cacheTag()` - Tag cache entries
|
|
25
|
+
- `revalidateTag()` - Invalidate by tags
|
|
26
|
+
- `updateTag()` - Update tags
|
|
27
|
+
- `revalidatePath()` - Invalidate by path
|
|
28
|
+
- Cache Components and PPR
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
## Supported Backends
|
|
29
31
|
|
|
30
|
-
Next.js 16
|
|
31
|
-
1. **ISR Cache** (`cacheHandler`) - For Incremental Static Regeneration
|
|
32
|
-
2. **Data Cache** (`cacheHandlers`) - For `"use cache"` and `"use cache: remote"`
|
|
32
|
+
All backends are **integration tested** with Next.js 16+ in CI so you can trust a release tag.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
| Backend | Use Case | Key Features |
|
|
35
|
+
|---------|----------|--------------|
|
|
36
|
+
| **Memory** | Development, Single-instance | Zero config, fast, no external dependencies |
|
|
37
|
+
| **Redis** | Production, Distributed | Industry standard, high performance, clustering |
|
|
38
|
+
| **Valkey** | Production, Open Source | Redis-compatible, LGPL licensed, drop-in replacement |
|
|
39
|
+
| **AWS ElastiCache** | Production, Managed | Fully managed, IAM auth, auto-scaling, high availability |
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
// next.config.mjs
|
|
38
|
-
import { createCacheConfig } from "@mrjasonroy/cache-components-cache-handler";
|
|
39
|
-
import { resolve } from "path";
|
|
41
|
+
## Install
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
dataCacheHandlerPath: resolve(process.cwd(), "./data-cache-handler.mjs"),
|
|
44
|
-
});
|
|
43
|
+
```bash
|
|
44
|
+
npm install @mrjasonroy/cache-components-cache-handler
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
...cacheConfig,
|
|
49
|
-
// Rest of your Next.js config
|
|
50
|
-
};
|
|
46
|
+
# For Redis/Valkey/ElastiCache, also install ioredis
|
|
47
|
+
npm install ioredis
|
|
51
48
|
```
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
```javascript
|
|
56
|
-
// cache-handler.mjs (ISR Cache)
|
|
57
|
-
import { createMemoryCacheHandler } from "@mrjasonroy/cache-components-cache-handler";
|
|
50
|
+
## Quick Start
|
|
58
51
|
|
|
59
|
-
|
|
60
|
-
maxItemsNumber: 1000,
|
|
61
|
-
maxItemSizeBytes: 100 * 1024 * 1024, // 100MB
|
|
62
|
-
});
|
|
63
|
-
```
|
|
52
|
+
### Zero-Config (Recommended)
|
|
64
53
|
|
|
65
54
|
```javascript
|
|
66
|
-
// data-cache-handler.mjs
|
|
67
|
-
import {
|
|
55
|
+
// data-cache-handler.mjs
|
|
56
|
+
import { createCacheHandler } from "@mrjasonroy/cache-components-cache-handler";
|
|
68
57
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
});
|
|
72
|
-
```
|
|
58
|
+
// Memory (dev)
|
|
59
|
+
export default createCacheHandler({ type: "memory" });
|
|
73
60
|
|
|
74
|
-
|
|
61
|
+
// Redis (production)
|
|
62
|
+
export default createCacheHandler({ type: "redis" });
|
|
75
63
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
import {
|
|
79
|
-
createMemoryCacheHandler,
|
|
80
|
-
createCompositeHandler,
|
|
81
|
-
} from "@mrjasonroy/cache-components-cache-handler";
|
|
82
|
-
|
|
83
|
-
const memoryHandler = createMemoryCacheHandler({
|
|
84
|
-
maxItemsNumber: 100, // Small L1 cache
|
|
85
|
-
});
|
|
64
|
+
// Valkey (production)
|
|
65
|
+
export default createCacheHandler({ type: "valkey" });
|
|
86
66
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
export default {
|
|
92
|
-
cacheHandler: createCompositeHandler({
|
|
93
|
-
handlers: [memoryHandler, redisHandler],
|
|
94
|
-
// Optional: route based on tags
|
|
95
|
-
setStrategy: (data) => {
|
|
96
|
-
if (data.tags.includes("memory-only")) {
|
|
97
|
-
return 0; // Use memory handler
|
|
98
|
-
}
|
|
99
|
-
return 1; // Use Redis handler
|
|
100
|
-
},
|
|
101
|
-
}),
|
|
102
|
-
};
|
|
67
|
+
// ElastiCache (AWS)
|
|
68
|
+
export default createCacheHandler({ type: "elasticache" });
|
|
103
69
|
```
|
|
104
70
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
### `createCacheConfig(options)`
|
|
108
|
-
|
|
109
|
-
Helper function to generate Next.js 16 cache configuration for both ISR and Data Cache.
|
|
110
|
-
|
|
111
|
-
**Options:**
|
|
112
|
-
|
|
113
|
-
| Option | Type | Required | Description |
|
|
114
|
-
|--------|------|----------|-------------|
|
|
115
|
-
| `isrHandlerPath` | `string` | Yes | Path to ISR cache handler file |
|
|
116
|
-
| `dataCacheHandlerPath` | `string` | Yes | Path to data cache handler file |
|
|
117
|
-
| `disableDefaultMemoryCache` | `boolean` | No | Disable Next.js default memory cache (default: `true`) |
|
|
118
|
-
|
|
119
|
-
**Returns:**
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
{
|
|
123
|
-
cacheHandler: string; // ISR cache handler path
|
|
124
|
-
cacheHandlers: {
|
|
125
|
-
default: string; // "use cache" handler path
|
|
126
|
-
remote: string; // "use cache: remote" handler path
|
|
127
|
-
};
|
|
128
|
-
cacheMaxMemorySize?: number; // 0 if disableDefaultMemoryCache is true
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
**Example:**
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
import { createCacheConfig } from "@mrjasonroy/cache-components-cache-handler";
|
|
136
|
-
import { resolve } from "path";
|
|
137
|
-
|
|
138
|
-
const cacheConfig = createCacheConfig({
|
|
139
|
-
isrHandlerPath: resolve(process.cwd(), "./cache-handler.mjs"),
|
|
140
|
-
dataCacheHandlerPath: resolve(process.cwd(), "./data-cache-handler.mjs"),
|
|
141
|
-
});
|
|
142
|
-
|
|
71
|
+
```javascript
|
|
72
|
+
// next.config.js
|
|
143
73
|
export default {
|
|
144
74
|
cacheComponents: true,
|
|
145
|
-
|
|
75
|
+
cacheHandlers: {
|
|
76
|
+
default: "./data-cache-handler.mjs",
|
|
77
|
+
remote: "./data-cache-handler.mjs",
|
|
78
|
+
},
|
|
79
|
+
cacheMaxMemorySize: 0, // Disable Next's built-in in-memory handler
|
|
146
80
|
};
|
|
147
81
|
```
|
|
148
82
|
|
|
149
|
-
|
|
83
|
+
**Environment Variables:**
|
|
84
|
+
- `REDIS_URL` / `VALKEY_URL` - Primary connection string for Redis-compatible stores (Valkey works with the same URI format, e.g. `redis://localhost:6380` when using the provided docker-compose service)
|
|
85
|
+
- `REDIS_PASSWORD` - Password/token used when your URL lacks credentials (works for every backend)
|
|
86
|
+
- `ELASTICACHE_ENDPOINT` / `ELASTICACHE_PORT` - AWS ElastiCache hostname + port
|
|
87
|
+
- `ELASTICACHE_TLS` - `"true"`/`"false"` toggle (defaults to `true` for ElastiCache)
|
|
88
|
+
- `ELASTICACHE_AUTH_TOKEN` - Password/token for IAM-authenticated ElastiCache clusters
|
|
150
89
|
|
|
151
|
-
Advanced
|
|
90
|
+
### Advanced: Custom Configuration
|
|
152
91
|
|
|
153
|
-
|
|
92
|
+
Need more control? You can override any option:
|
|
154
93
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
remoteDataCacheHandlerPath: resolve(process.cwd(), "./data-cache-redis.mjs"),
|
|
94
|
+
```javascript
|
|
95
|
+
// data-cache-handler.mjs
|
|
96
|
+
import { createCacheHandler } from "@mrjasonroy/cache-components-cache-handler";
|
|
97
|
+
|
|
98
|
+
export default createCacheHandler({
|
|
99
|
+
type: "elasticache",
|
|
100
|
+
endpoint: "cache.prod-cluster.cache.amazonaws.com",
|
|
101
|
+
port: 6380,
|
|
102
|
+
tls: true,
|
|
103
|
+
password: process.env.CACHE_AUTH_TOKEN,
|
|
104
|
+
keyPrefix: "myapp:cache:",
|
|
105
|
+
tagPrefix: "myapp:tags:",
|
|
106
|
+
debug: process.env.NODE_ENV === "development",
|
|
169
107
|
});
|
|
170
108
|
```
|
|
171
109
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
Creates an in-memory LRU cache handler.
|
|
175
|
-
|
|
176
|
-
**Options:**
|
|
177
|
-
|
|
178
|
-
| Option | Type | Default | Description |
|
|
179
|
-
|--------|------|---------|-------------|
|
|
180
|
-
| `maxItemsNumber` | `number` | `1000` | Maximum number of items to store |
|
|
181
|
-
| `maxItemSizeBytes` | `number` | `104857600` (100MB) | Maximum size per item |
|
|
182
|
-
| `defaultTTL` | `number` | `undefined` | Default TTL in seconds |
|
|
183
|
-
|
|
184
|
-
**Features:**
|
|
185
|
-
|
|
186
|
-
- LRU eviction when max size exceeded
|
|
187
|
-
- Automatic expiration based on TTL
|
|
188
|
-
- Tag-based revalidation (explicit and implicit)
|
|
189
|
-
- Size-based rejection of oversized entries
|
|
190
|
-
|
|
191
|
-
**Example:**
|
|
110
|
+
## Usage
|
|
192
111
|
|
|
193
112
|
```typescript
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
### `createCompositeHandler(options)`
|
|
202
|
-
|
|
203
|
-
Creates a composite handler that orchestrates multiple cache handlers.
|
|
204
|
-
|
|
205
|
-
**Options:**
|
|
206
|
-
|
|
207
|
-
| Option | Type | Required | Description |
|
|
208
|
-
|--------|------|----------|-------------|
|
|
209
|
-
| `handlers` | `CacheHandler[]` | Yes | Array of handlers (ordered by priority) |
|
|
210
|
-
| `setStrategy` | `(data) => number` | No | Function to choose which handler to use for writes |
|
|
211
|
-
|
|
212
|
-
**Features:**
|
|
213
|
-
|
|
214
|
-
- First-match read strategy (tries handlers in order)
|
|
215
|
-
- Configurable write strategy
|
|
216
|
-
- Fault tolerance with `Promise.allSettled()`
|
|
217
|
-
- Parallel revalidation across all handlers
|
|
218
|
-
|
|
219
|
-
**Example:**
|
|
113
|
+
// Basic caching
|
|
114
|
+
async function ProductList() {
|
|
115
|
+
"use cache";
|
|
116
|
+
const products = await db.products.findMany();
|
|
117
|
+
return <ProductGrid products={products} />;
|
|
118
|
+
}
|
|
220
119
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
handlers: [memoryHandler, redisHandler],
|
|
224
|
-
setStrategy: (data) => {
|
|
225
|
-
// Cache small items in memory, large items in Redis
|
|
226
|
-
const size = JSON.stringify(data).length;
|
|
227
|
-
return size < 10000 ? 0 : 1;
|
|
228
|
-
},
|
|
229
|
-
});
|
|
230
|
-
```
|
|
120
|
+
// With cache lifetime
|
|
121
|
+
import { cacheLife } from "next/cache";
|
|
231
122
|
|
|
232
|
-
|
|
123
|
+
async function BlogPost({ id }: { id: string }) {
|
|
124
|
+
"use cache";
|
|
125
|
+
cacheLife("hours"); // 1 hour
|
|
126
|
+
const post = await db.posts.findUnique({ where: { id } });
|
|
127
|
+
return <Article post={post} />;
|
|
128
|
+
}
|
|
233
129
|
|
|
234
|
-
|
|
130
|
+
// With tags for selective invalidation
|
|
131
|
+
import { cacheTag } from "next/cache";
|
|
235
132
|
|
|
236
|
-
|
|
237
|
-
// app/page.tsx
|
|
238
|
-
export default async function Page() {
|
|
133
|
+
async function UserProfile({ userId }: { userId: string }) {
|
|
239
134
|
"use cache";
|
|
240
|
-
|
|
241
|
-
|
|
135
|
+
cacheTag("user-profile", `user:${userId}`);
|
|
136
|
+
const user = await db.user.findUnique({ where: { id: userId } });
|
|
137
|
+
return <Profile user={user} />;
|
|
242
138
|
}
|
|
243
139
|
|
|
244
|
-
//
|
|
140
|
+
// Invalidate from API route
|
|
245
141
|
import { revalidateTag } from "next/cache";
|
|
246
142
|
|
|
247
143
|
export async function POST(request: Request) {
|
|
248
|
-
const {
|
|
249
|
-
revalidateTag(
|
|
144
|
+
const { userId } = await request.json();
|
|
145
|
+
revalidateTag(`user:${userId}`);
|
|
250
146
|
return Response.json({ revalidated: true });
|
|
251
147
|
}
|
|
252
148
|
```
|
|
253
149
|
|
|
254
|
-
|
|
150
|
+
## Cache Handler API
|
|
255
151
|
|
|
256
|
-
|
|
257
|
-
// app/page.tsx
|
|
258
|
-
export default async function Page() {
|
|
259
|
-
"use cache";
|
|
260
|
-
export const revalidate = 3600; // Revalidate after 1 hour
|
|
261
|
-
|
|
262
|
-
return <div>Cached for 1 hour</div>;
|
|
263
|
-
}
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### Custom Cache Tags
|
|
152
|
+
This library implements the Next.js 16+ `DataCacheHandler` interface:
|
|
267
153
|
|
|
268
154
|
```typescript
|
|
269
|
-
|
|
270
|
-
|
|
155
|
+
interface DataCacheHandler {
|
|
156
|
+
// Retrieve cached entry for a cache key
|
|
157
|
+
get(cacheKey: string, softTags: string[]): Promise<DataCacheEntry | undefined>;
|
|
271
158
|
|
|
272
|
-
|
|
159
|
+
// Store a cache entry (handles streaming responses)
|
|
160
|
+
set(cacheKey: string, pendingEntry: Promise<DataCacheEntry>): Promise<void>;
|
|
273
161
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
return fetch("https://api.example.com/data").then((res) => res.json());
|
|
277
|
-
},
|
|
278
|
-
["api-data"],
|
|
279
|
-
{
|
|
280
|
-
tags: ["api-data", "user-data"],
|
|
281
|
-
revalidate: 3600,
|
|
282
|
-
},
|
|
283
|
-
);
|
|
284
|
-
```
|
|
162
|
+
// Called periodically to refresh local tag manifest
|
|
163
|
+
refreshTags(): Promise<void>;
|
|
285
164
|
|
|
286
|
-
|
|
165
|
+
// Get maximum revalidation timestamp for tags
|
|
166
|
+
getExpiration(tags: string[]): Promise<Timestamp>;
|
|
287
167
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
Next.js 16 uses two types of tags:
|
|
291
|
-
|
|
292
|
-
1. **Explicit tags** - User-defined tags (e.g., `["user-123", "posts"]`)
|
|
293
|
-
2. **Implicit tags** - System-generated tags with `_N_T_` prefix for ISR
|
|
294
|
-
|
|
295
|
-
The handler tracks both types separately for efficient revalidation.
|
|
296
|
-
|
|
297
|
-
### LRU Eviction
|
|
298
|
-
|
|
299
|
-
When the cache reaches `maxItemsNumber`:
|
|
300
|
-
|
|
301
|
-
1. The oldest (least recently used) entry is evicted
|
|
302
|
-
2. Entries are moved to the end on each access
|
|
303
|
-
3. New entries are always added at the end
|
|
304
|
-
|
|
305
|
-
### TTL Expiration
|
|
306
|
-
|
|
307
|
-
Entries can expire based on:
|
|
308
|
-
|
|
309
|
-
1. `revalidate` value in cache context (seconds)
|
|
310
|
-
2. `defaultTTL` if no revalidate specified
|
|
311
|
-
3. Never expire if `revalidate: false`
|
|
312
|
-
|
|
313
|
-
Expiration is checked on every `get()` call (lazy expiration).
|
|
314
|
-
|
|
315
|
-
## Type Definitions
|
|
316
|
-
|
|
317
|
-
```typescript
|
|
318
|
-
interface CacheHandler {
|
|
319
|
-
name: string;
|
|
320
|
-
get(key: string, meta?: CacheHandlerGetMeta): Promise<CacheValue | null>;
|
|
321
|
-
set(key: string, value: CacheValue, context?: CacheHandlerContext): Promise<void>;
|
|
322
|
-
revalidateTag(tag: string): Promise<void>;
|
|
323
|
-
delete?(key: string): Promise<void>;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
interface CacheHandlerContext {
|
|
327
|
-
tags?: string[];
|
|
328
|
-
revalidate?: number | false;
|
|
329
|
-
softTags?: string[];
|
|
168
|
+
// Called when revalidateTag() invalidates tags
|
|
169
|
+
updateTags(tags: string[], durations?: { expire?: number }): Promise<void>;
|
|
330
170
|
}
|
|
331
|
-
|
|
332
|
-
interface CacheHandlerGetMeta {
|
|
333
|
-
implicitTags: string[];
|
|
334
|
-
}
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
## Testing
|
|
338
|
-
|
|
339
|
-
```bash
|
|
340
|
-
# Run tests
|
|
341
|
-
pnpm test
|
|
342
|
-
|
|
343
|
-
# Run tests in watch mode
|
|
344
|
-
pnpm test:watch
|
|
345
|
-
|
|
346
|
-
# Run with coverage
|
|
347
|
-
pnpm test --coverage
|
|
348
171
|
```
|
|
349
172
|
|
|
350
|
-
|
|
173
|
+
When you call `revalidateTag('my-tag')` in your application, Next.js internally calls our `updateTags(['my-tag'])` implementation, which marks all cache entries with that tag as stale.
|
|
351
174
|
|
|
352
|
-
|
|
175
|
+
## Documentation
|
|
353
176
|
|
|
354
|
-
-
|
|
355
|
-
-
|
|
356
|
-
-
|
|
357
|
-
-
|
|
358
|
-
- Efficient LRU implementation
|
|
177
|
+
- [GitHub Repository](https://github.com/mrjasonroy/cache-components-cache-handler)
|
|
178
|
+
- [Redis Configuration](https://github.com/mrjasonroy/cache-components-cache-handler/blob/main/docs/redis.md)
|
|
179
|
+
- [Valkey & ElastiCache Setup](https://github.com/mrjasonroy/cache-components-cache-handler/blob/main/docs/redis.md#valkey)
|
|
180
|
+
- [Contributing](https://github.com/mrjasonroy/cache-components-cache-handler/blob/main/CONTRIBUTING.md)
|
|
359
181
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
- Set: ~0.01ms per item
|
|
363
|
-
- Get: ~0.005ms per item
|
|
364
|
-
- Memory usage: ~1MB + cache data
|
|
365
|
-
|
|
366
|
-
## Migration from Other Handlers
|
|
367
|
-
|
|
368
|
-
### From Next.js Built-in Cache
|
|
369
|
-
|
|
370
|
-
```diff
|
|
371
|
-
// next.config.js
|
|
372
|
-
+ import { createMemoryCacheHandler } from "@mrjasonroy/cache-components-cache-handler";
|
|
373
|
-
|
|
374
|
-
export default {
|
|
375
|
-
+ cacheHandler: createMemoryCacheHandler(),
|
|
376
|
-
};
|
|
377
|
-
```
|
|
182
|
+
## CI & Test Matrix
|
|
378
183
|
|
|
379
|
-
|
|
184
|
+
| Workflow | What it Verifies |
|
|
185
|
+
|----------|------------------|
|
|
186
|
+
| **CI** | Biome lint + format check, typecheck, Vitest, and Playwright e2e suites against Memory, Redis, Valkey, and ElastiCache-mode backends |
|
|
187
|
+
| **Next.js Canary Test** | Daily compatibility runs against Next.js canary/rc/latest to catch upstream breakage |
|
|
188
|
+
| **Next.js Version Check** | Nightly scan for new Next.js releases, opens PRs to bump peer deps |
|
|
189
|
+
| **Publish** | Trusted publishing with provenance – release tags must pass the full matrix before npm sees them |
|
|
380
190
|
|
|
381
|
-
|
|
382
|
-
// next.config.js
|
|
383
|
-
import {
|
|
384
|
-
createMemoryCacheHandler,
|
|
385
|
-
+ createCompositeHandler,
|
|
386
|
-
} from "@mrjasonroy/cache-components-cache-handler";
|
|
387
|
-
import { createRedisHandler } from "./redis-handler";
|
|
191
|
+
## Development
|
|
388
192
|
|
|
389
|
-
|
|
390
|
-
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
+ ],
|
|
396
|
-
+ }),
|
|
397
|
-
};
|
|
193
|
+
```bash
|
|
194
|
+
git clone https://github.com/mrjasonroy/cache-components-cache-handler
|
|
195
|
+
cd cache-components-cache-handler
|
|
196
|
+
pnpm install
|
|
197
|
+
pnpm build
|
|
198
|
+
pnpm test
|
|
398
199
|
```
|
|
399
200
|
|
|
400
|
-
## Troubleshooting
|
|
401
|
-
|
|
402
|
-
### Cache not working
|
|
403
|
-
|
|
404
|
-
1. Check Next.js version (requires 16.0.0+)
|
|
405
|
-
2. Verify `"use cache"` directive is at top of component/function
|
|
406
|
-
3. Check browser/server console for errors
|
|
407
|
-
|
|
408
|
-
### Memory usage too high
|
|
409
|
-
|
|
410
|
-
1. Reduce `maxItemsNumber`
|
|
411
|
-
2. Reduce `maxItemSizeBytes`
|
|
412
|
-
3. Add TTL with `defaultTTL`
|
|
413
|
-
4. Use composite handler with Redis for large caches
|
|
414
|
-
|
|
415
|
-
### Revalidation not working
|
|
416
|
-
|
|
417
|
-
1. Verify tags are correctly defined
|
|
418
|
-
2. Check `revalidateTag()` is called on server
|
|
419
|
-
3. Ensure implicit tags are passed to `get()`
|
|
420
|
-
|
|
421
201
|
## Contributing
|
|
422
202
|
|
|
423
|
-
This project is
|
|
424
|
-
|
|
425
|
-
## License
|
|
203
|
+
This project is AI-friendly and contributor-friendly.
|
|
426
204
|
|
|
427
|
-
|
|
205
|
+
- **For AI Agents:** See [CLAUDE.md](https://github.com/mrjasonroy/cache-components-cache-handler/blob/main/CLAUDE.md) for instructions
|
|
206
|
+
- **For Humans:** See [CONTRIBUTING.md](https://github.com/mrjasonroy/cache-components-cache-handler/blob/main/CONTRIBUTING.md)
|
|
428
207
|
|
|
429
|
-
##
|
|
208
|
+
## License
|
|
430
209
|
|
|
431
|
-
|
|
432
|
-
- [Documentation](https://github.com/mrjasonroy/cache-components-cache-handler)
|
|
210
|
+
MIT © [Jason Roy](https://github.com/mrjasonroy)
|
|
433
211
|
|
|
434
|
-
##
|
|
212
|
+
## Acknowledgments
|
|
435
213
|
|
|
436
|
-
|
|
437
|
-
- [@mrjasonroy/cache-components-cache-handler-redis](../cache-handler-redis) - Redis implementation
|
|
214
|
+
Forked from/Inspired by [@fortedigital/nextjs-cache-handler](https://github.com/fortedigital/nextjs-cache-handler), but focusing only on supporting Next.js 16+ and the new caching system.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-config cache handler factory
|
|
3
|
+
* Automatically configures handlers based on type and environment variables
|
|
4
|
+
*/
|
|
5
|
+
import type { DataCacheHandler } from "./types.js";
|
|
6
|
+
export type CacheHandlerType = "memory" | "redis" | "valkey" | "elasticache";
|
|
7
|
+
export interface CacheHandlerOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Cache backend type
|
|
10
|
+
*/
|
|
11
|
+
type: CacheHandlerType;
|
|
12
|
+
/**
|
|
13
|
+
* Redis/Valkey URL (e.g., "redis://localhost:6379")
|
|
14
|
+
* @default process.env.REDIS_URL || process.env.VALKEY_URL
|
|
15
|
+
*/
|
|
16
|
+
url?: string;
|
|
17
|
+
/**
|
|
18
|
+
* ElastiCache/Redis endpoint hostname
|
|
19
|
+
* @default process.env.ELASTICACHE_ENDPOINT
|
|
20
|
+
*/
|
|
21
|
+
endpoint?: string;
|
|
22
|
+
/**
|
|
23
|
+
* ElastiCache/Redis port
|
|
24
|
+
* @default process.env.ELASTICACHE_PORT || 6379
|
|
25
|
+
*/
|
|
26
|
+
port?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Enable TLS/SSL
|
|
29
|
+
* @default true for elasticache, false for redis/valkey
|
|
30
|
+
*/
|
|
31
|
+
tls?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Auth token/password
|
|
34
|
+
* @default process.env.ELASTICACHE_AUTH_TOKEN or process.env.REDIS_PASSWORD
|
|
35
|
+
*/
|
|
36
|
+
password?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Key prefix for cache entries
|
|
39
|
+
* @default "nextjs:cache:"
|
|
40
|
+
*/
|
|
41
|
+
keyPrefix?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Key prefix for tags
|
|
44
|
+
* @default "nextjs:tags:"
|
|
45
|
+
*/
|
|
46
|
+
tagPrefix?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Enable debug logging
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
51
|
+
debug?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Max memory size (for memory handler only)
|
|
54
|
+
* @default 100MB
|
|
55
|
+
*/
|
|
56
|
+
maxSize?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Create a cache handler with zero configuration
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```javascript
|
|
63
|
+
* // Zero config - uses environment variables
|
|
64
|
+
* export default createCacheHandler({ type: "redis" });
|
|
65
|
+
*
|
|
66
|
+
* // With overrides
|
|
67
|
+
* export default createCacheHandler({
|
|
68
|
+
* type: "elasticache",
|
|
69
|
+
* endpoint: "my-cluster.cache.amazonaws.com",
|
|
70
|
+
* password: process.env.AUTH_TOKEN
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare function createCacheHandler(options: CacheHandlerOptions): DataCacheHandler;
|
|
75
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/data-cache/factory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAiCnD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,aAAa,CAAC;AAE7E,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,IAAI,EAAE,gBAAgB,CAAC;IAEvB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IAEd;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,gBAAgB,CAiFjF"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-config cache handler factory
|
|
3
|
+
* Automatically configures handlers based on type and environment variables
|
|
4
|
+
*/
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import { createMemoryDataCacheHandler } from "./memory.js";
|
|
7
|
+
import { createRedisDataCacheHandler } from "./redis.js";
|
|
8
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
9
|
+
/**
|
|
10
|
+
* Lazy load ioredis and return the constructor
|
|
11
|
+
*/
|
|
12
|
+
function loadIoredis(type) {
|
|
13
|
+
try {
|
|
14
|
+
return nodeRequire("ioredis");
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new Error(`ioredis is required for ${type} cache handler. Install it with: npm install ioredis`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create adapter for ioredis (lowercase methods) to match RedisClient interface (camelCase)
|
|
22
|
+
*/
|
|
23
|
+
function createRedisAdapter(redis) {
|
|
24
|
+
return {
|
|
25
|
+
get: (key) => redis.get(key),
|
|
26
|
+
set: (key, value) => redis.set(key, value),
|
|
27
|
+
del: (...keys) => redis.del(...keys),
|
|
28
|
+
exists: (...keys) => redis.exists(...keys),
|
|
29
|
+
ttl: (key) => redis.ttl(key),
|
|
30
|
+
hGet: (key, field) => redis.hget(key, field),
|
|
31
|
+
hSet: (key, field, value) => redis.hset(key, field, value),
|
|
32
|
+
hGetAll: (key) => redis.hgetall(key),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Create a cache handler with zero configuration
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```javascript
|
|
40
|
+
* // Zero config - uses environment variables
|
|
41
|
+
* export default createCacheHandler({ type: "redis" });
|
|
42
|
+
*
|
|
43
|
+
* // With overrides
|
|
44
|
+
* export default createCacheHandler({
|
|
45
|
+
* type: "elasticache",
|
|
46
|
+
* endpoint: "my-cluster.cache.amazonaws.com",
|
|
47
|
+
* password: process.env.AUTH_TOKEN
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function createCacheHandler(options) {
|
|
52
|
+
const { type, debug = false } = options;
|
|
53
|
+
switch (type) {
|
|
54
|
+
case "memory":
|
|
55
|
+
return createMemoryDataCacheHandler({
|
|
56
|
+
maxSize: options.maxSize ?? 100 * 1024 * 1024,
|
|
57
|
+
debug,
|
|
58
|
+
});
|
|
59
|
+
case "redis":
|
|
60
|
+
case "valkey": {
|
|
61
|
+
const Redis = loadIoredis(type);
|
|
62
|
+
const url = options.url || process.env.REDIS_URL || process.env.VALKEY_URL || "redis://localhost:6379";
|
|
63
|
+
const password = options.password ?? process.env.REDIS_PASSWORD;
|
|
64
|
+
const tlsEnabled = options.tls ?? false;
|
|
65
|
+
const redis = password || tlsEnabled
|
|
66
|
+
? new Redis(url, {
|
|
67
|
+
...(password ? { password } : {}),
|
|
68
|
+
...(tlsEnabled ? { tls: {} } : {}),
|
|
69
|
+
})
|
|
70
|
+
: new Redis(url);
|
|
71
|
+
const redisAdapter = createRedisAdapter(redis);
|
|
72
|
+
return createRedisDataCacheHandler({
|
|
73
|
+
redis: redisAdapter,
|
|
74
|
+
keyPrefix: options.keyPrefix,
|
|
75
|
+
tagPrefix: options.tagPrefix,
|
|
76
|
+
debug,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
case "elasticache": {
|
|
80
|
+
const Redis = loadIoredis(type);
|
|
81
|
+
const endpoint = options.endpoint || process.env.ELASTICACHE_ENDPOINT;
|
|
82
|
+
if (!endpoint) {
|
|
83
|
+
throw new Error("ElastiCache endpoint is required. Set ELASTICACHE_ENDPOINT environment variable or pass 'endpoint' option.");
|
|
84
|
+
}
|
|
85
|
+
const port = options.port ||
|
|
86
|
+
(process.env.ELASTICACHE_PORT ? Number.parseInt(process.env.ELASTICACHE_PORT, 10) : 6379);
|
|
87
|
+
const tls = options.tls ?? process.env.ELASTICACHE_TLS !== "false";
|
|
88
|
+
const password = options.password || process.env.ELASTICACHE_AUTH_TOKEN || process.env.REDIS_PASSWORD;
|
|
89
|
+
const redis = new Redis({
|
|
90
|
+
host: endpoint,
|
|
91
|
+
port,
|
|
92
|
+
tls: tls ? {} : undefined,
|
|
93
|
+
password,
|
|
94
|
+
connectTimeout: 10000,
|
|
95
|
+
retryStrategy: (times) => {
|
|
96
|
+
if (times > 3)
|
|
97
|
+
return null;
|
|
98
|
+
return Math.min(times * 200, 2000);
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
const redisAdapter = createRedisAdapter(redis);
|
|
102
|
+
return createRedisDataCacheHandler({
|
|
103
|
+
redis: redisAdapter,
|
|
104
|
+
keyPrefix: options.keyPrefix,
|
|
105
|
+
tagPrefix: options.tagPrefix,
|
|
106
|
+
debug,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
default:
|
|
110
|
+
throw new Error(`Unknown cache handler type: ${type}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/data-cache/factory.ts"],"names":[],"mappings":"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"}
|
package/dist/helpers/buffer.js
CHANGED
|
@@ -13,6 +13,9 @@ export function bufferToString(value) {
|
|
|
13
13
|
if (Buffer.isBuffer(value)) {
|
|
14
14
|
return value.toString("base64");
|
|
15
15
|
}
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
return value.map(bufferToString);
|
|
18
|
+
}
|
|
16
19
|
if (typeof value === "object" && value !== null) {
|
|
17
20
|
const result = {};
|
|
18
21
|
for (const [key, val] of Object.entries(value)) {
|
|
@@ -20,9 +23,6 @@ export function bufferToString(value) {
|
|
|
20
23
|
}
|
|
21
24
|
return result;
|
|
22
25
|
}
|
|
23
|
-
if (Array.isArray(value)) {
|
|
24
|
-
return value.map(bufferToString);
|
|
25
|
-
}
|
|
26
26
|
return value;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buffer.js","sourceRoot":"","sources":["../../src/helpers/buffer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/C,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,
|
|
1
|
+
{"version":3,"file":"buffer.js","sourceRoot":"","sources":["../../src/helpers/buffer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/C,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,QAAiB;IAC7D,IAAI,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC1C,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { calculateLifespan, isExpired } from "./helpers/lifespan.js";
|
|
|
11
11
|
export { createCacheConfig, createCacheConfigWithProfiles, type CacheHandlerConfig, } from "./helpers/next-config.js";
|
|
12
12
|
export type { CacheHandler, CacheHandlerContext, CacheHandlerGetMeta, CacheHandlerOptions, CacheHandlerValue, CacheValue, LifespanParameters, } from "./types.js";
|
|
13
13
|
export { IMPLICIT_TAG_PREFIX } from "./types.js";
|
|
14
|
+
export { createCacheHandler, type CacheHandlerOptions as DataCacheHandlerOptions, type CacheHandlerType, } from "./data-cache/factory.js";
|
|
14
15
|
export { createMemoryDataCacheHandler, type MemoryDataCacheHandlerOptions, } from "./data-cache/memory.js";
|
|
15
16
|
export { createRedisDataCacheHandler, type RedisClient, type RedisDataCacheHandlerOptions, } from "./data-cache/redis.js";
|
|
16
17
|
export type { DataCacheEntry, DataCacheHandler, Timestamp, } from "./data-cache/types.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,GAC9B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,6BAA6B,EAC7B,KAAK,kBAAkB,GACxB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,EACV,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,GAC9B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,6BAA6B,EAC7B,KAAK,kBAAkB,GACxB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,EACV,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAOjD,OAAO,EACL,kBAAkB,EAClB,KAAK,mBAAmB,IAAI,uBAAuB,EACnD,KAAK,gBAAgB,GACtB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,4BAA4B,EAC5B,KAAK,6BAA6B,GACnC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,2BAA2B,EAC3B,KAAK,WAAW,EAChB,KAAK,4BAA4B,GAClC,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,SAAS,GACV,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,9 @@ export { IMPLICIT_TAG_PREFIX } from "./types.js";
|
|
|
18
18
|
// =============================================================================
|
|
19
19
|
// Data Cache Handlers (for "use cache" directive)
|
|
20
20
|
// =============================================================================
|
|
21
|
+
// Zero-config factory (recommended)
|
|
22
|
+
export { createCacheHandler, } from "./data-cache/factory.js";
|
|
23
|
+
// Advanced: Direct handler creation
|
|
21
24
|
export { createMemoryDataCacheHandler, } from "./data-cache/memory.js";
|
|
22
25
|
export { createRedisDataCacheHandler, } from "./data-cache/redis.js";
|
|
23
26
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,gFAAgF;AAChF,2DAA2D;AAC3D,gFAAgF;AAEhF,OAAO,EACL,gBAAgB,EAEhB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AACjC,WAAW;AACX,OAAO,EACL,wBAAwB,EACxB,kBAAkB,GAEnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,GAElB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrE,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,6BAA6B,GAE9B,MAAM,0BAA0B,CAAC;AAWlC,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,gFAAgF;AAChF,kDAAkD;AAClD,gFAAgF;AAEhF,OAAO,EACL,4BAA4B,GAE7B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,2BAA2B,GAG5B,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,gFAAgF;AAChF,2DAA2D;AAC3D,gFAAgF;AAEhF,OAAO,EACL,gBAAgB,EAEhB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AACjC,WAAW;AACX,OAAO,EACL,wBAAwB,EACxB,kBAAkB,GAEnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,GAElB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrE,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,6BAA6B,GAE9B,MAAM,0BAA0B,CAAC;AAWlC,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,gFAAgF;AAChF,kDAAkD;AAClD,gFAAgF;AAEhF,oCAAoC;AACpC,OAAO,EACL,kBAAkB,GAGnB,MAAM,yBAAyB,CAAC;AAEjC,oCAAoC;AACpC,OAAO,EACL,4BAA4B,GAE7B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,2BAA2B,GAG5B,MAAM,uBAAuB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrjasonroy/cache-components-cache-handler",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.3",
|
|
4
4
|
"description": "Cache handler for Next.js 16+ with support for 'use cache' directive and Cache Components",
|
|
5
5
|
"author": "Jason Roy",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
".": {
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
13
13
|
"import": "./dist/index.js",
|
|
14
|
-
"require": "./dist/index.js",
|
|
15
14
|
"default": "./dist/index.js"
|
|
16
15
|
}
|
|
17
16
|
},
|