@eddacraft/anvil-runtime 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/LICENSE +14 -0
- package/dist/cache/cache-key.d.ts +45 -0
- package/dist/cache/cache-key.d.ts.map +1 -0
- package/dist/cache/cache-key.js +135 -0
- package/dist/cache/index.d.ts +27 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +38 -0
- package/dist/cache/providers/file-cache.d.ts +63 -0
- package/dist/cache/providers/file-cache.d.ts.map +1 -0
- package/dist/cache/providers/file-cache.js +369 -0
- package/dist/cache/providers/memory-cache.d.ts +52 -0
- package/dist/cache/providers/memory-cache.d.ts.map +1 -0
- package/dist/cache/providers/memory-cache.js +197 -0
- package/dist/cache/providers/null-cache.d.ts +26 -0
- package/dist/cache/providers/null-cache.d.ts.map +1 -0
- package/dist/cache/providers/null-cache.js +50 -0
- package/dist/cache/types.d.ts +114 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +4 -0
- package/dist/concurrency/agent.d.ts +137 -0
- package/dist/concurrency/agent.d.ts.map +1 -0
- package/dist/concurrency/agent.js +440 -0
- package/dist/concurrency/atomic.d.ts +93 -0
- package/dist/concurrency/atomic.d.ts.map +1 -0
- package/dist/concurrency/atomic.js +281 -0
- package/dist/concurrency/git-agent.d.ts +114 -0
- package/dist/concurrency/git-agent.d.ts.map +1 -0
- package/dist/concurrency/git-agent.js +313 -0
- package/dist/concurrency/index.d.ts +95 -0
- package/dist/concurrency/index.d.ts.map +1 -0
- package/dist/concurrency/index.js +127 -0
- package/dist/concurrency/lock-manager.d.ts +170 -0
- package/dist/concurrency/lock-manager.d.ts.map +1 -0
- package/dist/concurrency/lock-manager.js +525 -0
- package/dist/concurrency/queue-manager.d.ts +166 -0
- package/dist/concurrency/queue-manager.d.ts.map +1 -0
- package/dist/concurrency/queue-manager.js +442 -0
- package/dist/concurrency/types.d.ts +382 -0
- package/dist/concurrency/types.d.ts.map +1 -0
- package/dist/concurrency/types.js +204 -0
- package/dist/export/constraint-collector.d.ts +175 -0
- package/dist/export/constraint-collector.d.ts.map +1 -0
- package/dist/export/constraint-collector.js +203 -0
- package/dist/export/formatters/llms-txt-formatter.d.ts +89 -0
- package/dist/export/formatters/llms-txt-formatter.d.ts.map +1 -0
- package/dist/export/formatters/llms-txt-formatter.js +249 -0
- package/dist/export/formatters/mcp-resource-formatter.d.ts +186 -0
- package/dist/export/formatters/mcp-resource-formatter.d.ts.map +1 -0
- package/dist/export/formatters/mcp-resource-formatter.js +139 -0
- package/dist/export/formatters/prompt-formatter.d.ts +83 -0
- package/dist/export/formatters/prompt-formatter.d.ts.map +1 -0
- package/dist/export/formatters/prompt-formatter.js +256 -0
- package/dist/export/index.d.ts +10 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/export/index.js +9 -0
- package/dist/gate/check.interface.d.ts +15 -0
- package/dist/gate/check.interface.d.ts.map +1 -0
- package/dist/gate/check.interface.js +18 -0
- package/dist/gate/checks/antipattern.check.d.ts +27 -0
- package/dist/gate/checks/antipattern.check.d.ts.map +1 -0
- package/dist/gate/checks/antipattern.check.js +140 -0
- package/dist/gate/checks/architecture/circular-detector.d.ts +33 -0
- package/dist/gate/checks/architecture/circular-detector.d.ts.map +1 -0
- package/dist/gate/checks/architecture/circular-detector.js +71 -0
- package/dist/gate/checks/architecture/dependency-analyzer.d.ts +81 -0
- package/dist/gate/checks/architecture/dependency-analyzer.d.ts.map +1 -0
- package/dist/gate/checks/architecture/dependency-analyzer.js +136 -0
- package/dist/gate/checks/architecture/layer-validator.d.ts +75 -0
- package/dist/gate/checks/architecture/layer-validator.d.ts.map +1 -0
- package/dist/gate/checks/architecture/layer-validator.js +193 -0
- package/dist/gate/checks/architecture.check.d.ts +56 -0
- package/dist/gate/checks/architecture.check.d.ts.map +1 -0
- package/dist/gate/checks/architecture.check.js +394 -0
- package/dist/gate/checks/command-safety.check.d.ts +12 -0
- package/dist/gate/checks/command-safety.check.d.ts.map +1 -0
- package/dist/gate/checks/command-safety.check.js +230 -0
- package/dist/gate/checks/coverage.check.d.ts +9 -0
- package/dist/gate/checks/coverage.check.d.ts.map +1 -0
- package/dist/gate/checks/coverage.check.js +81 -0
- package/dist/gate/checks/dependency.check.d.ts +17 -0
- package/dist/gate/checks/dependency.check.d.ts.map +1 -0
- package/dist/gate/checks/dependency.check.js +342 -0
- package/dist/gate/checks/eslint.check.d.ts +14 -0
- package/dist/gate/checks/eslint.check.d.ts.map +1 -0
- package/dist/gate/checks/eslint.check.js +79 -0
- package/dist/gate/checks/policy.check.d.ts +78 -0
- package/dist/gate/checks/policy.check.d.ts.map +1 -0
- package/dist/gate/checks/policy.check.js +457 -0
- package/dist/gate/checks/secret/entropy-detector.d.ts +44 -0
- package/dist/gate/checks/secret/entropy-detector.d.ts.map +1 -0
- package/dist/gate/checks/secret/entropy-detector.js +76 -0
- package/dist/gate/checks/secret/git-scanner.d.ts +36 -0
- package/dist/gate/checks/secret/git-scanner.d.ts.map +1 -0
- package/dist/gate/checks/secret/git-scanner.js +90 -0
- package/dist/gate/checks/secret/secret-patterns.d.ts +42 -0
- package/dist/gate/checks/secret/secret-patterns.d.ts.map +1 -0
- package/dist/gate/checks/secret/secret-patterns.js +137 -0
- package/dist/gate/checks/secret.check.d.ts +56 -0
- package/dist/gate/checks/secret.check.d.ts.map +1 -0
- package/dist/gate/checks/secret.check.js +245 -0
- package/dist/gate/config/command-safety-config.d.ts +5 -0
- package/dist/gate/config/command-safety-config.d.ts.map +1 -0
- package/dist/gate/config/command-safety-config.js +69 -0
- package/dist/gate/config/index.d.ts +2 -0
- package/dist/gate/config/index.d.ts.map +1 -0
- package/dist/gate/config/index.js +1 -0
- package/dist/gate/formatters/command-safety-formatter.d.ts +10 -0
- package/dist/gate/formatters/command-safety-formatter.d.ts.map +1 -0
- package/dist/gate/formatters/command-safety-formatter.js +64 -0
- package/dist/gate/formatters/index.d.ts +2 -0
- package/dist/gate/formatters/index.d.ts.map +1 -0
- package/dist/gate/formatters/index.js +1 -0
- package/dist/gate/gate-config.d.ts +44 -0
- package/dist/gate/gate-config.d.ts.map +1 -0
- package/dist/gate/gate-config.js +334 -0
- package/dist/gate/gate-runner.d.ts +160 -0
- package/dist/gate/gate-runner.d.ts.map +1 -0
- package/dist/gate/gate-runner.js +531 -0
- package/dist/gate/index.d.ts +20 -0
- package/dist/gate/index.d.ts.map +1 -0
- package/dist/gate/index.js +14 -0
- package/dist/gate/parsers/command-parser.d.ts +18 -0
- package/dist/gate/parsers/command-parser.d.ts.map +1 -0
- package/dist/gate/parsers/command-parser.js +363 -0
- package/dist/gate/parsers/index.d.ts +2 -0
- package/dist/gate/parsers/index.d.ts.map +1 -0
- package/dist/gate/parsers/index.js +1 -0
- package/dist/gate/policy/index.d.ts +12 -0
- package/dist/gate/policy/index.d.ts.map +1 -0
- package/dist/gate/policy/index.js +10 -0
- package/dist/gate/rules/default-filesystem-rules.d.ts +3 -0
- package/dist/gate/rules/default-filesystem-rules.d.ts.map +1 -0
- package/dist/gate/rules/default-filesystem-rules.js +201 -0
- package/dist/gate/rules/default-git-rules.d.ts +3 -0
- package/dist/gate/rules/default-git-rules.d.ts.map +1 -0
- package/dist/gate/rules/default-git-rules.js +192 -0
- package/dist/gate/rules/index.d.ts +5 -0
- package/dist/gate/rules/index.d.ts.map +1 -0
- package/dist/gate/rules/index.js +3 -0
- package/dist/gate/rules/rule-matcher.d.ts +27 -0
- package/dist/gate/rules/rule-matcher.d.ts.map +1 -0
- package/dist/gate/rules/rule-matcher.js +228 -0
- package/dist/gate/rules/types.d.ts +250 -0
- package/dist/gate/rules/types.d.ts.map +1 -0
- package/dist/gate/rules/types.js +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/types/gate.types.d.ts +42 -0
- package/dist/types/gate.types.d.ts.map +1 -0
- package/dist/types/gate.types.js +94 -0
- package/dist/watch/debouncer.d.ts +90 -0
- package/dist/watch/debouncer.d.ts.map +1 -0
- package/dist/watch/debouncer.js +135 -0
- package/dist/watch/file-watcher.d.ts +73 -0
- package/dist/watch/file-watcher.d.ts.map +1 -0
- package/dist/watch/file-watcher.js +121 -0
- package/dist/watch/git-status.d.ts +98 -0
- package/dist/watch/git-status.d.ts.map +1 -0
- package/dist/watch/git-status.js +266 -0
- package/dist/watch/index.d.ts +16 -0
- package/dist/watch/index.d.ts.map +1 -0
- package/dist/watch/index.js +15 -0
- package/dist/watch/orchestrator.d.ts +113 -0
- package/dist/watch/orchestrator.d.ts.map +1 -0
- package/dist/watch/orchestrator.js +409 -0
- package/dist/watch/types.d.ts +190 -0
- package/dist/watch/types.d.ts.map +1 -0
- package/dist/watch/types.js +76 -0
- package/package.json +60 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based cache provider
|
|
3
|
+
* Stores cache entries as JSON files in .anvil/cache/
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, unlinkSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
6
|
+
import { readFile, rm, mkdir } from 'node:fs/promises';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { createHash, createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { createDebugger } from '@eddacraft/anvil-core';
|
|
12
|
+
import { atomicWriteText } from '../../concurrency/atomic.js';
|
|
13
|
+
const debug = createDebugger('cache');
|
|
14
|
+
const CacheEntrySchema = z.object({
|
|
15
|
+
value: z.unknown(),
|
|
16
|
+
created_at: z.number(),
|
|
17
|
+
expires_at: z.number().optional(),
|
|
18
|
+
key: z.string(),
|
|
19
|
+
input_hash: z.string().optional(),
|
|
20
|
+
});
|
|
21
|
+
const CacheIndexEntrySchema = z.object({
|
|
22
|
+
file: z.string(),
|
|
23
|
+
created_at: z.number(),
|
|
24
|
+
expires_at: z.number().optional(),
|
|
25
|
+
size_bytes: z.number(),
|
|
26
|
+
});
|
|
27
|
+
const CacheIndexSchema = z.object({
|
|
28
|
+
version: z.number(),
|
|
29
|
+
entries: z.record(z.string(), CacheIndexEntrySchema),
|
|
30
|
+
stats: z.object({
|
|
31
|
+
hits: z.number(),
|
|
32
|
+
misses: z.number(),
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* Default cache directory
|
|
37
|
+
*/
|
|
38
|
+
const DEFAULT_CACHE_DIR = '.anvil/cache';
|
|
39
|
+
/**
|
|
40
|
+
* Default TTL: 24 hours
|
|
41
|
+
*/
|
|
42
|
+
const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
43
|
+
/**
|
|
44
|
+
* File-based cache provider
|
|
45
|
+
*
|
|
46
|
+
* Storage structure:
|
|
47
|
+
* .anvil/cache/
|
|
48
|
+
* ├── index.json # Cache registry and stats
|
|
49
|
+
* └── entries/ # Cache entry files
|
|
50
|
+
* └── {key-hash}.json
|
|
51
|
+
*/
|
|
52
|
+
export class FileCacheProvider {
|
|
53
|
+
name = 'file';
|
|
54
|
+
cacheDir;
|
|
55
|
+
entriesDir;
|
|
56
|
+
indexPath;
|
|
57
|
+
defaultTtl;
|
|
58
|
+
maxSizeBytes;
|
|
59
|
+
hmacKey;
|
|
60
|
+
index = null;
|
|
61
|
+
indexDirty = false;
|
|
62
|
+
constructor(workspaceRoot, config = {}) {
|
|
63
|
+
if (config.useGlobalCache) {
|
|
64
|
+
this.cacheDir = join(homedir(), '.anvil', 'cache');
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.cacheDir = config.cacheDir || join(workspaceRoot, DEFAULT_CACHE_DIR);
|
|
68
|
+
}
|
|
69
|
+
this.entriesDir = join(this.cacheDir, 'entries');
|
|
70
|
+
this.indexPath = join(this.cacheDir, 'index.json');
|
|
71
|
+
this.defaultTtl = config.defaultTtl ?? DEFAULT_TTL_MS;
|
|
72
|
+
this.maxSizeBytes = config.maxSizeBytes ?? 100 * 1024 * 1024; // 100MB
|
|
73
|
+
// HMAC key: prefer env var, then persisted random key in cache dir
|
|
74
|
+
this.hmacKey = process.env['ANVIL_CACHE_HMAC_KEY'] ?? this.loadOrCreateHmacKey();
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Load a persisted HMAC key from the cache dir, or generate and store one.
|
|
78
|
+
* The key is stored with 0o600 permissions so only the current user can read it.
|
|
79
|
+
*/
|
|
80
|
+
loadOrCreateHmacKey() {
|
|
81
|
+
const keyPath = join(this.cacheDir, '.hmac-key');
|
|
82
|
+
try {
|
|
83
|
+
return readFileSync(keyPath, 'utf-8').trim();
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
const key = randomBytes(32).toString('hex');
|
|
87
|
+
mkdirSync(this.cacheDir, { recursive: true });
|
|
88
|
+
writeFileSync(keyPath, key, { mode: 0o600 });
|
|
89
|
+
return key;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async get(key) {
|
|
93
|
+
debug(`file-cache get: key=${key}`);
|
|
94
|
+
const index = await this.loadIndex();
|
|
95
|
+
const entryMeta = index.entries[key];
|
|
96
|
+
if (!entryMeta) {
|
|
97
|
+
debug(`file-cache miss: key=${key} (not in index)`);
|
|
98
|
+
index.stats.misses++;
|
|
99
|
+
this.indexDirty = true;
|
|
100
|
+
await this.saveIndex();
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
// Check expiration
|
|
104
|
+
if (entryMeta.expires_at && Date.now() > entryMeta.expires_at) {
|
|
105
|
+
debug(`file-cache miss: key=${key} (expired)`);
|
|
106
|
+
await this.invalidate(key);
|
|
107
|
+
index.stats.misses++;
|
|
108
|
+
this.indexDirty = true;
|
|
109
|
+
await this.saveIndex();
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
// Read entry file and verify HMAC integrity
|
|
113
|
+
const entryPath = join(this.entriesDir, entryMeta.file);
|
|
114
|
+
try {
|
|
115
|
+
const raw = await readFile(entryPath, 'utf-8');
|
|
116
|
+
const newlineIndex = raw.indexOf('\n');
|
|
117
|
+
const storedHmac = newlineIndex >= 0 ? raw.slice(0, newlineIndex) : '';
|
|
118
|
+
const content = newlineIndex >= 0 ? raw.slice(newlineIndex + 1) : raw;
|
|
119
|
+
// Verify HMAC integrity (mandatory — reject entries without valid HMAC)
|
|
120
|
+
if (!storedHmac || !/^[0-9a-f]{64}$/i.test(storedHmac)) {
|
|
121
|
+
debug('Cache entry missing HMAC, rejecting to prevent injection', { key });
|
|
122
|
+
await this.invalidate(key);
|
|
123
|
+
index.stats.misses++;
|
|
124
|
+
this.indexDirty = true;
|
|
125
|
+
await this.saveIndex();
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
const expectedHmac = this.computeHmac(content);
|
|
129
|
+
if (!timingSafeEqual(Buffer.from(storedHmac, 'hex'), Buffer.from(expectedHmac, 'hex'))) {
|
|
130
|
+
debug('Cache entry HMAC verification failed, possible tampering', { key });
|
|
131
|
+
await this.invalidate(key);
|
|
132
|
+
index.stats.misses++;
|
|
133
|
+
this.indexDirty = true;
|
|
134
|
+
await this.saveIndex();
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const parseResult = CacheEntrySchema.safeParse(JSON.parse(content));
|
|
138
|
+
if (!parseResult.success) {
|
|
139
|
+
debug('Invalid cache entry schema, removing from index', parseResult.error);
|
|
140
|
+
await this.invalidate(key);
|
|
141
|
+
index.stats.misses++;
|
|
142
|
+
this.indexDirty = true;
|
|
143
|
+
await this.saveIndex();
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const entry = parseResult.data;
|
|
147
|
+
debug(`file-cache hit: key=${key}`);
|
|
148
|
+
index.stats.hits++;
|
|
149
|
+
this.indexDirty = true;
|
|
150
|
+
await this.saveIndex();
|
|
151
|
+
return entry;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
debug('Cache entry file missing or corrupted, removing from index', error);
|
|
155
|
+
await this.invalidate(key);
|
|
156
|
+
index.stats.misses++;
|
|
157
|
+
this.indexDirty = true;
|
|
158
|
+
await this.saveIndex();
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async set(key, value, options) {
|
|
163
|
+
debug(`file-cache set: key=${key} ttl=${options.ttl ?? this.defaultTtl}`);
|
|
164
|
+
await this.ensureCacheDir();
|
|
165
|
+
const index = await this.loadIndex();
|
|
166
|
+
const now = Date.now();
|
|
167
|
+
const expiresAt = options.ttl ? now + options.ttl : now + this.defaultTtl;
|
|
168
|
+
const entry = {
|
|
169
|
+
value,
|
|
170
|
+
created_at: now,
|
|
171
|
+
expires_at: expiresAt,
|
|
172
|
+
key,
|
|
173
|
+
input_hash: options.input_hash,
|
|
174
|
+
};
|
|
175
|
+
// Generate filename from key hash
|
|
176
|
+
const fileHash = this.hashKey(key);
|
|
177
|
+
const fileName = `${fileHash}.json`;
|
|
178
|
+
const filePath = join(this.entriesDir, fileName);
|
|
179
|
+
// Write entry file atomically to prevent corruption in multi-agent scenarios
|
|
180
|
+
// Include HMAC for integrity verification
|
|
181
|
+
const content = JSON.stringify(entry, null, 2);
|
|
182
|
+
const hmac = this.computeHmac(content);
|
|
183
|
+
await atomicWriteText(filePath, `${hmac}\n${content}`);
|
|
184
|
+
// Update index
|
|
185
|
+
index.entries[key] = {
|
|
186
|
+
file: fileName,
|
|
187
|
+
created_at: now,
|
|
188
|
+
expires_at: expiresAt,
|
|
189
|
+
size_bytes: Buffer.byteLength(content, 'utf-8'),
|
|
190
|
+
};
|
|
191
|
+
this.indexDirty = true;
|
|
192
|
+
await this.saveIndex();
|
|
193
|
+
// Check if cache size exceeds limit
|
|
194
|
+
await this.maybeEvict();
|
|
195
|
+
}
|
|
196
|
+
async invalidate(key) {
|
|
197
|
+
debug(`file-cache invalidate: key=${key}`);
|
|
198
|
+
const index = await this.loadIndex();
|
|
199
|
+
const entryMeta = index.entries[key];
|
|
200
|
+
if (!entryMeta) {
|
|
201
|
+
debug(`file-cache invalidate: key=${key} not found`);
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
// Remove entry file
|
|
205
|
+
const filePath = join(this.entriesDir, entryMeta.file);
|
|
206
|
+
try {
|
|
207
|
+
unlinkSync(filePath);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
debug('Cache entry file already deleted or inaccessible', error);
|
|
211
|
+
}
|
|
212
|
+
// Remove from index
|
|
213
|
+
delete index.entries[key];
|
|
214
|
+
this.indexDirty = true;
|
|
215
|
+
await this.saveIndex();
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
async invalidatePattern(pattern) {
|
|
219
|
+
debug(`file-cache invalidatePattern: pattern=${pattern}`);
|
|
220
|
+
const index = await this.loadIndex();
|
|
221
|
+
const regex = this.patternToRegex(pattern);
|
|
222
|
+
const keysToInvalidate = Object.keys(index.entries).filter((key) => regex.test(key));
|
|
223
|
+
debug(`file-cache invalidatePattern: matched ${keysToInvalidate.length} entries`);
|
|
224
|
+
for (const key of keysToInvalidate) {
|
|
225
|
+
await this.invalidate(key);
|
|
226
|
+
}
|
|
227
|
+
return keysToInvalidate.length;
|
|
228
|
+
}
|
|
229
|
+
async getStats() {
|
|
230
|
+
const index = await this.loadIndex();
|
|
231
|
+
const entries = Object.keys(index.entries).length;
|
|
232
|
+
const sizeBytes = Object.values(index.entries).reduce((sum, e) => sum + e.size_bytes, 0);
|
|
233
|
+
const totalRequests = index.stats.hits + index.stats.misses;
|
|
234
|
+
const hitRate = totalRequests > 0 ? (index.stats.hits / totalRequests) * 100 : 0;
|
|
235
|
+
return {
|
|
236
|
+
hits: index.stats.hits,
|
|
237
|
+
misses: index.stats.misses,
|
|
238
|
+
entries,
|
|
239
|
+
size_bytes: sizeBytes,
|
|
240
|
+
hit_rate: Math.round(hitRate * 100) / 100,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
async clear() {
|
|
244
|
+
try {
|
|
245
|
+
await rm(this.cacheDir, { recursive: true, force: true });
|
|
246
|
+
this.index = null;
|
|
247
|
+
this.indexDirty = false;
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
debug('Failed to clear cache directory (may not exist)', error);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
async isAvailable() {
|
|
254
|
+
try {
|
|
255
|
+
await this.ensureCacheDir();
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
debug('Cache directory not available or not writable', error);
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Clean up expired entries
|
|
265
|
+
*/
|
|
266
|
+
async cleanup() {
|
|
267
|
+
debug('file-cache cleanup: scanning for expired entries');
|
|
268
|
+
const index = await this.loadIndex();
|
|
269
|
+
const now = Date.now();
|
|
270
|
+
const expiredKeys = Object.entries(index.entries)
|
|
271
|
+
.filter(([, meta]) => meta.expires_at && meta.expires_at < now)
|
|
272
|
+
.map(([key]) => key);
|
|
273
|
+
debug(`file-cache cleanup: found ${expiredKeys.length} expired entries`);
|
|
274
|
+
for (const key of expiredKeys) {
|
|
275
|
+
await this.invalidate(key);
|
|
276
|
+
}
|
|
277
|
+
return expiredKeys.length;
|
|
278
|
+
}
|
|
279
|
+
async ensureCacheDir() {
|
|
280
|
+
if (!existsSync(this.entriesDir)) {
|
|
281
|
+
await mkdir(this.entriesDir, { recursive: true });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
async loadIndex() {
|
|
285
|
+
if (this.index) {
|
|
286
|
+
return this.index;
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
const content = await readFile(this.indexPath, 'utf-8');
|
|
290
|
+
const parseResult = CacheIndexSchema.safeParse(JSON.parse(content));
|
|
291
|
+
if (parseResult.success) {
|
|
292
|
+
this.index = parseResult.data;
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
debug('Invalid cache index schema, creating new one', parseResult.error);
|
|
296
|
+
this.index = {
|
|
297
|
+
version: 1,
|
|
298
|
+
entries: {},
|
|
299
|
+
stats: { hits: 0, misses: 0 },
|
|
300
|
+
};
|
|
301
|
+
this.indexDirty = true;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
debug('Cache index missing or corrupted, creating new one', error);
|
|
306
|
+
this.index = {
|
|
307
|
+
version: 1,
|
|
308
|
+
entries: {},
|
|
309
|
+
stats: { hits: 0, misses: 0 },
|
|
310
|
+
};
|
|
311
|
+
this.indexDirty = true;
|
|
312
|
+
}
|
|
313
|
+
return this.index;
|
|
314
|
+
}
|
|
315
|
+
async saveIndex() {
|
|
316
|
+
if (!this.indexDirty || !this.index) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
await this.ensureCacheDir();
|
|
320
|
+
// Use atomic write to prevent corruption in multi-agent scenarios
|
|
321
|
+
await atomicWriteText(this.indexPath, JSON.stringify(this.index, null, 2));
|
|
322
|
+
this.indexDirty = false;
|
|
323
|
+
}
|
|
324
|
+
computeHmac(content) {
|
|
325
|
+
return createHmac('sha256', this.hmacKey).update(content).digest('hex');
|
|
326
|
+
}
|
|
327
|
+
hashKey(key) {
|
|
328
|
+
// Simple hash for filename safety
|
|
329
|
+
return createHash('sha256').update(key).digest('hex').slice(0, 32);
|
|
330
|
+
}
|
|
331
|
+
patternToRegex(pattern) {
|
|
332
|
+
const MAX_PATTERN_LENGTH = 200;
|
|
333
|
+
const MAX_WILDCARDS = 10;
|
|
334
|
+
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
335
|
+
throw new Error(`Cache pattern too long: ${pattern.length} > ${MAX_PATTERN_LENGTH}`);
|
|
336
|
+
}
|
|
337
|
+
const wildcardCount = (pattern.match(/\*/g) || []).length;
|
|
338
|
+
if (wildcardCount > MAX_WILDCARDS) {
|
|
339
|
+
throw new Error(`Too many wildcards in pattern: ${wildcardCount} > ${MAX_WILDCARDS}`);
|
|
340
|
+
}
|
|
341
|
+
const DOUBLE_STAR_PLACEHOLDER = '\x00DOUBLESTAR\x00';
|
|
342
|
+
const escaped = pattern
|
|
343
|
+
.replace(/\*\*/g, DOUBLE_STAR_PLACEHOLDER)
|
|
344
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
|
345
|
+
.replace(/\*/g, '[^:]*')
|
|
346
|
+
.replace(new RegExp(DOUBLE_STAR_PLACEHOLDER, 'g'), '[^:]*(?::[^:]*)*');
|
|
347
|
+
return new RegExp(`^${escaped}$`);
|
|
348
|
+
}
|
|
349
|
+
async maybeEvict() {
|
|
350
|
+
const stats = await this.getStats();
|
|
351
|
+
if (stats.size_bytes <= this.maxSizeBytes) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const index = await this.loadIndex();
|
|
355
|
+
// Sort entries by creation time (oldest first)
|
|
356
|
+
const sortedEntries = Object.entries(index.entries).sort(([, a], [, b]) => a.created_at - b.created_at);
|
|
357
|
+
// Evict oldest entries until under limit
|
|
358
|
+
let currentSize = stats.size_bytes;
|
|
359
|
+
for (const [key] of sortedEntries) {
|
|
360
|
+
if (currentSize <= this.maxSizeBytes * 0.8) {
|
|
361
|
+
// Evict until 80% of limit
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
const meta = index.entries[key];
|
|
365
|
+
currentSize -= meta.size_bytes;
|
|
366
|
+
await this.invalidate(key);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory cache provider
|
|
3
|
+
* Used for watch mode and short-lived sessions
|
|
4
|
+
*/
|
|
5
|
+
import type { CacheProvider, CacheEntry, CacheSetOptions, CacheStats } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Memory cache configuration
|
|
8
|
+
*/
|
|
9
|
+
export interface MemoryCacheConfig {
|
|
10
|
+
/** Maximum number of entries (default: 1000) */
|
|
11
|
+
maxEntries?: number;
|
|
12
|
+
/** Default TTL in milliseconds (default: 5 minutes for watch mode) */
|
|
13
|
+
defaultTtl?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* In-memory cache provider
|
|
17
|
+
*
|
|
18
|
+
* Features:
|
|
19
|
+
* - Fast access for watch mode
|
|
20
|
+
* - LRU eviction when max entries exceeded
|
|
21
|
+
* - Automatic expiration cleanup
|
|
22
|
+
*/
|
|
23
|
+
export declare class MemoryCacheProvider implements CacheProvider {
|
|
24
|
+
readonly name = "memory";
|
|
25
|
+
private readonly cache;
|
|
26
|
+
private readonly accessOrder;
|
|
27
|
+
private readonly maxEntries;
|
|
28
|
+
private readonly defaultTtl;
|
|
29
|
+
private stats;
|
|
30
|
+
constructor(config?: MemoryCacheConfig);
|
|
31
|
+
get<T>(key: string): Promise<CacheEntry<T> | null>;
|
|
32
|
+
set<T>(key: string, value: T, options: CacheSetOptions): Promise<void>;
|
|
33
|
+
invalidate(key: string): Promise<boolean>;
|
|
34
|
+
invalidatePattern(pattern: string): Promise<number>;
|
|
35
|
+
getStats(): Promise<CacheStats>;
|
|
36
|
+
clear(): Promise<void>;
|
|
37
|
+
isAvailable(): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Clean up expired entries
|
|
40
|
+
*/
|
|
41
|
+
cleanup(): Promise<number>;
|
|
42
|
+
/**
|
|
43
|
+
* Get current entry count
|
|
44
|
+
*/
|
|
45
|
+
get size(): number;
|
|
46
|
+
private updateAccessOrder;
|
|
47
|
+
private removeFromAccessOrder;
|
|
48
|
+
private evictLRU;
|
|
49
|
+
private patternToRegex;
|
|
50
|
+
private estimateSize;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=memory-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-cache.d.ts","sourceRoot":"","sources":["../../../src/cache/providers/memory-cache.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK1F;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,qBAAa,mBAAoB,YAAW,aAAa;IACvD,QAAQ,CAAC,IAAI,YAAY;IAEzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0C;IAChE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgB;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,OAAO,CAAC,KAAK,CAGX;gBAEU,MAAM,GAAE,iBAAsB;IAKpC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IA0BlD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBtE,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQzC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmBnD,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC;IAqB/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAIrC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;IAmBhC;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,QAAQ;IAsBhB,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,YAAY;CAQrB"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory cache provider
|
|
3
|
+
* Used for watch mode and short-lived sessions
|
|
4
|
+
*/
|
|
5
|
+
import { createDebugger } from '@eddacraft/anvil-core';
|
|
6
|
+
const debug = createDebugger('cache');
|
|
7
|
+
/**
|
|
8
|
+
* In-memory cache provider
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Fast access for watch mode
|
|
12
|
+
* - LRU eviction when max entries exceeded
|
|
13
|
+
* - Automatic expiration cleanup
|
|
14
|
+
*/
|
|
15
|
+
export class MemoryCacheProvider {
|
|
16
|
+
name = 'memory';
|
|
17
|
+
cache = new Map();
|
|
18
|
+
accessOrder = [];
|
|
19
|
+
maxEntries;
|
|
20
|
+
defaultTtl;
|
|
21
|
+
stats = {
|
|
22
|
+
hits: 0,
|
|
23
|
+
misses: 0,
|
|
24
|
+
};
|
|
25
|
+
constructor(config = {}) {
|
|
26
|
+
this.maxEntries = config.maxEntries ?? 1000;
|
|
27
|
+
this.defaultTtl = config.defaultTtl ?? 5 * 60 * 1000; // 5 minutes
|
|
28
|
+
}
|
|
29
|
+
async get(key) {
|
|
30
|
+
debug(`memory-cache get: key=${key} size=${this.cache.size}`);
|
|
31
|
+
const entry = this.cache.get(key);
|
|
32
|
+
if (!entry) {
|
|
33
|
+
debug(`memory-cache miss: key=${key} (not found)`);
|
|
34
|
+
this.stats.misses++;
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
// Check expiration
|
|
38
|
+
if (entry.expires_at && Date.now() > entry.expires_at) {
|
|
39
|
+
debug(`memory-cache miss: key=${key} (expired)`);
|
|
40
|
+
await this.invalidate(key);
|
|
41
|
+
this.stats.misses++;
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
// Update access order (move to end)
|
|
45
|
+
this.updateAccessOrder(key);
|
|
46
|
+
debug(`memory-cache hit: key=${key}`);
|
|
47
|
+
this.stats.hits++;
|
|
48
|
+
return entry;
|
|
49
|
+
}
|
|
50
|
+
async set(key, value, options) {
|
|
51
|
+
debug(`memory-cache set: key=${key} size=${this.cache.size}/${this.maxEntries}`);
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
const expiresAt = options.ttl ? now + options.ttl : now + this.defaultTtl;
|
|
54
|
+
const entry = {
|
|
55
|
+
value,
|
|
56
|
+
created_at: now,
|
|
57
|
+
expires_at: expiresAt,
|
|
58
|
+
key,
|
|
59
|
+
input_hash: options.input_hash,
|
|
60
|
+
};
|
|
61
|
+
// Check if we need to evict
|
|
62
|
+
if (!this.cache.has(key) && this.cache.size >= this.maxEntries) {
|
|
63
|
+
debug(`memory-cache set: evicting LRU (at capacity ${this.maxEntries})`);
|
|
64
|
+
this.evictLRU();
|
|
65
|
+
}
|
|
66
|
+
this.cache.set(key, entry);
|
|
67
|
+
this.updateAccessOrder(key);
|
|
68
|
+
}
|
|
69
|
+
async invalidate(key) {
|
|
70
|
+
debug(`memory-cache invalidate: key=${key}`);
|
|
71
|
+
const existed = this.cache.has(key);
|
|
72
|
+
this.cache.delete(key);
|
|
73
|
+
this.removeFromAccessOrder(key);
|
|
74
|
+
return existed;
|
|
75
|
+
}
|
|
76
|
+
async invalidatePattern(pattern) {
|
|
77
|
+
debug(`memory-cache invalidatePattern: pattern=${pattern}`);
|
|
78
|
+
const regex = this.patternToRegex(pattern);
|
|
79
|
+
const keysToDelete = [];
|
|
80
|
+
for (const key of this.cache.keys()) {
|
|
81
|
+
if (regex.test(key)) {
|
|
82
|
+
keysToDelete.push(key);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
debug(`memory-cache invalidatePattern: matched ${keysToDelete.length} entries`);
|
|
86
|
+
for (const key of keysToDelete) {
|
|
87
|
+
await this.invalidate(key);
|
|
88
|
+
}
|
|
89
|
+
return keysToDelete.length;
|
|
90
|
+
}
|
|
91
|
+
async getStats() {
|
|
92
|
+
const entries = this.cache.size;
|
|
93
|
+
let sizeBytes = 0;
|
|
94
|
+
// Estimate size (rough approximation)
|
|
95
|
+
for (const entry of this.cache.values()) {
|
|
96
|
+
sizeBytes += this.estimateSize(entry);
|
|
97
|
+
}
|
|
98
|
+
const totalRequests = this.stats.hits + this.stats.misses;
|
|
99
|
+
const hitRate = totalRequests > 0 ? (this.stats.hits / totalRequests) * 100 : 0;
|
|
100
|
+
return {
|
|
101
|
+
hits: this.stats.hits,
|
|
102
|
+
misses: this.stats.misses,
|
|
103
|
+
entries,
|
|
104
|
+
size_bytes: sizeBytes,
|
|
105
|
+
hit_rate: Math.round(hitRate * 100) / 100,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async clear() {
|
|
109
|
+
this.cache.clear();
|
|
110
|
+
this.accessOrder.length = 0;
|
|
111
|
+
this.stats = { hits: 0, misses: 0 };
|
|
112
|
+
}
|
|
113
|
+
async isAvailable() {
|
|
114
|
+
return true; // Memory cache is always available
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Clean up expired entries
|
|
118
|
+
*/
|
|
119
|
+
async cleanup() {
|
|
120
|
+
debug(`memory-cache cleanup: scanning ${this.cache.size} entries`);
|
|
121
|
+
const now = Date.now();
|
|
122
|
+
const expiredKeys = [];
|
|
123
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
124
|
+
if (entry.expires_at && entry.expires_at < now) {
|
|
125
|
+
expiredKeys.push(key);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
debug(`memory-cache cleanup: removing ${expiredKeys.length} expired entries`);
|
|
129
|
+
for (const key of expiredKeys) {
|
|
130
|
+
await this.invalidate(key);
|
|
131
|
+
}
|
|
132
|
+
return expiredKeys.length;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get current entry count
|
|
136
|
+
*/
|
|
137
|
+
get size() {
|
|
138
|
+
return this.cache.size;
|
|
139
|
+
}
|
|
140
|
+
updateAccessOrder(key) {
|
|
141
|
+
this.removeFromAccessOrder(key);
|
|
142
|
+
this.accessOrder.push(key);
|
|
143
|
+
}
|
|
144
|
+
removeFromAccessOrder(key) {
|
|
145
|
+
const index = this.accessOrder.indexOf(key);
|
|
146
|
+
if (index !== -1) {
|
|
147
|
+
this.accessOrder.splice(index, 1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
evictLRU() {
|
|
151
|
+
// Clean up expired entries first
|
|
152
|
+
const now = Date.now();
|
|
153
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
154
|
+
if (entry.expires_at && entry.expires_at < now) {
|
|
155
|
+
this.cache.delete(key);
|
|
156
|
+
this.removeFromAccessOrder(key);
|
|
157
|
+
if (this.cache.size < this.maxEntries) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// If still over limit, evict least recently used
|
|
163
|
+
while (this.cache.size >= this.maxEntries && this.accessOrder.length > 0) {
|
|
164
|
+
const lruKey = this.accessOrder.shift();
|
|
165
|
+
if (lruKey) {
|
|
166
|
+
this.cache.delete(lruKey);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
patternToRegex(pattern) {
|
|
171
|
+
const MAX_PATTERN_LENGTH = 200;
|
|
172
|
+
const MAX_WILDCARDS = 10;
|
|
173
|
+
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
174
|
+
throw new Error(`Cache pattern too long: ${pattern.length} > ${MAX_PATTERN_LENGTH}`);
|
|
175
|
+
}
|
|
176
|
+
const wildcardCount = (pattern.match(/\*/g) || []).length;
|
|
177
|
+
if (wildcardCount > MAX_WILDCARDS) {
|
|
178
|
+
throw new Error(`Too many wildcards in pattern: ${wildcardCount} > ${MAX_WILDCARDS}`);
|
|
179
|
+
}
|
|
180
|
+
const DOUBLE_STAR_PLACEHOLDER = '\x00DOUBLESTAR\x00';
|
|
181
|
+
const escaped = pattern
|
|
182
|
+
.replace(/\*\*/g, DOUBLE_STAR_PLACEHOLDER)
|
|
183
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
|
184
|
+
.replace(/\*/g, '[^:]*')
|
|
185
|
+
.replace(new RegExp(DOUBLE_STAR_PLACEHOLDER, 'g'), '[^:]*(?::[^:]*)*');
|
|
186
|
+
return new RegExp(`^${escaped}$`);
|
|
187
|
+
}
|
|
188
|
+
estimateSize(entry) {
|
|
189
|
+
// Rough estimation of object size in memory
|
|
190
|
+
try {
|
|
191
|
+
return JSON.stringify(entry).length * 2; // UTF-16 chars
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return 1024; // Default estimate for non-serialisable objects
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Null cache provider (no-op)
|
|
3
|
+
* Used when caching is disabled via --no-cache flag
|
|
4
|
+
*/
|
|
5
|
+
import type { CacheProvider, CacheEntry, CacheSetOptions, CacheStats } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Null cache provider - disables caching
|
|
8
|
+
*
|
|
9
|
+
* All operations are no-ops that return appropriate null/empty values.
|
|
10
|
+
* Used when:
|
|
11
|
+
* - --no-cache flag is passed
|
|
12
|
+
* - Caching is disabled in configuration
|
|
13
|
+
* - Testing scenarios where caching should be bypassed
|
|
14
|
+
*/
|
|
15
|
+
export declare class NullCacheProvider implements CacheProvider {
|
|
16
|
+
readonly name = "null";
|
|
17
|
+
private stats;
|
|
18
|
+
get<T>(_key: string): Promise<CacheEntry<T> | null>;
|
|
19
|
+
set<T>(_key: string, _value: T, _options: CacheSetOptions): Promise<void>;
|
|
20
|
+
invalidate(_key: string): Promise<boolean>;
|
|
21
|
+
invalidatePattern(_pattern: string): Promise<number>;
|
|
22
|
+
getStats(): Promise<CacheStats>;
|
|
23
|
+
clear(): Promise<void>;
|
|
24
|
+
isAvailable(): Promise<boolean>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=null-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"null-cache.d.ts","sourceRoot":"","sources":["../../../src/cache/providers/null-cache.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK1F;;;;;;;;GAQG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IACrD,QAAQ,CAAC,IAAI,UAAU;IAEvB,OAAO,CAAC,KAAK,CAEX;IAEI,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAMnD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzE,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1C,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIpD,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC;IAU/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAGtC"}
|