@parsrun/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.
- package/README.md +135 -0
- package/dist/adapters/cloudflare-kv.d.ts +51 -0
- package/dist/adapters/cloudflare-kv.js +197 -0
- package/dist/adapters/cloudflare-kv.js.map +1 -0
- package/dist/adapters/index.d.ts +6 -0
- package/dist/adapters/index.js +783 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/memory.d.ts +59 -0
- package/dist/adapters/memory.js +179 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/redis.d.ts +72 -0
- package/dist/adapters/redis.js +242 -0
- package/dist/adapters/redis.js.map +1 -0
- package/dist/adapters/upstash.d.ts +50 -0
- package/dist/adapters/upstash.js +229 -0
- package/dist/adapters/upstash.js.map +1 -0
- package/dist/index.d.ts +140 -0
- package/dist/index.js +978 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +234 -0
- package/dist/types.js +43 -0
- package/dist/types.js.map +1 -0
- package/package.json +80 -0
package/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# @parsrun/cache
|
|
2
|
+
|
|
3
|
+
Edge-compatible caching for Pars with multiple adapter support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multi-Adapter**: Memory, Redis, Upstash, Cloudflare KV
|
|
8
|
+
- **Edge-Compatible**: Works on all runtimes
|
|
9
|
+
- **TTL Support**: Automatic expiration
|
|
10
|
+
- **Namespace**: Isolated cache spaces
|
|
11
|
+
- **Tags**: Cache invalidation by tags
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @parsrun/cache
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { createCache } from '@parsrun/cache';
|
|
23
|
+
|
|
24
|
+
const cache = createCache({
|
|
25
|
+
adapter: 'memory', // or 'redis', 'upstash', 'cloudflare-kv'
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Set value
|
|
29
|
+
await cache.set('key', { data: 'value' }, { ttl: 3600 });
|
|
30
|
+
|
|
31
|
+
// Get value
|
|
32
|
+
const value = await cache.get('key');
|
|
33
|
+
|
|
34
|
+
// Delete
|
|
35
|
+
await cache.delete('key');
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## API Overview
|
|
39
|
+
|
|
40
|
+
### Adapters
|
|
41
|
+
|
|
42
|
+
#### Memory (Development)
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { createMemoryCache } from '@parsrun/cache/adapters/memory';
|
|
46
|
+
|
|
47
|
+
const cache = createMemoryCache({
|
|
48
|
+
maxSize: 1000,
|
|
49
|
+
defaultTTL: 3600,
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
#### Redis
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { createRedisCache } from '@parsrun/cache/adapters/redis';
|
|
57
|
+
|
|
58
|
+
const cache = createRedisCache({
|
|
59
|
+
url: 'redis://localhost:6379',
|
|
60
|
+
prefix: 'myapp:',
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### Upstash (Serverless Redis)
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { createUpstashCache } from '@parsrun/cache/adapters/upstash';
|
|
68
|
+
|
|
69
|
+
const cache = createUpstashCache({
|
|
70
|
+
url: process.env.UPSTASH_REDIS_URL,
|
|
71
|
+
token: process.env.UPSTASH_REDIS_TOKEN,
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### Cloudflare KV
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { createCloudflareKVCache } from '@parsrun/cache/adapters/cloudflare-kv';
|
|
79
|
+
|
|
80
|
+
// In Cloudflare Worker
|
|
81
|
+
const cache = createCloudflareKVCache({
|
|
82
|
+
namespace: env.CACHE_KV,
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Cache Operations
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// Set with TTL (seconds)
|
|
90
|
+
await cache.set('user:1', userData, { ttl: 3600 });
|
|
91
|
+
|
|
92
|
+
// Set with tags
|
|
93
|
+
await cache.set('user:1', userData, {
|
|
94
|
+
ttl: 3600,
|
|
95
|
+
tags: ['users', 'user:1'],
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Get
|
|
99
|
+
const user = await cache.get<User>('user:1');
|
|
100
|
+
|
|
101
|
+
// Get or set
|
|
102
|
+
const user = await cache.getOrSet('user:1', async () => {
|
|
103
|
+
return await fetchUser(1);
|
|
104
|
+
}, { ttl: 3600 });
|
|
105
|
+
|
|
106
|
+
// Delete
|
|
107
|
+
await cache.delete('user:1');
|
|
108
|
+
|
|
109
|
+
// Delete by tag
|
|
110
|
+
await cache.deleteByTag('users');
|
|
111
|
+
|
|
112
|
+
// Clear all
|
|
113
|
+
await cache.clear();
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### With Namespace
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const userCache = cache.namespace('users');
|
|
120
|
+
await userCache.set('1', userData); // Key: users:1
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Exports
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { ... } from '@parsrun/cache'; // Main exports
|
|
127
|
+
import { ... } from '@parsrun/cache/adapters/memory'; // Memory adapter
|
|
128
|
+
import { ... } from '@parsrun/cache/adapters/redis'; // Redis adapter
|
|
129
|
+
import { ... } from '@parsrun/cache/adapters/upstash'; // Upstash adapter
|
|
130
|
+
import { ... } from '@parsrun/cache/adapters/cloudflare-kv'; // Cloudflare KV
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { CacheAdapter, CloudflareKVCacheConfig, CacheGetOptions, CacheSetOptions } from '../types.js';
|
|
2
|
+
import '@parsrun/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @parsrun/cache - Cloudflare KV Adapter
|
|
6
|
+
* Cache adapter for Cloudflare Workers KV
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Cloudflare KV Cache Adapter
|
|
11
|
+
* Uses Cloudflare Workers KV for edge caching
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // In Cloudflare Workers
|
|
16
|
+
* export default {
|
|
17
|
+
* async fetch(request, env) {
|
|
18
|
+
* const cache = new CloudflareKVCacheAdapter({
|
|
19
|
+
* namespace: env.CACHE_KV,
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* await cache.set('user:123', { name: 'John' }, { ttl: 3600 });
|
|
23
|
+
* const user = await cache.get('user:123');
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
declare class CloudflareKVCacheAdapter implements CacheAdapter {
|
|
29
|
+
readonly type: "cloudflare-kv";
|
|
30
|
+
private kv;
|
|
31
|
+
private keyPrefix;
|
|
32
|
+
constructor(config: CloudflareKVCacheConfig);
|
|
33
|
+
private prefixKey;
|
|
34
|
+
get<T = unknown>(key: string, _options?: CacheGetOptions): Promise<T | null>;
|
|
35
|
+
set<T = unknown>(key: string, value: T, options?: CacheSetOptions): Promise<void>;
|
|
36
|
+
delete(key: string): Promise<void>;
|
|
37
|
+
has(key: string): Promise<boolean>;
|
|
38
|
+
clear(): Promise<void>;
|
|
39
|
+
getMany<T = unknown>(keys: string[]): Promise<Map<string, T | null>>;
|
|
40
|
+
setMany<T = unknown>(entries: Map<string, T>, options?: CacheSetOptions): Promise<void>;
|
|
41
|
+
deleteMany(keys: string[]): Promise<void>;
|
|
42
|
+
invalidateByTags(tags: string[]): Promise<void>;
|
|
43
|
+
ttl(_key: string): Promise<number>;
|
|
44
|
+
close(): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create a Cloudflare KV cache adapter
|
|
48
|
+
*/
|
|
49
|
+
declare function createCloudflareKVCacheAdapter(config: CloudflareKVCacheConfig): CloudflareKVCacheAdapter;
|
|
50
|
+
|
|
51
|
+
export { CloudflareKVCacheAdapter, createCloudflareKVCacheAdapter };
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
import {
|
|
3
|
+
type,
|
|
4
|
+
cacheSetOptions,
|
|
5
|
+
cacheGetResult,
|
|
6
|
+
cacheStats,
|
|
7
|
+
memoryCacheConfig,
|
|
8
|
+
redisCacheConfig,
|
|
9
|
+
upstashCacheConfig,
|
|
10
|
+
cloudflareKvConfig,
|
|
11
|
+
multiTierCacheConfig,
|
|
12
|
+
cacheConfig
|
|
13
|
+
} from "@parsrun/types";
|
|
14
|
+
var CacheError = class extends Error {
|
|
15
|
+
constructor(message, code, cause) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.code = code;
|
|
18
|
+
this.cause = cause;
|
|
19
|
+
this.name = "CacheError";
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var CacheErrorCodes = {
|
|
23
|
+
CONNECTION_FAILED: "CONNECTION_FAILED",
|
|
24
|
+
OPERATION_FAILED: "OPERATION_FAILED",
|
|
25
|
+
SERIALIZATION_ERROR: "SERIALIZATION_ERROR",
|
|
26
|
+
INVALID_CONFIG: "INVALID_CONFIG",
|
|
27
|
+
NOT_IMPLEMENTED: "NOT_IMPLEMENTED"
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/adapters/cloudflare-kv.ts
|
|
31
|
+
var CloudflareKVCacheAdapter = class {
|
|
32
|
+
type = "cloudflare-kv";
|
|
33
|
+
kv;
|
|
34
|
+
keyPrefix;
|
|
35
|
+
constructor(config) {
|
|
36
|
+
this.kv = config.namespace;
|
|
37
|
+
this.keyPrefix = config.keyPrefix ?? "";
|
|
38
|
+
}
|
|
39
|
+
prefixKey(key) {
|
|
40
|
+
return this.keyPrefix ? `${this.keyPrefix}:${key}` : key;
|
|
41
|
+
}
|
|
42
|
+
async get(key, _options) {
|
|
43
|
+
try {
|
|
44
|
+
const data = await this.kv.get(this.prefixKey(key), { type: "json" });
|
|
45
|
+
if (!data) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return data.value;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
throw new CacheError(
|
|
51
|
+
`Cloudflare KV get failed: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
52
|
+
CacheErrorCodes.OPERATION_FAILED,
|
|
53
|
+
err
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async set(key, value, options) {
|
|
58
|
+
try {
|
|
59
|
+
const data = {
|
|
60
|
+
value,
|
|
61
|
+
tags: options?.tags,
|
|
62
|
+
metadata: options?.metadata
|
|
63
|
+
};
|
|
64
|
+
const putOptions = {};
|
|
65
|
+
if (options?.ttl) {
|
|
66
|
+
putOptions.expirationTtl = options.ttl;
|
|
67
|
+
}
|
|
68
|
+
if (options?.tags || options?.metadata) {
|
|
69
|
+
putOptions.metadata = {
|
|
70
|
+
tags: options.tags,
|
|
71
|
+
...options.metadata
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
await this.kv.put(this.prefixKey(key), JSON.stringify(data), putOptions);
|
|
75
|
+
if (options?.tags && options.tags.length > 0) {
|
|
76
|
+
for (const tag of options.tags) {
|
|
77
|
+
const tagKey = this.prefixKey(`__tag:${tag}`);
|
|
78
|
+
const existing = await this.kv.get(tagKey, { type: "json" });
|
|
79
|
+
const keys = existing ? [.../* @__PURE__ */ new Set([...existing, key])] : [key];
|
|
80
|
+
await this.kv.put(tagKey, JSON.stringify(keys));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
throw new CacheError(
|
|
85
|
+
`Cloudflare KV set failed: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
86
|
+
CacheErrorCodes.OPERATION_FAILED,
|
|
87
|
+
err
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async delete(key) {
|
|
92
|
+
try {
|
|
93
|
+
await this.kv.delete(this.prefixKey(key));
|
|
94
|
+
} catch (err) {
|
|
95
|
+
throw new CacheError(
|
|
96
|
+
`Cloudflare KV delete failed: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
97
|
+
CacheErrorCodes.OPERATION_FAILED,
|
|
98
|
+
err
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async has(key) {
|
|
103
|
+
try {
|
|
104
|
+
const value = await this.kv.get(this.prefixKey(key));
|
|
105
|
+
return value !== null;
|
|
106
|
+
} catch (err) {
|
|
107
|
+
throw new CacheError(
|
|
108
|
+
`Cloudflare KV has failed: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
109
|
+
CacheErrorCodes.OPERATION_FAILED,
|
|
110
|
+
err
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async clear() {
|
|
115
|
+
try {
|
|
116
|
+
const prefix = this.keyPrefix ? `${this.keyPrefix}:` : "";
|
|
117
|
+
let cursor;
|
|
118
|
+
do {
|
|
119
|
+
const result = await this.kv.list({
|
|
120
|
+
prefix,
|
|
121
|
+
limit: 1e3,
|
|
122
|
+
cursor
|
|
123
|
+
});
|
|
124
|
+
for (const key of result.keys) {
|
|
125
|
+
await this.kv.delete(key.name);
|
|
126
|
+
}
|
|
127
|
+
cursor = result.list_complete ? void 0 : result.cursor;
|
|
128
|
+
} while (cursor);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
throw new CacheError(
|
|
131
|
+
`Cloudflare KV clear failed: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
132
|
+
CacheErrorCodes.OPERATION_FAILED,
|
|
133
|
+
err
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async getMany(keys) {
|
|
138
|
+
const map = /* @__PURE__ */ new Map();
|
|
139
|
+
const promises = keys.map(async (key) => {
|
|
140
|
+
const value = await this.get(key);
|
|
141
|
+
return { key, value };
|
|
142
|
+
});
|
|
143
|
+
const results = await Promise.all(promises);
|
|
144
|
+
for (const { key, value } of results) {
|
|
145
|
+
map.set(key, value);
|
|
146
|
+
}
|
|
147
|
+
return map;
|
|
148
|
+
}
|
|
149
|
+
async setMany(entries, options) {
|
|
150
|
+
const promises = [];
|
|
151
|
+
for (const [key, value] of entries) {
|
|
152
|
+
promises.push(this.set(key, value, options));
|
|
153
|
+
}
|
|
154
|
+
await Promise.all(promises);
|
|
155
|
+
}
|
|
156
|
+
async deleteMany(keys) {
|
|
157
|
+
const promises = keys.map((key) => this.delete(key));
|
|
158
|
+
await Promise.all(promises);
|
|
159
|
+
}
|
|
160
|
+
async invalidateByTags(tags) {
|
|
161
|
+
try {
|
|
162
|
+
const keysToDelete = /* @__PURE__ */ new Set();
|
|
163
|
+
for (const tag of tags) {
|
|
164
|
+
const tagKey = this.prefixKey(`__tag:${tag}`);
|
|
165
|
+
const keys = await this.kv.get(tagKey, { type: "json" });
|
|
166
|
+
if (keys) {
|
|
167
|
+
for (const key of keys) {
|
|
168
|
+
keysToDelete.add(key);
|
|
169
|
+
}
|
|
170
|
+
await this.kv.delete(tagKey);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (keysToDelete.size > 0) {
|
|
174
|
+
await this.deleteMany([...keysToDelete]);
|
|
175
|
+
}
|
|
176
|
+
} catch (err) {
|
|
177
|
+
throw new CacheError(
|
|
178
|
+
`Cloudflare KV invalidate by tags failed: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
179
|
+
CacheErrorCodes.OPERATION_FAILED,
|
|
180
|
+
err
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async ttl(_key) {
|
|
185
|
+
return -1;
|
|
186
|
+
}
|
|
187
|
+
async close() {
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
function createCloudflareKVCacheAdapter(config) {
|
|
191
|
+
return new CloudflareKVCacheAdapter(config);
|
|
192
|
+
}
|
|
193
|
+
export {
|
|
194
|
+
CloudflareKVCacheAdapter,
|
|
195
|
+
createCloudflareKVCacheAdapter
|
|
196
|
+
};
|
|
197
|
+
//# sourceMappingURL=cloudflare-kv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/types.ts","../../src/adapters/cloudflare-kv.ts"],"sourcesContent":["/**\n * @parsrun/cache - Type Definitions\n * Cache types and interfaces\n */\n\n// Re-export types from @parsrun/types for convenience\nexport {\n type,\n cacheSetOptions as parsCacheSetOptions,\n cacheGetResult,\n cacheStats,\n memoryCacheConfig,\n redisCacheConfig,\n upstashCacheConfig,\n cloudflareKvConfig,\n multiTierCacheConfig,\n cacheConfig,\n type CacheSetOptions as ParsCacheSetOptions,\n type CacheGetResult,\n type CacheStats,\n type MemoryCacheConfig as ParsMemoryCacheConfig,\n type RedisCacheConfig as ParsRedisCacheConfig,\n type UpstashCacheConfig as ParsUpstashCacheConfig,\n type CloudflareKvConfig as ParsCloudflareKvConfig,\n type MultiTierCacheConfig,\n type CacheConfig,\n} from \"@parsrun/types\";\n\n/**\n * Cache adapter type\n */\nexport type CacheAdapterType = \"memory\" | \"redis\" | \"upstash\" | \"cloudflare-kv\";\n\n/**\n * Cache entry with metadata\n */\nexport interface CacheEntry<T = unknown> {\n /** Cached value */\n value: T;\n /** Expiration timestamp (ms) */\n expiresAt?: number | undefined;\n /** Original TTL in seconds (for refresh/sliding expiration) */\n ttlSeconds?: number | undefined;\n /** Cache entry tags for invalidation */\n tags?: string[] | undefined;\n /** Cache entry metadata */\n metadata?: Record<string, unknown> | undefined;\n}\n\n/**\n * Cache get options\n */\nexport interface CacheGetOptions {\n /** Whether to update TTL on access (sliding expiration) */\n refresh?: boolean | undefined;\n}\n\n/**\n * Cache set options\n */\nexport interface CacheSetOptions {\n /** Time to live in seconds */\n ttl?: number | undefined;\n /** Tags for cache invalidation */\n tags?: string[] | undefined;\n /** Additional metadata */\n metadata?: Record<string, unknown> | undefined;\n}\n\n/**\n * Cache delete options\n */\nexport interface CacheDeleteOptions {\n /** Delete all entries with matching tags */\n tags?: string[] | undefined;\n}\n\n/**\n * Cache adapter interface\n */\nexport interface CacheAdapter {\n /** Adapter type */\n readonly type: CacheAdapterType;\n\n /**\n * Get a value from cache\n * @param key Cache key\n * @param options Get options\n * @returns Cached value or null if not found/expired\n */\n get<T = unknown>(key: string, options?: CacheGetOptions): Promise<T | null>;\n\n /**\n * Set a value in cache\n * @param key Cache key\n * @param value Value to cache\n * @param options Set options\n */\n set<T = unknown>(key: string, value: T, options?: CacheSetOptions): Promise<void>;\n\n /**\n * Delete a value from cache\n * @param key Cache key\n */\n delete(key: string): Promise<void>;\n\n /**\n * Check if a key exists in cache\n * @param key Cache key\n */\n has(key: string): Promise<boolean>;\n\n /**\n * Clear all entries from cache\n */\n clear?(): Promise<void>;\n\n /**\n * Get multiple values at once\n * @param keys Cache keys\n */\n getMany?<T = unknown>(keys: string[]): Promise<Map<string, T | null>>;\n\n /**\n * Set multiple values at once\n * @param entries Key-value pairs\n * @param options Set options (applied to all)\n */\n setMany?<T = unknown>(entries: Map<string, T>, options?: CacheSetOptions): Promise<void>;\n\n /**\n * Delete multiple keys\n * @param keys Cache keys\n */\n deleteMany?(keys: string[]): Promise<void>;\n\n /**\n * Invalidate entries by tags\n * @param tags Tags to invalidate\n */\n invalidateByTags?(tags: string[]): Promise<void>;\n\n /**\n * Get TTL for a key (in seconds)\n * @param key Cache key\n * @returns TTL in seconds, -1 if no expiry, -2 if key doesn't exist\n */\n ttl?(key: string): Promise<number>;\n\n /**\n * Close/cleanup adapter resources\n */\n close?(): Promise<void>;\n}\n\n/**\n * Cache service configuration\n */\nexport interface CacheServiceConfig {\n /** Cache adapter to use */\n adapter: CacheAdapter;\n /** Default TTL in seconds */\n defaultTtl?: number | undefined;\n /** Key prefix for namespacing */\n keyPrefix?: string | undefined;\n /** Enable debug logging */\n debug?: boolean | undefined;\n}\n\n/**\n * Memory adapter configuration\n */\nexport interface MemoryCacheConfig {\n /** Maximum number of entries */\n maxEntries?: number | undefined;\n /** Cleanup interval in ms (default: 60000) */\n cleanupInterval?: number | undefined;\n}\n\n/**\n * Redis adapter configuration\n */\nexport interface RedisCacheConfig {\n /** Redis connection URL */\n url?: string | undefined;\n /** Redis host */\n host?: string | undefined;\n /** Redis port */\n port?: number | undefined;\n /** Redis password */\n password?: string | undefined;\n /** Redis database number */\n db?: number | undefined;\n /** Key prefix */\n keyPrefix?: string | undefined;\n /** Use TLS */\n tls?: boolean | undefined;\n}\n\n/**\n * Upstash adapter configuration\n */\nexport interface UpstashCacheConfig {\n /** Upstash Redis URL */\n url: string;\n /** Upstash Redis token */\n token: string;\n /** Key prefix */\n keyPrefix?: string | undefined;\n}\n\n/**\n * Cloudflare KV adapter configuration\n */\nexport interface CloudflareKVCacheConfig {\n /** KV namespace binding */\n namespace: KVNamespace;\n /** Key prefix */\n keyPrefix?: string | undefined;\n}\n\n/**\n * KVNamespace interface (Cloudflare Workers)\n */\nexport interface KVNamespace {\n get(key: string, options?: { type?: \"text\" | \"json\" | \"arrayBuffer\" | \"stream\" }): Promise<string | null>;\n get(key: string, options: { type: \"json\" }): Promise<unknown>;\n put(key: string, value: string | ArrayBuffer | ReadableStream, options?: { expiration?: number; expirationTtl?: number; metadata?: unknown }): Promise<void>;\n delete(key: string): Promise<void>;\n list(options?: { prefix?: string | undefined; limit?: number | undefined; cursor?: string | undefined }): Promise<{ keys: Array<{ name: string; expiration?: number; metadata?: unknown }>; list_complete: boolean; cursor?: string }>;\n getWithMetadata<T = unknown>(key: string, options?: { type?: \"text\" | \"json\" | \"arrayBuffer\" | \"stream\" }): Promise<{ value: string | null; metadata: T | null }>;\n}\n\n/**\n * Cache error\n */\nexport class CacheError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly cause?: unknown\n ) {\n super(message);\n this.name = \"CacheError\";\n }\n}\n\n/**\n * Common cache error codes\n */\nexport const CacheErrorCodes = {\n CONNECTION_FAILED: \"CONNECTION_FAILED\",\n OPERATION_FAILED: \"OPERATION_FAILED\",\n SERIALIZATION_ERROR: \"SERIALIZATION_ERROR\",\n INVALID_CONFIG: \"INVALID_CONFIG\",\n NOT_IMPLEMENTED: \"NOT_IMPLEMENTED\",\n} as const;\n","/**\n * @parsrun/cache - Cloudflare KV Adapter\n * Cache adapter for Cloudflare Workers KV\n */\n\nimport type {\n CacheAdapter,\n CacheGetOptions,\n CacheSetOptions,\n CloudflareKVCacheConfig,\n KVNamespace,\n} from \"../types.js\";\nimport { CacheError, CacheErrorCodes } from \"../types.js\";\n\n/**\n * Cloudflare KV Cache Adapter\n * Uses Cloudflare Workers KV for edge caching\n *\n * @example\n * ```typescript\n * // In Cloudflare Workers\n * export default {\n * async fetch(request, env) {\n * const cache = new CloudflareKVCacheAdapter({\n * namespace: env.CACHE_KV,\n * });\n *\n * await cache.set('user:123', { name: 'John' }, { ttl: 3600 });\n * const user = await cache.get('user:123');\n * }\n * }\n * ```\n */\nexport class CloudflareKVCacheAdapter implements CacheAdapter {\n readonly type = \"cloudflare-kv\" as const;\n\n private kv: KVNamespace;\n private keyPrefix: string;\n\n constructor(config: CloudflareKVCacheConfig) {\n this.kv = config.namespace;\n this.keyPrefix = config.keyPrefix ?? \"\";\n }\n\n private prefixKey(key: string): string {\n return this.keyPrefix ? `${this.keyPrefix}:${key}` : key;\n }\n\n async get<T = unknown>(key: string, _options?: CacheGetOptions): Promise<T | null> {\n try {\n const data = await this.kv.get(this.prefixKey(key), { type: \"json\" }) as { value: T; tags?: string[] } | null;\n\n if (!data) {\n return null;\n }\n\n return data.value;\n } catch (err) {\n throw new CacheError(\n `Cloudflare KV get failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n CacheErrorCodes.OPERATION_FAILED,\n err\n );\n }\n }\n\n async set<T = unknown>(key: string, value: T, options?: CacheSetOptions): Promise<void> {\n try {\n const data = {\n value,\n tags: options?.tags,\n metadata: options?.metadata,\n };\n\n const putOptions: { expirationTtl?: number; metadata?: unknown } = {};\n\n if (options?.ttl) {\n putOptions.expirationTtl = options.ttl;\n }\n\n // Store metadata in KV metadata field\n if (options?.tags || options?.metadata) {\n putOptions.metadata = {\n tags: options.tags,\n ...options.metadata,\n };\n }\n\n await this.kv.put(this.prefixKey(key), JSON.stringify(data), putOptions);\n\n // Store tag-to-key mappings\n if (options?.tags && options.tags.length > 0) {\n for (const tag of options.tags) {\n const tagKey = this.prefixKey(`__tag:${tag}`);\n const existing = await this.kv.get(tagKey, { type: \"json\" }) as string[] | null;\n const keys = existing ? [...new Set([...existing, key])] : [key];\n await this.kv.put(tagKey, JSON.stringify(keys));\n }\n }\n } catch (err) {\n throw new CacheError(\n `Cloudflare KV set failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n CacheErrorCodes.OPERATION_FAILED,\n err\n );\n }\n }\n\n async delete(key: string): Promise<void> {\n try {\n await this.kv.delete(this.prefixKey(key));\n } catch (err) {\n throw new CacheError(\n `Cloudflare KV delete failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n CacheErrorCodes.OPERATION_FAILED,\n err\n );\n }\n }\n\n async has(key: string): Promise<boolean> {\n try {\n const value = await this.kv.get(this.prefixKey(key));\n return value !== null;\n } catch (err) {\n throw new CacheError(\n `Cloudflare KV has failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n CacheErrorCodes.OPERATION_FAILED,\n err\n );\n }\n }\n\n async clear(): Promise<void> {\n try {\n const prefix = this.keyPrefix ? `${this.keyPrefix}:` : \"\";\n let cursor: string | undefined;\n\n do {\n const result = await this.kv.list({\n prefix,\n limit: 1000,\n cursor,\n });\n\n for (const key of result.keys) {\n await this.kv.delete(key.name);\n }\n\n cursor = result.list_complete ? undefined : result.cursor;\n } while (cursor);\n } catch (err) {\n throw new CacheError(\n `Cloudflare KV clear failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n CacheErrorCodes.OPERATION_FAILED,\n err\n );\n }\n }\n\n async getMany<T = unknown>(keys: string[]): Promise<Map<string, T | null>> {\n const map = new Map<string, T | null>();\n\n // KV doesn't support batch get, so we do parallel fetches\n const promises = keys.map(async (key) => {\n const value = await this.get<T>(key);\n return { key, value };\n });\n\n const results = await Promise.all(promises);\n\n for (const { key, value } of results) {\n map.set(key, value);\n }\n\n return map;\n }\n\n async setMany<T = unknown>(entries: Map<string, T>, options?: CacheSetOptions): Promise<void> {\n // KV doesn't support batch write, so we do parallel writes\n const promises: Promise<void>[] = [];\n\n for (const [key, value] of entries) {\n promises.push(this.set(key, value, options));\n }\n\n await Promise.all(promises);\n }\n\n async deleteMany(keys: string[]): Promise<void> {\n // KV doesn't support batch delete, so we do parallel deletes\n const promises = keys.map((key) => this.delete(key));\n await Promise.all(promises);\n }\n\n async invalidateByTags(tags: string[]): Promise<void> {\n try {\n const keysToDelete = new Set<string>();\n\n for (const tag of tags) {\n const tagKey = this.prefixKey(`__tag:${tag}`);\n const keys = await this.kv.get(tagKey, { type: \"json\" }) as string[] | null;\n\n if (keys) {\n for (const key of keys) {\n keysToDelete.add(key);\n }\n // Delete tag mapping\n await this.kv.delete(tagKey);\n }\n }\n\n if (keysToDelete.size > 0) {\n await this.deleteMany([...keysToDelete]);\n }\n } catch (err) {\n throw new CacheError(\n `Cloudflare KV invalidate by tags failed: ${err instanceof Error ? err.message : \"Unknown error\"}`,\n CacheErrorCodes.OPERATION_FAILED,\n err\n );\n }\n }\n\n async ttl(_key: string): Promise<number> {\n // Cloudflare KV doesn't expose TTL, return -1 (unknown)\n return -1;\n }\n\n async close(): Promise<void> {\n // No-op for KV\n }\n}\n\n/**\n * Create a Cloudflare KV cache adapter\n */\nexport function createCloudflareKVCacheAdapter(config: CloudflareKVCacheConfig): CloudflareKVCacheAdapter {\n return new CloudflareKVCacheAdapter(config);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { MemoryCacheAdapter, createMemoryCacheAdapter } from './memory.js';
|
|
2
|
+
export { RedisCacheAdapter, createRedisCacheAdapter } from './redis.js';
|
|
3
|
+
export { UpstashCacheAdapter, createUpstashCacheAdapter } from './upstash.js';
|
|
4
|
+
export { CloudflareKVCacheAdapter, createCloudflareKVCacheAdapter } from './cloudflare-kv.js';
|
|
5
|
+
import '../types.js';
|
|
6
|
+
import '@parsrun/types';
|