@push.rocks/smartregistry 2.2.3 → 2.4.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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/cargo/classes.cargoregistry.d.ts +7 -1
- package/dist_ts/cargo/classes.cargoregistry.js +42 -4
- package/dist_ts/cargo/classes.cargoupstream.d.ts +44 -0
- package/dist_ts/cargo/classes.cargoupstream.js +129 -0
- package/dist_ts/cargo/index.d.ts +1 -0
- package/dist_ts/cargo/index.js +2 -1
- package/dist_ts/classes.smartregistry.d.ts +33 -2
- package/dist_ts/classes.smartregistry.js +45 -12
- package/dist_ts/composer/classes.composerregistry.d.ts +7 -1
- package/dist_ts/composer/classes.composerregistry.js +34 -3
- package/dist_ts/composer/classes.composerupstream.d.ts +40 -0
- package/dist_ts/composer/classes.composerupstream.js +159 -0
- package/dist_ts/composer/index.d.ts +1 -0
- package/dist_ts/composer/index.js +2 -1
- package/dist_ts/core/classes.authmanager.d.ts +30 -80
- package/dist_ts/core/classes.authmanager.js +63 -337
- package/dist_ts/core/classes.defaultauthprovider.d.ts +78 -0
- package/dist_ts/core/classes.defaultauthprovider.js +311 -0
- package/dist_ts/core/classes.registrystorage.d.ts +70 -4
- package/dist_ts/core/classes.registrystorage.js +165 -5
- package/dist_ts/core/index.d.ts +3 -0
- package/dist_ts/core/index.js +7 -2
- package/dist_ts/core/interfaces.auth.d.ts +83 -0
- package/dist_ts/core/interfaces.auth.js +2 -0
- package/dist_ts/core/interfaces.core.d.ts +38 -0
- package/dist_ts/core/interfaces.storage.d.ts +120 -0
- package/dist_ts/core/interfaces.storage.js +2 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +3 -1
- package/dist_ts/maven/classes.mavenregistry.d.ts +12 -1
- package/dist_ts/maven/classes.mavenregistry.js +69 -4
- package/dist_ts/maven/classes.mavenupstream.d.ts +45 -0
- package/dist_ts/maven/classes.mavenupstream.js +153 -0
- package/dist_ts/maven/index.d.ts +1 -0
- package/dist_ts/maven/index.js +2 -1
- package/dist_ts/npm/classes.npmregistry.d.ts +3 -1
- package/dist_ts/npm/classes.npmregistry.js +55 -6
- package/dist_ts/npm/classes.npmupstream.d.ts +51 -0
- package/dist_ts/npm/classes.npmupstream.js +206 -0
- package/dist_ts/npm/index.d.ts +1 -0
- package/dist_ts/npm/index.js +2 -1
- package/dist_ts/oci/classes.ociregistry.d.ts +4 -1
- package/dist_ts/oci/classes.ociregistry.js +78 -17
- package/dist_ts/oci/classes.ociupstream.d.ts +62 -0
- package/dist_ts/oci/classes.ociupstream.js +206 -0
- package/dist_ts/oci/index.d.ts +1 -0
- package/dist_ts/oci/index.js +2 -1
- package/dist_ts/plugins.d.ts +4 -1
- package/dist_ts/plugins.js +6 -2
- package/dist_ts/pypi/classes.pypiregistry.d.ts +7 -1
- package/dist_ts/pypi/classes.pypiregistry.js +60 -4
- package/dist_ts/pypi/classes.pypiupstream.d.ts +48 -0
- package/dist_ts/pypi/classes.pypiupstream.js +165 -0
- package/dist_ts/pypi/index.d.ts +1 -0
- package/dist_ts/pypi/index.js +2 -1
- package/dist_ts/rubygems/classes.rubygemsregistry.d.ts +7 -1
- package/dist_ts/rubygems/classes.rubygemsregistry.js +35 -4
- package/dist_ts/rubygems/classes.rubygemsupstream.d.ts +47 -0
- package/dist_ts/rubygems/classes.rubygemsupstream.js +184 -0
- package/dist_ts/rubygems/index.d.ts +1 -0
- package/dist_ts/rubygems/index.js +2 -1
- package/dist_ts/upstream/classes.baseupstream.d.ts +112 -0
- package/dist_ts/upstream/classes.baseupstream.js +411 -0
- package/dist_ts/upstream/classes.circuitbreaker.d.ts +111 -0
- package/dist_ts/upstream/classes.circuitbreaker.js +192 -0
- package/dist_ts/upstream/classes.upstreamcache.d.ts +170 -0
- package/dist_ts/upstream/classes.upstreamcache.js +485 -0
- package/dist_ts/upstream/index.d.ts +6 -0
- package/dist_ts/upstream/index.js +7 -0
- package/dist_ts/upstream/interfaces.upstream.d.ts +169 -0
- package/dist_ts/upstream/interfaces.upstream.js +23 -0
- package/package.json +4 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/cargo/classes.cargoregistry.ts +48 -3
- package/ts/cargo/classes.cargoupstream.ts +159 -0
- package/ts/cargo/index.ts +1 -0
- package/ts/classes.smartregistry.ts +88 -11
- package/ts/composer/classes.composerregistry.ts +39 -2
- package/ts/composer/classes.composerupstream.ts +200 -0
- package/ts/composer/index.ts +1 -0
- package/ts/core/classes.authmanager.ts +74 -412
- package/ts/core/classes.defaultauthprovider.ts +393 -0
- package/ts/core/classes.registrystorage.ts +199 -5
- package/ts/core/index.ts +8 -1
- package/ts/core/interfaces.auth.ts +91 -0
- package/ts/core/interfaces.core.ts +42 -0
- package/ts/core/interfaces.storage.ts +130 -0
- package/ts/index.ts +3 -0
- package/ts/maven/classes.mavenregistry.ts +84 -3
- package/ts/maven/classes.mavenupstream.ts +220 -0
- package/ts/maven/index.ts +1 -0
- package/ts/npm/classes.npmregistry.ts +61 -5
- package/ts/npm/classes.npmupstream.ts +260 -0
- package/ts/npm/index.ts +1 -0
- package/ts/oci/classes.ociregistry.ts +89 -17
- package/ts/oci/classes.ociupstream.ts +263 -0
- package/ts/oci/index.ts +1 -0
- package/ts/plugins.ts +7 -1
- package/ts/pypi/classes.pypiregistry.ts +68 -3
- package/ts/pypi/classes.pypiupstream.ts +211 -0
- package/ts/pypi/index.ts +1 -0
- package/ts/rubygems/classes.rubygemsregistry.ts +40 -3
- package/ts/rubygems/classes.rubygemsupstream.ts +230 -0
- package/ts/rubygems/index.ts +1 -0
- package/ts/upstream/classes.baseupstream.ts +526 -0
- package/ts/upstream/classes.circuitbreaker.ts +238 -0
- package/ts/upstream/classes.upstreamcache.ts +626 -0
- package/ts/upstream/index.ts +11 -0
- package/ts/upstream/interfaces.upstream.ts +195 -0
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
import { DEFAULT_CACHE_CONFIG } from './interfaces.upstream.js';
|
|
2
|
+
/**
|
|
3
|
+
* S3-backed upstream cache with in-memory hot layer.
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - TTL-based expiration
|
|
7
|
+
* - Stale-while-revalidate support
|
|
8
|
+
* - Negative caching (404s)
|
|
9
|
+
* - Content-type aware caching
|
|
10
|
+
* - ETag support for conditional requests
|
|
11
|
+
* - Multi-upstream support via URL-based cache paths
|
|
12
|
+
* - Persistent S3 storage with in-memory hot layer
|
|
13
|
+
*
|
|
14
|
+
* Cache paths are structured as:
|
|
15
|
+
* cache/{escaped-upstream-url}/{protocol}:{method}:{path}
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // In-memory only (default)
|
|
20
|
+
* const cache = new UpstreamCache(config);
|
|
21
|
+
*
|
|
22
|
+
* // With S3 persistence
|
|
23
|
+
* const cache = new UpstreamCache(config, 10000, storage);
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export class UpstreamCache {
|
|
27
|
+
/** In-memory hot cache */
|
|
28
|
+
memoryCache = new Map();
|
|
29
|
+
/** Configuration */
|
|
30
|
+
config;
|
|
31
|
+
/** Maximum in-memory cache entries */
|
|
32
|
+
maxMemoryEntries;
|
|
33
|
+
/** S3 storage backend (optional) */
|
|
34
|
+
storage;
|
|
35
|
+
/** Cleanup interval handle */
|
|
36
|
+
cleanupInterval = null;
|
|
37
|
+
constructor(config, maxMemoryEntries = 10000, storage) {
|
|
38
|
+
this.config = { ...DEFAULT_CACHE_CONFIG, ...config };
|
|
39
|
+
this.maxMemoryEntries = maxMemoryEntries;
|
|
40
|
+
this.storage = storage;
|
|
41
|
+
// Start periodic cleanup if caching is enabled
|
|
42
|
+
if (this.config.enabled) {
|
|
43
|
+
this.startCleanup();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if caching is enabled.
|
|
48
|
+
*/
|
|
49
|
+
isEnabled() {
|
|
50
|
+
return this.config.enabled;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if S3 storage is configured.
|
|
54
|
+
*/
|
|
55
|
+
hasStorage() {
|
|
56
|
+
return !!this.storage;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get cached entry for a request context.
|
|
60
|
+
* Checks memory first, then falls back to S3.
|
|
61
|
+
* Returns null if not found or expired (unless stale-while-revalidate).
|
|
62
|
+
*/
|
|
63
|
+
async get(context, upstreamUrl) {
|
|
64
|
+
if (!this.config.enabled) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const key = this.buildCacheKey(context, upstreamUrl);
|
|
68
|
+
// Check memory cache first
|
|
69
|
+
let entry = this.memoryCache.get(key);
|
|
70
|
+
// If not in memory and we have storage, check S3
|
|
71
|
+
if (!entry && this.storage) {
|
|
72
|
+
entry = await this.loadFromStorage(key);
|
|
73
|
+
if (entry) {
|
|
74
|
+
// Promote to memory cache
|
|
75
|
+
this.memoryCache.set(key, entry);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!entry) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const now = new Date();
|
|
82
|
+
// Check if entry is expired
|
|
83
|
+
if (entry.expiresAt && entry.expiresAt < now) {
|
|
84
|
+
// Check if we can serve stale content
|
|
85
|
+
if (this.config.staleWhileRevalidate && !entry.stale) {
|
|
86
|
+
const staleAge = (now.getTime() - entry.expiresAt.getTime()) / 1000;
|
|
87
|
+
if (staleAge <= this.config.staleMaxAgeSeconds) {
|
|
88
|
+
// Mark as stale and return
|
|
89
|
+
entry.stale = true;
|
|
90
|
+
return entry;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Entry is too old, remove it
|
|
94
|
+
this.memoryCache.delete(key);
|
|
95
|
+
if (this.storage) {
|
|
96
|
+
await this.deleteFromStorage(key).catch(() => { });
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
return entry;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Store a response in the cache (memory and optionally S3).
|
|
104
|
+
*/
|
|
105
|
+
async set(context, data, contentType, headers, upstreamId, upstreamUrl, options) {
|
|
106
|
+
if (!this.config.enabled) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// Enforce max memory entries limit
|
|
110
|
+
if (this.memoryCache.size >= this.maxMemoryEntries) {
|
|
111
|
+
this.evictOldest();
|
|
112
|
+
}
|
|
113
|
+
const key = this.buildCacheKey(context, upstreamUrl);
|
|
114
|
+
const now = new Date();
|
|
115
|
+
// Determine TTL based on content type
|
|
116
|
+
const ttlSeconds = options?.ttlSeconds ?? this.determineTtl(context, contentType, headers);
|
|
117
|
+
const entry = {
|
|
118
|
+
data,
|
|
119
|
+
contentType,
|
|
120
|
+
headers,
|
|
121
|
+
cachedAt: now,
|
|
122
|
+
expiresAt: ttlSeconds > 0 ? new Date(now.getTime() + ttlSeconds * 1000) : undefined,
|
|
123
|
+
etag: headers['etag'] || options?.etag,
|
|
124
|
+
upstreamId,
|
|
125
|
+
stale: false,
|
|
126
|
+
};
|
|
127
|
+
// Store in memory
|
|
128
|
+
this.memoryCache.set(key, entry);
|
|
129
|
+
// Store in S3 if available
|
|
130
|
+
if (this.storage) {
|
|
131
|
+
await this.saveToStorage(key, entry, upstreamUrl).catch(() => { });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Store a negative cache entry (404 response).
|
|
136
|
+
*/
|
|
137
|
+
async setNegative(context, upstreamId, upstreamUrl) {
|
|
138
|
+
if (!this.config.enabled || this.config.negativeCacheTtlSeconds <= 0) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const key = this.buildCacheKey(context, upstreamUrl);
|
|
142
|
+
const now = new Date();
|
|
143
|
+
const entry = {
|
|
144
|
+
data: Buffer.from(''),
|
|
145
|
+
contentType: 'application/octet-stream',
|
|
146
|
+
headers: {},
|
|
147
|
+
cachedAt: now,
|
|
148
|
+
expiresAt: new Date(now.getTime() + this.config.negativeCacheTtlSeconds * 1000),
|
|
149
|
+
upstreamId,
|
|
150
|
+
stale: false,
|
|
151
|
+
};
|
|
152
|
+
this.memoryCache.set(key, entry);
|
|
153
|
+
if (this.storage) {
|
|
154
|
+
await this.saveToStorage(key, entry, upstreamUrl).catch(() => { });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Check if there's a negative cache entry for this context.
|
|
159
|
+
*/
|
|
160
|
+
async hasNegative(context, upstreamUrl) {
|
|
161
|
+
const entry = await this.get(context, upstreamUrl);
|
|
162
|
+
return entry !== null && entry.data.length === 0;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Invalidate a specific cache entry.
|
|
166
|
+
*/
|
|
167
|
+
async invalidate(context, upstreamUrl) {
|
|
168
|
+
const key = this.buildCacheKey(context, upstreamUrl);
|
|
169
|
+
const deleted = this.memoryCache.delete(key);
|
|
170
|
+
if (this.storage) {
|
|
171
|
+
await this.deleteFromStorage(key).catch(() => { });
|
|
172
|
+
}
|
|
173
|
+
return deleted;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Invalidate all entries matching a pattern.
|
|
177
|
+
* Useful for invalidating all versions of a package.
|
|
178
|
+
*/
|
|
179
|
+
async invalidatePattern(pattern) {
|
|
180
|
+
let count = 0;
|
|
181
|
+
for (const key of this.memoryCache.keys()) {
|
|
182
|
+
if (pattern.test(key)) {
|
|
183
|
+
this.memoryCache.delete(key);
|
|
184
|
+
if (this.storage) {
|
|
185
|
+
await this.deleteFromStorage(key).catch(() => { });
|
|
186
|
+
}
|
|
187
|
+
count++;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return count;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Invalidate all entries from a specific upstream.
|
|
194
|
+
*/
|
|
195
|
+
async invalidateUpstream(upstreamId) {
|
|
196
|
+
let count = 0;
|
|
197
|
+
for (const [key, entry] of this.memoryCache.entries()) {
|
|
198
|
+
if (entry.upstreamId === upstreamId) {
|
|
199
|
+
this.memoryCache.delete(key);
|
|
200
|
+
if (this.storage) {
|
|
201
|
+
await this.deleteFromStorage(key).catch(() => { });
|
|
202
|
+
}
|
|
203
|
+
count++;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return count;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Clear all cache entries (memory and S3).
|
|
210
|
+
*/
|
|
211
|
+
async clear() {
|
|
212
|
+
this.memoryCache.clear();
|
|
213
|
+
// Note: S3 cleanup would require listing and deleting all cache/* objects
|
|
214
|
+
// This is left as a future enhancement for bulk cleanup
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Get cache statistics.
|
|
218
|
+
*/
|
|
219
|
+
getStats() {
|
|
220
|
+
let freshCount = 0;
|
|
221
|
+
let staleCount = 0;
|
|
222
|
+
let negativeCount = 0;
|
|
223
|
+
let totalSize = 0;
|
|
224
|
+
const now = new Date();
|
|
225
|
+
for (const entry of this.memoryCache.values()) {
|
|
226
|
+
totalSize += entry.data.length;
|
|
227
|
+
if (entry.data.length === 0) {
|
|
228
|
+
negativeCount++;
|
|
229
|
+
}
|
|
230
|
+
else if (entry.stale || (entry.expiresAt && entry.expiresAt < now)) {
|
|
231
|
+
staleCount++;
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
freshCount++;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
totalEntries: this.memoryCache.size,
|
|
239
|
+
freshEntries: freshCount,
|
|
240
|
+
staleEntries: staleCount,
|
|
241
|
+
negativeEntries: negativeCount,
|
|
242
|
+
totalSizeBytes: totalSize,
|
|
243
|
+
maxEntries: this.maxMemoryEntries,
|
|
244
|
+
enabled: this.config.enabled,
|
|
245
|
+
hasStorage: !!this.storage,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Stop the cache and cleanup.
|
|
250
|
+
*/
|
|
251
|
+
stop() {
|
|
252
|
+
if (this.cleanupInterval) {
|
|
253
|
+
clearInterval(this.cleanupInterval);
|
|
254
|
+
this.cleanupInterval = null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// ========================================================================
|
|
258
|
+
// Storage Methods
|
|
259
|
+
// ========================================================================
|
|
260
|
+
/**
|
|
261
|
+
* Build storage path for a cache key.
|
|
262
|
+
* Escapes upstream URL for safe use in S3 paths.
|
|
263
|
+
*/
|
|
264
|
+
buildStoragePath(key) {
|
|
265
|
+
return `cache/${key}`;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Build storage path for cache metadata.
|
|
269
|
+
*/
|
|
270
|
+
buildMetadataPath(key) {
|
|
271
|
+
return `cache/${key}.meta`;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Load a cache entry from S3 storage.
|
|
275
|
+
*/
|
|
276
|
+
async loadFromStorage(key) {
|
|
277
|
+
if (!this.storage)
|
|
278
|
+
return null;
|
|
279
|
+
try {
|
|
280
|
+
const dataPath = this.buildStoragePath(key);
|
|
281
|
+
const metaPath = this.buildMetadataPath(key);
|
|
282
|
+
// Load data and metadata in parallel
|
|
283
|
+
const [data, metaBuffer] = await Promise.all([
|
|
284
|
+
this.storage.getObject(dataPath),
|
|
285
|
+
this.storage.getObject(metaPath),
|
|
286
|
+
]);
|
|
287
|
+
if (!data || !metaBuffer) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
const meta = JSON.parse(metaBuffer.toString('utf-8'));
|
|
291
|
+
return {
|
|
292
|
+
data,
|
|
293
|
+
contentType: meta.contentType,
|
|
294
|
+
headers: meta.headers,
|
|
295
|
+
cachedAt: new Date(meta.cachedAt),
|
|
296
|
+
expiresAt: meta.expiresAt ? new Date(meta.expiresAt) : undefined,
|
|
297
|
+
etag: meta.etag,
|
|
298
|
+
upstreamId: meta.upstreamId,
|
|
299
|
+
stale: false,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Save a cache entry to S3 storage.
|
|
308
|
+
*/
|
|
309
|
+
async saveToStorage(key, entry, upstreamUrl) {
|
|
310
|
+
if (!this.storage)
|
|
311
|
+
return;
|
|
312
|
+
const dataPath = this.buildStoragePath(key);
|
|
313
|
+
const metaPath = this.buildMetadataPath(key);
|
|
314
|
+
const meta = {
|
|
315
|
+
contentType: entry.contentType,
|
|
316
|
+
headers: entry.headers,
|
|
317
|
+
cachedAt: entry.cachedAt.toISOString(),
|
|
318
|
+
expiresAt: entry.expiresAt?.toISOString(),
|
|
319
|
+
etag: entry.etag,
|
|
320
|
+
upstreamId: entry.upstreamId,
|
|
321
|
+
upstreamUrl,
|
|
322
|
+
};
|
|
323
|
+
// Save data and metadata in parallel
|
|
324
|
+
await Promise.all([
|
|
325
|
+
this.storage.putObject(dataPath, entry.data),
|
|
326
|
+
this.storage.putObject(metaPath, Buffer.from(JSON.stringify(meta), 'utf-8')),
|
|
327
|
+
]);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Delete a cache entry from S3 storage.
|
|
331
|
+
*/
|
|
332
|
+
async deleteFromStorage(key) {
|
|
333
|
+
if (!this.storage)
|
|
334
|
+
return;
|
|
335
|
+
const dataPath = this.buildStoragePath(key);
|
|
336
|
+
const metaPath = this.buildMetadataPath(key);
|
|
337
|
+
await Promise.all([
|
|
338
|
+
this.storage.deleteObject(dataPath).catch(() => { }),
|
|
339
|
+
this.storage.deleteObject(metaPath).catch(() => { }),
|
|
340
|
+
]);
|
|
341
|
+
}
|
|
342
|
+
// ========================================================================
|
|
343
|
+
// Helper Methods
|
|
344
|
+
// ========================================================================
|
|
345
|
+
/**
|
|
346
|
+
* Escape a URL for safe use in storage paths.
|
|
347
|
+
*/
|
|
348
|
+
escapeUrl(url) {
|
|
349
|
+
// Remove protocol prefix and escape special characters
|
|
350
|
+
return url
|
|
351
|
+
.replace(/^https?:\/\//, '')
|
|
352
|
+
.replace(/[\/\\:*?"<>|]/g, '_')
|
|
353
|
+
.replace(/__+/g, '_');
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Build a unique cache key for a request context.
|
|
357
|
+
* Includes escaped upstream URL for multi-upstream support.
|
|
358
|
+
*/
|
|
359
|
+
buildCacheKey(context, upstreamUrl) {
|
|
360
|
+
// Include method, protocol, path, and sorted query params
|
|
361
|
+
const queryString = Object.keys(context.query)
|
|
362
|
+
.sort()
|
|
363
|
+
.map(k => `${k}=${context.query[k]}`)
|
|
364
|
+
.join('&');
|
|
365
|
+
const baseKey = `${context.protocol}:${context.method}:${context.path}${queryString ? '?' + queryString : ''}`;
|
|
366
|
+
if (upstreamUrl) {
|
|
367
|
+
return `${this.escapeUrl(upstreamUrl)}/${baseKey}`;
|
|
368
|
+
}
|
|
369
|
+
return baseKey;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Determine TTL based on content characteristics.
|
|
373
|
+
*/
|
|
374
|
+
determineTtl(context, contentType, headers) {
|
|
375
|
+
// Check for Cache-Control header
|
|
376
|
+
const cacheControl = headers['cache-control'];
|
|
377
|
+
if (cacheControl) {
|
|
378
|
+
const maxAgeMatch = cacheControl.match(/max-age=(\d+)/);
|
|
379
|
+
if (maxAgeMatch) {
|
|
380
|
+
return parseInt(maxAgeMatch[1], 10);
|
|
381
|
+
}
|
|
382
|
+
if (cacheControl.includes('no-store') || cacheControl.includes('no-cache')) {
|
|
383
|
+
return 0;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
// Check if content is immutable (content-addressable)
|
|
387
|
+
if (this.isImmutableContent(context, contentType)) {
|
|
388
|
+
return this.config.immutableTtlSeconds;
|
|
389
|
+
}
|
|
390
|
+
// Default TTL for mutable content
|
|
391
|
+
return this.config.defaultTtlSeconds;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Check if content is immutable (content-addressable).
|
|
395
|
+
*/
|
|
396
|
+
isImmutableContent(context, contentType) {
|
|
397
|
+
// OCI blobs with digest are immutable
|
|
398
|
+
if (context.protocol === 'oci' && context.resourceType === 'blob') {
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
// NPM tarballs are immutable (versioned)
|
|
402
|
+
if (context.protocol === 'npm' && context.resourceType === 'tarball') {
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
// Maven artifacts with version are immutable
|
|
406
|
+
if (context.protocol === 'maven' && context.resourceType === 'artifact') {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
// Cargo crate files are immutable
|
|
410
|
+
if (context.protocol === 'cargo' && context.resourceType === 'crate') {
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
// Composer dist files are immutable
|
|
414
|
+
if (context.protocol === 'composer' && context.resourceType === 'dist') {
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
// PyPI package files are immutable
|
|
418
|
+
if (context.protocol === 'pypi' && context.resourceType === 'package') {
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
// RubyGems .gem files are immutable
|
|
422
|
+
if (context.protocol === 'rubygems' && context.resourceType === 'gem') {
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Evict oldest entries to make room for new ones.
|
|
429
|
+
*/
|
|
430
|
+
evictOldest() {
|
|
431
|
+
// Evict 10% of max entries
|
|
432
|
+
const evictCount = Math.ceil(this.maxMemoryEntries * 0.1);
|
|
433
|
+
let evicted = 0;
|
|
434
|
+
// First, try to evict stale entries
|
|
435
|
+
const now = new Date();
|
|
436
|
+
for (const [key, entry] of this.memoryCache.entries()) {
|
|
437
|
+
if (evicted >= evictCount)
|
|
438
|
+
break;
|
|
439
|
+
if (entry.stale || (entry.expiresAt && entry.expiresAt < now)) {
|
|
440
|
+
this.memoryCache.delete(key);
|
|
441
|
+
evicted++;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// If not enough evicted, evict oldest by cachedAt
|
|
445
|
+
if (evicted < evictCount) {
|
|
446
|
+
const entries = Array.from(this.memoryCache.entries())
|
|
447
|
+
.sort((a, b) => a[1].cachedAt.getTime() - b[1].cachedAt.getTime());
|
|
448
|
+
for (const [key] of entries) {
|
|
449
|
+
if (evicted >= evictCount)
|
|
450
|
+
break;
|
|
451
|
+
this.memoryCache.delete(key);
|
|
452
|
+
evicted++;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Start periodic cleanup of expired entries.
|
|
458
|
+
*/
|
|
459
|
+
startCleanup() {
|
|
460
|
+
// Run cleanup every minute
|
|
461
|
+
this.cleanupInterval = setInterval(() => {
|
|
462
|
+
this.cleanup();
|
|
463
|
+
}, 60000);
|
|
464
|
+
// Don't keep the process alive just for cleanup
|
|
465
|
+
if (this.cleanupInterval.unref) {
|
|
466
|
+
this.cleanupInterval.unref();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Remove all expired entries from memory cache.
|
|
471
|
+
*/
|
|
472
|
+
cleanup() {
|
|
473
|
+
const now = new Date();
|
|
474
|
+
const staleDeadline = new Date(now.getTime() - this.config.staleMaxAgeSeconds * 1000);
|
|
475
|
+
for (const [key, entry] of this.memoryCache.entries()) {
|
|
476
|
+
if (entry.expiresAt) {
|
|
477
|
+
// Remove if past stale deadline
|
|
478
|
+
if (entry.expiresAt < staleDeadline) {
|
|
479
|
+
this.memoryCache.delete(key);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy51cHN0cmVhbWNhY2hlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvdXBzdHJlYW0vY2xhc3Nlcy51cHN0cmVhbWNhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUtBLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBZ0JoRTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F1Qkc7QUFDSCxNQUFNLE9BQU8sYUFBYTtJQUN4QiwwQkFBMEI7SUFDVCxXQUFXLEdBQTZCLElBQUksR0FBRyxFQUFFLENBQUM7SUFFbkUsb0JBQW9CO0lBQ0gsTUFBTSxDQUF1QjtJQUU5QyxzQ0FBc0M7SUFDckIsZ0JBQWdCLENBQVM7SUFFMUMsb0NBQW9DO0lBQ25CLE9BQU8sQ0FBbUI7SUFFM0MsOEJBQThCO0lBQ3RCLGVBQWUsR0FBMEMsSUFBSSxDQUFDO0lBRXRFLFlBQ0UsTUFBc0MsRUFDdEMsbUJBQTJCLEtBQUssRUFDaEMsT0FBeUI7UUFFekIsSUFBSSxDQUFDLE1BQU0sR0FBRyxFQUFFLEdBQUcsb0JBQW9CLEVBQUUsR0FBRyxNQUFNLEVBQUUsQ0FBQztRQUNyRCxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsZ0JBQWdCLENBQUM7UUFDekMsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFFdkIsK0NBQStDO1FBQy9DLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUN4QixJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDdEIsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNJLFNBQVM7UUFDZCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDO0lBQzdCLENBQUM7SUFFRDs7T0FFRztJQUNJLFVBQVU7UUFDZixPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDO0lBQ3hCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksS0FBSyxDQUFDLEdBQUcsQ0FBQyxPQUE4QixFQUFFLFdBQW9CO1FBQ25FLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3pCLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUVELE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsT0FBTyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBRXJELDJCQUEyQjtRQUMzQixJQUFJLEtBQUssR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUV0QyxpREFBaUQ7UUFDakQsSUFBSSxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDM0IsS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN4QyxJQUFJLEtBQUssRUFBRSxDQUFDO2dCQUNWLDBCQUEwQjtnQkFDMUIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQ25DLENBQUM7UUFDSCxDQUFDO1FBRUQsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ1gsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBRUQsTUFBTSxHQUFHLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUV2Qiw0QkFBNEI7UUFDNUIsSUFBSSxLQUFLLENBQUMsU0FBUyxJQUFJLEtBQUssQ0FBQyxTQUFTLEdBQUcsR0FBRyxFQUFFLENBQUM7WUFDN0Msc0NBQXNDO1lBQ3RDLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxvQkFBb0IsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDckQsTUFBTSxRQUFRLEdBQUcsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDcEUsSUFBSSxRQUFRLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO29CQUMvQywyQkFBMkI7b0JBQzNCLEtBQUssQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDO29CQUNuQixPQUFPLEtBQUssQ0FBQztnQkFDZixDQUFDO1lBQ0gsQ0FBQztZQUNELDhCQUE4QjtZQUM5QixJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUM3QixJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDakIsTUFBTSxJQUFJLENBQUMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxHQUFFLENBQUMsQ0FBQyxDQUFDO1lBQ3BELENBQUM7WUFDRCxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFFRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxHQUFHLENBQ2QsT0FBOEIsRUFDOUIsSUFBWSxFQUNaLFdBQW1CLEVBQ25CLE9BQStCLEVBQy9CLFVBQWtCLEVBQ2xCLFdBQW1CLEVBQ25CLE9BQTBCO1FBRTFCLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3pCLE9BQU87UUFDVCxDQUFDO1FBRUQsbUNBQW1DO1FBQ25DLElBQUksSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7WUFDbkQsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ3JCLENBQUM7UUFFRCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLE9BQU8sRUFBRSxXQUFXLENBQUMsQ0FBQztRQUNyRCxNQUFNLEdBQUcsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO1FBRXZCLHNDQUFzQztRQUN0QyxNQUFNLFVBQVUsR0FBRyxPQUFPLEVBQUUsVUFBVSxJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLFdBQVcsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUUzRixNQUFNLEtBQUssR0FBZ0I7WUFDekIsSUFBSTtZQUNKLFdBQVc7WUFDWCxPQUFPO1lBQ1AsUUFBUSxFQUFFLEdBQUc7WUFDYixTQUFTLEVBQUUsVUFBVSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxHQUFHLFVBQVUsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUztZQUNuRixJQUFJLEVBQUUsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLE9BQU8sRUFBRSxJQUFJO1lBQ3RDLFVBQVU7WUFDVixLQUFLLEVBQUUsS0FBSztTQUNiLENBQUM7UUFFRixrQkFBa0I7UUFDbEIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBRWpDLDJCQUEyQjtRQUMzQixJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNqQixNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxFQUFFLEtBQUssRUFBRSxXQUFXLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLEdBQUUsQ0FBQyxDQUFDLENBQUM7UUFDcEUsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxXQUFXLENBQUMsT0FBOEIsRUFBRSxVQUFrQixFQUFFLFdBQW1CO1FBQzlGLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLHVCQUF1QixJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ3JFLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxPQUFPLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDckQsTUFBTSxHQUFHLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUV2QixNQUFNLEtBQUssR0FBZ0I7WUFDekIsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ3JCLFdBQVcsRUFBRSwwQkFBMEI7WUFDdkMsT0FBTyxFQUFFLEVBQUU7WUFDWCxRQUFRLEVBQUUsR0FBRztZQUNiLFNBQVMsRUFBRSxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyx1QkFBdUIsR0FBRyxJQUFJLENBQUM7WUFDL0UsVUFBVTtZQUNWLEtBQUssRUFBRSxLQUFLO1NBQ2IsQ0FBQztRQUVGLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUVqQyxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNqQixNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsR0FBRyxFQUFFLEtBQUssRUFBRSxXQUFXLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLEdBQUUsQ0FBQyxDQUFDLENBQUM7UUFDcEUsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxXQUFXLENBQUMsT0FBOEIsRUFBRSxXQUFvQjtRQUMzRSxNQUFNLEtBQUssR0FBRyxNQUFNLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBQ25ELE9BQU8sS0FBSyxLQUFLLElBQUksSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUM7SUFDbkQsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLFVBQVUsQ0FBQyxPQUE4QixFQUFFLFdBQW9CO1FBQzFFLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsT0FBTyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBQ3JELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBRTdDLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2pCLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsR0FBRSxDQUFDLENBQUMsQ0FBQztRQUNwRCxDQUFDO1FBRUQsT0FBTyxPQUFPLENBQUM7SUFDakIsQ0FBQztJQUVEOzs7T0FHRztJQUNJLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxPQUFlO1FBQzVDLElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQztRQUNkLEtBQUssTUFBTSxHQUFHLElBQUksSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDO1lBQzFDLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUN0QixJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDN0IsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7b0JBQ2pCLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsR0FBRSxDQUFDLENBQUMsQ0FBQztnQkFDcEQsQ0FBQztnQkFDRCxLQUFLLEVBQUUsQ0FBQztZQUNWLENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsa0JBQWtCLENBQUMsVUFBa0I7UUFDaEQsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO1FBQ2QsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxJQUFJLElBQUksQ0FBQyxXQUFXLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztZQUN0RCxJQUFJLEtBQUssQ0FBQyxVQUFVLEtBQUssVUFBVSxFQUFFLENBQUM7Z0JBQ3BDLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUM3QixJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztvQkFDakIsTUFBTSxJQUFJLENBQUMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxHQUFFLENBQUMsQ0FBQyxDQUFDO2dCQUNwRCxDQUFDO2dCQUNELEtBQUssRUFBRSxDQUFDO1lBQ1YsQ0FBQztRQUNILENBQUM7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxLQUFLO1FBQ2hCLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLENBQUM7UUFFekIsMEVBQTBFO1FBQzFFLHdEQUF3RDtJQUMxRCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxRQUFRO1FBQ2IsSUFBSSxVQUFVLEdBQUcsQ0FBQyxDQUFDO1FBQ25CLElBQUksVUFBVSxHQUFHLENBQUMsQ0FBQztRQUNuQixJQUFJLGFBQWEsR0FBRyxDQUFDLENBQUM7UUFDdEIsSUFBSSxTQUFTLEdBQUcsQ0FBQyxDQUFDO1FBQ2xCLE1BQU0sR0FBRyxHQUFHLElBQUksSUFBSSxFQUFFLENBQUM7UUFFdkIsS0FBSyxNQUFNLEtBQUssSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUM7WUFDOUMsU0FBUyxJQUFJLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO1lBRS9CLElBQUksS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQzVCLGFBQWEsRUFBRSxDQUFDO1lBQ2xCLENBQUM7aUJBQU0sSUFBSSxLQUFLLENBQUMsS0FBSyxJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsSUFBSSxLQUFLLENBQUMsU0FBUyxHQUFHLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ3JFLFVBQVUsRUFBRSxDQUFDO1lBQ2YsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLFVBQVUsRUFBRSxDQUFDO1lBQ2YsQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPO1lBQ0wsWUFBWSxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSTtZQUNuQyxZQUFZLEVBQUUsVUFBVTtZQUN4QixZQUFZLEVBQUUsVUFBVTtZQUN4QixlQUFlLEVBQUUsYUFBYTtZQUM5QixjQUFjLEVBQUUsU0FBUztZQUN6QixVQUFVLEVBQUUsSUFBSSxDQUFDLGdCQUFnQjtZQUNqQyxPQUFPLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPO1lBQzVCLFVBQVUsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU87U0FDM0IsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNJLElBQUk7UUFDVCxJQUFJLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztZQUN6QixhQUFhLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDO1lBQ3BDLElBQUksQ0FBQyxlQUFlLEdBQUcsSUFBSSxDQUFDO1FBQzlCLENBQUM7SUFDSCxDQUFDO0lBRUQsMkVBQTJFO0lBQzNFLGtCQUFrQjtJQUNsQiwyRUFBMkU7SUFFM0U7OztPQUdHO0lBQ0ssZ0JBQWdCLENBQUMsR0FBVztRQUNsQyxPQUFPLFNBQVMsR0FBRyxFQUFFLENBQUM7SUFDeEIsQ0FBQztJQUVEOztPQUVHO0lBQ0ssaUJBQWlCLENBQUMsR0FBVztRQUNuQyxPQUFPLFNBQVMsR0FBRyxPQUFPLENBQUM7SUFDN0IsQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLGVBQWUsQ0FBQyxHQUFXO1FBQ3ZDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTztZQUFFLE9BQU8sSUFBSSxDQUFDO1FBRS9CLElBQUksQ0FBQztZQUNILE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUM1QyxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLENBQUM7WUFFN0MscUNBQXFDO1lBQ3JDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsVUFBVSxDQUFDLEdBQUcsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDO2dCQUMzQyxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUM7Z0JBQ2hDLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQzthQUNqQyxDQUFDLENBQUM7WUFFSCxJQUFJLENBQUMsSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7Z0JBQ3pCLE9BQU8sSUFBSSxDQUFDO1lBQ2QsQ0FBQztZQUVELE1BQU0sSUFBSSxHQUFtQixJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztZQUV0RSxPQUFPO2dCQUNMLElBQUk7Z0JBQ0osV0FBVyxFQUFFLElBQUksQ0FBQyxXQUFXO2dCQUM3QixPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU87Z0JBQ3JCLFFBQVEsRUFBRSxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDO2dCQUNqQyxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTO2dCQUNoRSxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUk7Z0JBQ2YsVUFBVSxFQUFFLElBQUksQ0FBQyxVQUFVO2dCQUMzQixLQUFLLEVBQUUsS0FBSzthQUNiLENBQUM7UUFDSixDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQ1AsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssS0FBSyxDQUFDLGFBQWEsQ0FBQyxHQUFXLEVBQUUsS0FBa0IsRUFBRSxXQUFtQjtRQUM5RSxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU87WUFBRSxPQUFPO1FBRTFCLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUM1QyxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLENBQUM7UUFFN0MsTUFBTSxJQUFJLEdBQW1CO1lBQzNCLFdBQVcsRUFBRSxLQUFLLENBQUMsV0FBVztZQUM5QixPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU87WUFDdEIsUUFBUSxFQUFFLEtBQUssQ0FBQyxRQUFRLENBQUMsV0FBVyxFQUFFO1lBQ3RDLFNBQVMsRUFBRSxLQUFLLENBQUMsU0FBUyxFQUFFLFdBQVcsRUFBRTtZQUN6QyxJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUk7WUFDaEIsVUFBVSxFQUFFLEtBQUssQ0FBQyxVQUFVO1lBQzVCLFdBQVc7U0FDWixDQUFDO1FBRUYscUNBQXFDO1FBQ3JDLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQztZQUNoQixJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxRQUFRLEVBQUUsS0FBSyxDQUFDLElBQUksQ0FBQztZQUM1QyxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1NBQzdFLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxHQUFXO1FBQ3pDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTztZQUFFLE9BQU87UUFFMUIsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUU3QyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUM7WUFDaEIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsUUFBUSxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxHQUFFLENBQUMsQ0FBQztZQUNuRCxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxRQUFRLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLEdBQUUsQ0FBQyxDQUFDO1NBQ3BELENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCwyRUFBMkU7SUFDM0UsaUJBQWlCO0lBQ2pCLDJFQUEyRTtJQUUzRTs7T0FFRztJQUNLLFNBQVMsQ0FBQyxHQUFXO1FBQzNCLHVEQUF1RDtRQUN2RCxPQUFPLEdBQUc7YUFDUCxPQUFPLENBQUMsY0FBYyxFQUFFLEVBQUUsQ0FBQzthQUMzQixPQUFPLENBQUMsZ0JBQWdCLEVBQUUsR0FBRyxDQUFDO2FBQzlCLE9BQU8sQ0FBQyxNQUFNLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDMUIsQ0FBQztJQUVEOzs7T0FHRztJQUNLLGFBQWEsQ0FBQyxPQUE4QixFQUFFLFdBQW9CO1FBQ3hFLDBEQUEwRDtRQUMxRCxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUM7YUFDM0MsSUFBSSxFQUFFO2FBQ04sR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2FBQ3BDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUViLE1BQU0sT0FBTyxHQUFHLEdBQUcsT0FBTyxDQUFDLFFBQVEsSUFBSSxPQUFPLENBQUMsTUFBTSxJQUFJLE9BQU8sQ0FBQyxJQUFJLEdBQUcsV0FBVyxDQUFDLENBQUMsQ0FBQyxHQUFHLEdBQUcsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUUvRyxJQUFJLFdBQVcsRUFBRSxDQUFDO1lBQ2hCLE9BQU8sR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLFdBQVcsQ0FBQyxJQUFJLE9BQU8sRUFBRSxDQUFDO1FBQ3JELENBQUM7UUFFRCxPQUFPLE9BQU8sQ0FBQztJQUNqQixDQUFDO0lBRUQ7O09BRUc7SUFDSyxZQUFZLENBQ2xCLE9BQThCLEVBQzlCLFdBQW1CLEVBQ25CLE9BQStCO1FBRS9CLGlDQUFpQztRQUNqQyxNQUFNLFlBQVksR0FBRyxPQUFPLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDOUMsSUFBSSxZQUFZLEVBQUUsQ0FBQztZQUNqQixNQUFNLFdBQVcsR0FBRyxZQUFZLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxDQUFDO1lBQ3hELElBQUksV0FBVyxFQUFFLENBQUM7Z0JBQ2hCLE9BQU8sUUFBUSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztZQUN0QyxDQUFDO1lBQ0QsSUFBSSxZQUFZLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxJQUFJLFlBQVksQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztnQkFDM0UsT0FBTyxDQUFDLENBQUM7WUFDWCxDQUFDO1FBQ0gsQ0FBQztRQUVELHNEQUFzRDtRQUN0RCxJQUFJLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLEVBQUUsV0FBVyxDQUFDLEVBQUUsQ0FBQztZQUNsRCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsbUJBQW1CLENBQUM7UUFDekMsQ0FBQztRQUVELGtDQUFrQztRQUNsQyxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsaUJBQWlCLENBQUM7SUFDdkMsQ0FBQztJQUVEOztPQUVHO0lBQ0ssa0JBQWtCLENBQUMsT0FBOEIsRUFBRSxXQUFtQjtRQUM1RSxzQ0FBc0M7UUFDdEMsSUFBSSxPQUFPLENBQUMsUUFBUSxLQUFLLEtBQUssSUFBSSxPQUFPLENBQUMsWUFBWSxLQUFLLE1BQU0sRUFBRSxDQUFDO1lBQ2xFLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUVELHlDQUF5QztRQUN6QyxJQUFJLE9BQU8sQ0FBQyxRQUFRLEtBQUssS0FBSyxJQUFJLE9BQU8sQ0FBQyxZQUFZLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDckUsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBRUQsNkNBQTZDO1FBQzdDLElBQUksT0FBTyxDQUFDLFFBQVEsS0FBSyxPQUFPLElBQUksT0FBTyxDQUFDLFlBQVksS0FBSyxVQUFVLEVBQUUsQ0FBQztZQUN4RSxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFFRCxrQ0FBa0M7UUFDbEMsSUFBSSxPQUFPLENBQUMsUUFBUSxLQUFLLE9BQU8sSUFBSSxPQUFPLENBQUMsWUFBWSxLQUFLLE9BQU8sRUFBRSxDQUFDO1lBQ3JFLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUVELG9DQUFvQztRQUNwQyxJQUFJLE9BQU8sQ0FBQyxRQUFRLEtBQUssVUFBVSxJQUFJLE9BQU8sQ0FBQyxZQUFZLEtBQUssTUFBTSxFQUFFLENBQUM7WUFDdkUsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBRUQsbUNBQW1DO1FBQ25DLElBQUksT0FBTyxDQUFDLFFBQVEsS0FBSyxNQUFNLElBQUksT0FBTyxDQUFDLFlBQVksS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUN0RSxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFFRCxvQ0FBb0M7UUFDcEMsSUFBSSxPQUFPLENBQUMsUUFBUSxLQUFLLFVBQVUsSUFBSSxPQUFPLENBQUMsWUFBWSxLQUFLLEtBQUssRUFBRSxDQUFDO1lBQ3RFLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUVELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUVEOztPQUVHO0lBQ0ssV0FBVztRQUNqQiwyQkFBMkI7UUFDM0IsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsR0FBRyxDQUFDLENBQUM7UUFDMUQsSUFBSSxPQUFPLEdBQUcsQ0FBQyxDQUFDO1FBRWhCLG9DQUFvQztRQUNwQyxNQUFNLEdBQUcsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO1FBQ3ZCLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUM7WUFDdEQsSUFBSSxPQUFPLElBQUksVUFBVTtnQkFBRSxNQUFNO1lBQ2pDLElBQUksS0FBSyxDQUFDLEtBQUssSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLElBQUksS0FBSyxDQUFDLFNBQVMsR0FBRyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUM5RCxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDN0IsT0FBTyxFQUFFLENBQUM7WUFDWixDQUFDO1FBQ0gsQ0FBQztRQUVELGtEQUFrRDtRQUNsRCxJQUFJLE9BQU8sR0FBRyxVQUFVLEVBQUUsQ0FBQztZQUN6QixNQUFNLE9BQU8sR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsT0FBTyxFQUFFLENBQUM7aUJBQ25ELElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsT0FBTyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBRXJFLEtBQUssTUFBTSxDQUFDLEdBQUcsQ0FBQyxJQUFJLE9BQU8sRUFBRSxDQUFDO2dCQUM1QixJQUFJLE9BQU8sSUFBSSxVQUFVO29CQUFFLE1BQU07Z0JBQ2pDLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUM3QixPQUFPLEVBQUUsQ0FBQztZQUNaLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssWUFBWTtRQUNsQiwyQkFBMkI7UUFDM0IsSUFBSSxDQUFDLGVBQWUsR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFO1lBQ3RDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNqQixDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFVixnREFBZ0Q7UUFDaEQsSUFBSSxJQUFJLENBQUMsZUFBZSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQy9CLElBQUksQ0FBQyxlQUFlLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDL0IsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNLLE9BQU87UUFDYixNQUFNLEdBQUcsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO1FBQ3ZCLE1BQU0sYUFBYSxHQUFHLElBQUksSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLGtCQUFrQixHQUFHLElBQUksQ0FBQyxDQUFDO1FBRXRGLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUM7WUFDdEQsSUFBSSxLQUFLLENBQUMsU0FBUyxFQUFFLENBQUM7Z0JBQ3BCLGdDQUFnQztnQkFDaEMsSUFBSSxLQUFLLENBQUMsU0FBUyxHQUFHLGFBQWEsRUFBRSxDQUFDO29CQUNwQyxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDL0IsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztDQUNGIn0=
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './interfaces.upstream.js';
|
|
2
|
+
export { CircuitBreaker, CircuitOpenError, withCircuitBreaker } from './classes.circuitbreaker.js';
|
|
3
|
+
export type { ICircuitBreakerMetrics } from './classes.circuitbreaker.js';
|
|
4
|
+
export { UpstreamCache } from './classes.upstreamcache.js';
|
|
5
|
+
export type { ICacheSetOptions, ICacheStats } from './classes.upstreamcache.js';
|
|
6
|
+
export { BaseUpstream } from './classes.baseupstream.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Interfaces and types
|
|
2
|
+
export * from './interfaces.upstream.js';
|
|
3
|
+
// Classes
|
|
4
|
+
export { CircuitBreaker, CircuitOpenError, withCircuitBreaker } from './classes.circuitbreaker.js';
|
|
5
|
+
export { UpstreamCache } from './classes.upstreamcache.js';
|
|
6
|
+
export { BaseUpstream } from './classes.baseupstream.js';
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy91cHN0cmVhbS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx1QkFBdUI7QUFDdkIsY0FBYywwQkFBMEIsQ0FBQztBQUV6QyxVQUFVO0FBQ1YsT0FBTyxFQUFFLGNBQWMsRUFBRSxnQkFBZ0IsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBR25HLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUczRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sMkJBQTJCLENBQUMifQ==
|