@memberjunction/core 2.127.0 → 2.129.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/__tests__/mocks/TestMetadataProvider.d.ts +4 -2
- package/dist/__tests__/mocks/TestMetadataProvider.d.ts.map +1 -1
- package/dist/__tests__/mocks/TestMetadataProvider.js +9 -3
- package/dist/__tests__/mocks/TestMetadataProvider.js.map +1 -1
- package/dist/generic/RegisterForStartup.d.ts +228 -0
- package/dist/generic/RegisterForStartup.d.ts.map +1 -0
- package/dist/generic/RegisterForStartup.js +233 -0
- package/dist/generic/RegisterForStartup.js.map +1 -0
- package/dist/generic/baseEngine.d.ts +191 -8
- package/dist/generic/baseEngine.d.ts.map +1 -1
- package/dist/generic/baseEngine.js +360 -14
- package/dist/generic/baseEngine.js.map +1 -1
- package/dist/generic/baseEngineRegistry.d.ts +247 -0
- package/dist/generic/baseEngineRegistry.d.ts.map +1 -0
- package/dist/generic/baseEngineRegistry.js +470 -0
- package/dist/generic/baseEngineRegistry.js.map +1 -0
- package/dist/generic/entityInfo.d.ts +50 -0
- package/dist/generic/entityInfo.d.ts.map +1 -1
- package/dist/generic/entityInfo.js +56 -0
- package/dist/generic/entityInfo.js.map +1 -1
- package/dist/generic/graphqlTypeNames.d.ts +90 -0
- package/dist/generic/graphqlTypeNames.d.ts.map +1 -0
- package/dist/generic/graphqlTypeNames.js +119 -0
- package/dist/generic/graphqlTypeNames.js.map +1 -0
- package/dist/generic/interfaces.d.ts +234 -3
- package/dist/generic/interfaces.d.ts.map +1 -1
- package/dist/generic/interfaces.js.map +1 -1
- package/dist/generic/localCacheManager.d.ts +388 -0
- package/dist/generic/localCacheManager.d.ts.map +1 -0
- package/dist/generic/localCacheManager.js +856 -0
- package/dist/generic/localCacheManager.js.map +1 -0
- package/dist/generic/providerBase.d.ts +227 -13
- package/dist/generic/providerBase.d.ts.map +1 -1
- package/dist/generic/providerBase.js +751 -6
- package/dist/generic/providerBase.js.map +1 -1
- package/dist/generic/queryInfo.d.ts +18 -0
- package/dist/generic/queryInfo.d.ts.map +1 -1
- package/dist/generic/queryInfo.js +18 -0
- package/dist/generic/queryInfo.js.map +1 -1
- package/dist/generic/queryInfoInterfaces.d.ts +17 -0
- package/dist/generic/queryInfoInterfaces.d.ts.map +1 -1
- package/dist/generic/runQuery.d.ts +30 -0
- package/dist/generic/runQuery.d.ts.map +1 -1
- package/dist/generic/runQuery.js +13 -0
- package/dist/generic/runQuery.js.map +1 -1
- package/dist/generic/telemetryManager.d.ts +628 -0
- package/dist/generic/telemetryManager.d.ts.map +1 -0
- package/dist/generic/telemetryManager.js +1011 -0
- package/dist/generic/telemetryManager.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/views/runView.d.ts +25 -0
- package/dist/views/runView.d.ts.map +1 -1
- package/dist/views/runView.js +4 -5
- package/dist/views/runView.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,856 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalCacheManager = exports.CacheCategory = void 0;
|
|
4
|
+
const global_1 = require("@memberjunction/global");
|
|
5
|
+
const logging_1 = require("./logging");
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// DEFAULT CONFIGURATION
|
|
8
|
+
// ============================================================================
|
|
9
|
+
const DEFAULT_CONFIG = {
|
|
10
|
+
enabled: true,
|
|
11
|
+
maxSizeBytes: 50 * 1024 * 1024, // 50MB
|
|
12
|
+
maxEntries: 1000,
|
|
13
|
+
defaultTTLMs: 5 * 60 * 1000, // 5 minutes
|
|
14
|
+
evictionPolicy: 'lru'
|
|
15
|
+
};
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// STORAGE CATEGORIES
|
|
18
|
+
// ============================================================================
|
|
19
|
+
/**
|
|
20
|
+
* Storage categories for organizing cache data.
|
|
21
|
+
* These map to IndexedDB object stores or localStorage key prefixes.
|
|
22
|
+
*/
|
|
23
|
+
exports.CacheCategory = {
|
|
24
|
+
/** Cache for RunView results */
|
|
25
|
+
RunViewCache: 'RunViewCache',
|
|
26
|
+
/** Cache for RunQuery results */
|
|
27
|
+
RunQueryCache: 'RunQueryCache',
|
|
28
|
+
/** Cache for Dataset results */
|
|
29
|
+
DatasetCache: 'DatasetCache',
|
|
30
|
+
/** Cache for metadata */
|
|
31
|
+
Metadata: 'Metadata',
|
|
32
|
+
/** Default category for uncategorized data */
|
|
33
|
+
Default: 'default'
|
|
34
|
+
};
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// LOCAL CACHE MANAGER
|
|
37
|
+
// ============================================================================
|
|
38
|
+
/**
|
|
39
|
+
* LocalCacheManager is a singleton that provides a unified caching abstraction
|
|
40
|
+
* for datasets, RunView results, and RunQuery results. It wraps ILocalStorageProvider
|
|
41
|
+
* for actual storage and maintains an internal registry of all cached items.
|
|
42
|
+
*
|
|
43
|
+
* Key features:
|
|
44
|
+
* - Typed methods for datasets, RunViews, and RunQueries
|
|
45
|
+
* - Automatic cache metadata tracking (timestamps, access counts, sizes)
|
|
46
|
+
* - Hit/miss statistics for performance monitoring
|
|
47
|
+
* - Eviction policies (LRU, LFU, FIFO) for memory management
|
|
48
|
+
* - Dashboard-friendly registry queries
|
|
49
|
+
*
|
|
50
|
+
* Usage:
|
|
51
|
+
* ```typescript
|
|
52
|
+
* // Initialize during app startup
|
|
53
|
+
* await LocalCacheManager.Instance.Initialize(storageProvider);
|
|
54
|
+
*
|
|
55
|
+
* // Cache a dataset
|
|
56
|
+
* await LocalCacheManager.Instance.SetDataset('MyDataset', filters, dataset, keyPrefix);
|
|
57
|
+
*
|
|
58
|
+
* // Retrieve cached data
|
|
59
|
+
* const cached = await LocalCacheManager.Instance.GetDataset('MyDataset', filters, keyPrefix);
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
class LocalCacheManager extends global_1.BaseSingleton {
|
|
63
|
+
/**
|
|
64
|
+
* Returns the singleton instance of LocalCacheManager
|
|
65
|
+
*/
|
|
66
|
+
static get Instance() {
|
|
67
|
+
return super.getInstance();
|
|
68
|
+
}
|
|
69
|
+
constructor() {
|
|
70
|
+
super();
|
|
71
|
+
this._storageProvider = null;
|
|
72
|
+
this._registry = new Map();
|
|
73
|
+
this._initialized = false;
|
|
74
|
+
this._initializePromise = null;
|
|
75
|
+
this._stats = { hits: 0, misses: 0 };
|
|
76
|
+
this._config = { ...DEFAULT_CONFIG };
|
|
77
|
+
this.REGISTRY_KEY = '__MJ_CACHE_REGISTRY__';
|
|
78
|
+
this._persistTimeout = null;
|
|
79
|
+
}
|
|
80
|
+
// ========================================================================
|
|
81
|
+
// INITIALIZATION
|
|
82
|
+
// ========================================================================
|
|
83
|
+
/**
|
|
84
|
+
* Initialize the cache manager with a storage provider.
|
|
85
|
+
* This should be called during app startup after the storage provider is available.
|
|
86
|
+
*
|
|
87
|
+
* This method is safe to call multiple times - subsequent calls will return the same
|
|
88
|
+
* promise as the first caller, ensuring initialization only happens once.
|
|
89
|
+
*
|
|
90
|
+
* @param storageProvider - The local storage provider to use for persistence
|
|
91
|
+
* @param config - Optional configuration overrides
|
|
92
|
+
* @returns A promise that resolves when initialization is complete
|
|
93
|
+
*/
|
|
94
|
+
Initialize(storageProvider, config) {
|
|
95
|
+
// If already initialized, return immediately
|
|
96
|
+
if (this._initialized) {
|
|
97
|
+
return Promise.resolve();
|
|
98
|
+
}
|
|
99
|
+
// If initialization is in progress, return the existing promise
|
|
100
|
+
// so all callers await the same initialization
|
|
101
|
+
if (this._initializePromise) {
|
|
102
|
+
return this._initializePromise;
|
|
103
|
+
}
|
|
104
|
+
// First caller - start initialization and store the promise
|
|
105
|
+
this._initializePromise = this.doInitialize(storageProvider, config);
|
|
106
|
+
return this._initializePromise;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Internal initialization logic - only called once by the first caller
|
|
110
|
+
*/
|
|
111
|
+
async doInitialize(storageProvider, config) {
|
|
112
|
+
this._storageProvider = storageProvider;
|
|
113
|
+
if (config) {
|
|
114
|
+
this._config = { ...this._config, ...config };
|
|
115
|
+
}
|
|
116
|
+
await this.loadRegistry();
|
|
117
|
+
this._initialized = true;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Returns whether the cache manager has been initialized
|
|
121
|
+
*/
|
|
122
|
+
get IsInitialized() {
|
|
123
|
+
return this._initialized;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Returns the current configuration
|
|
127
|
+
*/
|
|
128
|
+
get Config() {
|
|
129
|
+
return { ...this._config };
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Updates the configuration at runtime
|
|
133
|
+
*/
|
|
134
|
+
UpdateConfig(config) {
|
|
135
|
+
this._config = { ...this._config, ...config };
|
|
136
|
+
}
|
|
137
|
+
// ========================================================================
|
|
138
|
+
// DATASET CACHING
|
|
139
|
+
// ========================================================================
|
|
140
|
+
/**
|
|
141
|
+
* Stores a dataset in the local cache.
|
|
142
|
+
*
|
|
143
|
+
* @param name - The dataset name
|
|
144
|
+
* @param itemFilters - Optional filters applied to the dataset
|
|
145
|
+
* @param dataset - The dataset result to cache
|
|
146
|
+
* @param keyPrefix - Prefix for the cache key (typically includes connection info)
|
|
147
|
+
*/
|
|
148
|
+
async SetDataset(name, itemFilters, dataset, keyPrefix) {
|
|
149
|
+
if (!this._storageProvider || !this._config.enabled)
|
|
150
|
+
return;
|
|
151
|
+
const key = this.buildDatasetKey(name, itemFilters, keyPrefix);
|
|
152
|
+
const value = JSON.stringify(dataset);
|
|
153
|
+
const sizeBytes = this.estimateSize(value);
|
|
154
|
+
// Check if we need to evict entries
|
|
155
|
+
await this.evictIfNeeded(sizeBytes);
|
|
156
|
+
try {
|
|
157
|
+
await this._storageProvider.SetItem(key, value, exports.CacheCategory.DatasetCache);
|
|
158
|
+
await this._storageProvider.SetItem(key + '_date', dataset.LatestUpdateDate.toISOString(), exports.CacheCategory.DatasetCache);
|
|
159
|
+
this.registerEntry({
|
|
160
|
+
key,
|
|
161
|
+
type: 'dataset',
|
|
162
|
+
name,
|
|
163
|
+
params: itemFilters ? { itemFilters } : undefined,
|
|
164
|
+
cachedAt: Date.now(),
|
|
165
|
+
lastAccessedAt: Date.now(),
|
|
166
|
+
accessCount: 1,
|
|
167
|
+
sizeBytes,
|
|
168
|
+
maxUpdatedAt: dataset.LatestUpdateDate.toISOString()
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
(0, logging_1.LogError)(`LocalCacheManager.SetDataset failed: ${e}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Retrieves a cached dataset.
|
|
177
|
+
*
|
|
178
|
+
* @param name - The dataset name
|
|
179
|
+
* @param itemFilters - Optional filters applied to the dataset
|
|
180
|
+
* @param keyPrefix - Prefix for the cache key
|
|
181
|
+
* @returns The cached dataset or null if not found
|
|
182
|
+
*/
|
|
183
|
+
async GetDataset(name, itemFilters, keyPrefix) {
|
|
184
|
+
if (!this._storageProvider || !this._config.enabled)
|
|
185
|
+
return null;
|
|
186
|
+
const key = this.buildDatasetKey(name, itemFilters, keyPrefix);
|
|
187
|
+
try {
|
|
188
|
+
const value = await this._storageProvider.GetItem(key, exports.CacheCategory.DatasetCache);
|
|
189
|
+
if (value) {
|
|
190
|
+
this.recordAccess(key);
|
|
191
|
+
this._stats.hits++;
|
|
192
|
+
return JSON.parse(value);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (e) {
|
|
196
|
+
(0, logging_1.LogError)(`LocalCacheManager.GetDataset failed: ${e}`);
|
|
197
|
+
}
|
|
198
|
+
this._stats.misses++;
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Gets the timestamp of a cached dataset.
|
|
203
|
+
*
|
|
204
|
+
* @param name - The dataset name
|
|
205
|
+
* @param itemFilters - Optional filters applied to the dataset
|
|
206
|
+
* @param keyPrefix - Prefix for the cache key
|
|
207
|
+
* @returns The cache timestamp or null if not found
|
|
208
|
+
*/
|
|
209
|
+
async GetDatasetTimestamp(name, itemFilters, keyPrefix) {
|
|
210
|
+
if (!this._storageProvider)
|
|
211
|
+
return null;
|
|
212
|
+
const key = this.buildDatasetKey(name, itemFilters, keyPrefix);
|
|
213
|
+
try {
|
|
214
|
+
const dateStr = await this._storageProvider.GetItem(key + '_date', exports.CacheCategory.DatasetCache);
|
|
215
|
+
return dateStr ? new Date(dateStr) : null;
|
|
216
|
+
}
|
|
217
|
+
catch (e) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Clears a cached dataset.
|
|
223
|
+
*
|
|
224
|
+
* @param name - The dataset name
|
|
225
|
+
* @param itemFilters - Optional filters applied to the dataset
|
|
226
|
+
* @param keyPrefix - Prefix for the cache key
|
|
227
|
+
*/
|
|
228
|
+
async ClearDataset(name, itemFilters, keyPrefix) {
|
|
229
|
+
if (!this._storageProvider)
|
|
230
|
+
return;
|
|
231
|
+
const key = this.buildDatasetKey(name, itemFilters, keyPrefix);
|
|
232
|
+
try {
|
|
233
|
+
await this._storageProvider.Remove(key, exports.CacheCategory.DatasetCache);
|
|
234
|
+
await this._storageProvider.Remove(key + '_date', exports.CacheCategory.DatasetCache);
|
|
235
|
+
this.unregisterEntry(key);
|
|
236
|
+
}
|
|
237
|
+
catch (e) {
|
|
238
|
+
(0, logging_1.LogError)(`LocalCacheManager.ClearDataset failed: ${e}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Checks if a dataset is cached.
|
|
243
|
+
*
|
|
244
|
+
* @param name - The dataset name
|
|
245
|
+
* @param itemFilters - Optional filters applied to the dataset
|
|
246
|
+
* @param keyPrefix - Prefix for the cache key
|
|
247
|
+
* @returns True if the dataset is cached
|
|
248
|
+
*/
|
|
249
|
+
async IsDatasetCached(name, itemFilters, keyPrefix) {
|
|
250
|
+
if (!this._storageProvider)
|
|
251
|
+
return false;
|
|
252
|
+
const key = this.buildDatasetKey(name, itemFilters, keyPrefix);
|
|
253
|
+
try {
|
|
254
|
+
const val = await this._storageProvider.GetItem(key, exports.CacheCategory.DatasetCache);
|
|
255
|
+
return val != null;
|
|
256
|
+
}
|
|
257
|
+
catch (e) {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// ========================================================================
|
|
262
|
+
// RUNVIEW CACHING
|
|
263
|
+
// ========================================================================
|
|
264
|
+
/**
|
|
265
|
+
* Generates a human-readable cache fingerprint for a RunView request.
|
|
266
|
+
* This fingerprint uniquely identifies the query based on its parameters and connection.
|
|
267
|
+
*
|
|
268
|
+
* Format: EntityName|filter|orderBy|resultType|maxRows|startRow|connection
|
|
269
|
+
* Example: Users|Active=1|Name ASC|simple|100|0|localhost
|
|
270
|
+
*
|
|
271
|
+
* @param params - The RunView parameters
|
|
272
|
+
* @param connectionPrefix - Prefix identifying the connection (e.g., server URL) to differentiate caches across connections
|
|
273
|
+
* @returns A unique, human-readable fingerprint string
|
|
274
|
+
*/
|
|
275
|
+
GenerateRunViewFingerprint(params, connectionPrefix) {
|
|
276
|
+
const entity = params.EntityName?.trim() || 'Unknown';
|
|
277
|
+
const filter = (params.ExtraFilter || '').trim();
|
|
278
|
+
const orderBy = (params.OrderBy || '').trim();
|
|
279
|
+
const resultType = params.ResultType || 'simple';
|
|
280
|
+
const maxRows = params.MaxRows ?? -1;
|
|
281
|
+
const startRow = params.StartRow ?? 0;
|
|
282
|
+
const connection = connectionPrefix || '';
|
|
283
|
+
// Build human-readable fingerprint with pipe separators
|
|
284
|
+
// Format: Entity|Filter|OrderBy|ResultType|MaxRows|StartRow|Connection
|
|
285
|
+
const parts = [
|
|
286
|
+
entity,
|
|
287
|
+
filter || '_', // Use underscore for empty filter
|
|
288
|
+
orderBy || '_', // Use underscore for empty orderBy
|
|
289
|
+
resultType,
|
|
290
|
+
maxRows.toString(),
|
|
291
|
+
startRow.toString()
|
|
292
|
+
];
|
|
293
|
+
// Only include connection if provided
|
|
294
|
+
if (connection) {
|
|
295
|
+
parts.push(connection);
|
|
296
|
+
}
|
|
297
|
+
return parts.join('|');
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Stores a RunView result in the cache.
|
|
301
|
+
*
|
|
302
|
+
* @param fingerprint - The cache fingerprint (from GenerateRunViewFingerprint)
|
|
303
|
+
* @param params - The original RunView parameters
|
|
304
|
+
* @param results - The results to cache
|
|
305
|
+
* @param maxUpdatedAt - The latest __mj_UpdatedAt from the results
|
|
306
|
+
* @param rowCount - Optional row count (defaults to results.length if not provided)
|
|
307
|
+
*/
|
|
308
|
+
async SetRunViewResult(fingerprint, params, results, maxUpdatedAt, rowCount) {
|
|
309
|
+
if (!this._storageProvider || !this._config.enabled)
|
|
310
|
+
return;
|
|
311
|
+
const actualRowCount = rowCount ?? results.length;
|
|
312
|
+
const value = JSON.stringify({ results, maxUpdatedAt, rowCount: actualRowCount });
|
|
313
|
+
const sizeBytes = this.estimateSize(value);
|
|
314
|
+
// Check if we need to evict entries
|
|
315
|
+
await this.evictIfNeeded(sizeBytes);
|
|
316
|
+
try {
|
|
317
|
+
await this._storageProvider.SetItem(fingerprint, value, exports.CacheCategory.RunViewCache);
|
|
318
|
+
this.registerEntry({
|
|
319
|
+
key: fingerprint,
|
|
320
|
+
type: 'runview',
|
|
321
|
+
name: params.EntityName || 'Unknown',
|
|
322
|
+
fingerprint,
|
|
323
|
+
params: {
|
|
324
|
+
EntityName: params.EntityName,
|
|
325
|
+
ExtraFilter: params.ExtraFilter,
|
|
326
|
+
OrderBy: params.OrderBy,
|
|
327
|
+
ResultType: params.ResultType,
|
|
328
|
+
MaxRows: params.MaxRows
|
|
329
|
+
},
|
|
330
|
+
cachedAt: Date.now(),
|
|
331
|
+
lastAccessedAt: Date.now(),
|
|
332
|
+
accessCount: 1,
|
|
333
|
+
sizeBytes,
|
|
334
|
+
maxUpdatedAt,
|
|
335
|
+
rowCount: actualRowCount
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
catch (e) {
|
|
339
|
+
(0, logging_1.LogError)(`LocalCacheManager.SetRunViewResult failed: ${e}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Retrieves a cached RunView result.
|
|
344
|
+
*
|
|
345
|
+
* @param fingerprint - The cache fingerprint
|
|
346
|
+
* @returns The cached results, maxUpdatedAt, and rowCount, or null if not found
|
|
347
|
+
*/
|
|
348
|
+
async GetRunViewResult(fingerprint) {
|
|
349
|
+
if (!this._storageProvider || !this._config.enabled)
|
|
350
|
+
return null;
|
|
351
|
+
try {
|
|
352
|
+
const value = await this._storageProvider.GetItem(fingerprint, exports.CacheCategory.RunViewCache);
|
|
353
|
+
if (value) {
|
|
354
|
+
this.recordAccess(fingerprint);
|
|
355
|
+
this._stats.hits++;
|
|
356
|
+
const parsed = JSON.parse(value);
|
|
357
|
+
// Handle legacy entries that may not have rowCount
|
|
358
|
+
return {
|
|
359
|
+
results: parsed.results,
|
|
360
|
+
maxUpdatedAt: parsed.maxUpdatedAt,
|
|
361
|
+
rowCount: parsed.rowCount ?? parsed.results?.length ?? 0
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
catch (e) {
|
|
366
|
+
(0, logging_1.LogError)(`LocalCacheManager.GetRunViewResult failed: ${e}`);
|
|
367
|
+
}
|
|
368
|
+
this._stats.misses++;
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Invalidates a cached RunView result.
|
|
373
|
+
*
|
|
374
|
+
* @param fingerprint - The cache fingerprint to invalidate
|
|
375
|
+
*/
|
|
376
|
+
async InvalidateRunViewResult(fingerprint) {
|
|
377
|
+
if (!this._storageProvider)
|
|
378
|
+
return;
|
|
379
|
+
try {
|
|
380
|
+
await this._storageProvider.Remove(fingerprint, exports.CacheCategory.RunViewCache);
|
|
381
|
+
this.unregisterEntry(fingerprint);
|
|
382
|
+
}
|
|
383
|
+
catch (e) {
|
|
384
|
+
(0, logging_1.LogError)(`LocalCacheManager.InvalidateRunViewResult failed: ${e}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Invalidates all cached RunView results for a specific entity.
|
|
389
|
+
* Useful when an entity's data changes and all related caches should be cleared.
|
|
390
|
+
*
|
|
391
|
+
* @param entityName - The entity name to invalidate
|
|
392
|
+
*/
|
|
393
|
+
async InvalidateEntityCaches(entityName) {
|
|
394
|
+
if (!this._storageProvider)
|
|
395
|
+
return;
|
|
396
|
+
const normalizedName = entityName.toLowerCase().trim();
|
|
397
|
+
const toRemove = [];
|
|
398
|
+
for (const [key, entry] of this._registry.entries()) {
|
|
399
|
+
if (entry.type === 'runview' && entry.name.toLowerCase().trim() === normalizedName) {
|
|
400
|
+
toRemove.push(key);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
for (const key of toRemove) {
|
|
404
|
+
try {
|
|
405
|
+
await this._storageProvider.Remove(key, exports.CacheCategory.RunViewCache);
|
|
406
|
+
this._registry.delete(key);
|
|
407
|
+
}
|
|
408
|
+
catch (e) {
|
|
409
|
+
(0, logging_1.LogError)(`LocalCacheManager.InvalidateEntityCaches failed for key ${key}: ${e}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
await this.persistRegistry();
|
|
413
|
+
}
|
|
414
|
+
// ========================================================================
|
|
415
|
+
// RUNQUERY CACHING
|
|
416
|
+
// ========================================================================
|
|
417
|
+
/**
|
|
418
|
+
* Generates a human-readable cache fingerprint for a RunQuery request.
|
|
419
|
+
*
|
|
420
|
+
* Format: QueryName|QueryID|params|connection
|
|
421
|
+
* Example: GetActiveUsers|abc123|{"status":"active"}|localhost
|
|
422
|
+
*
|
|
423
|
+
* @param queryId - The query ID
|
|
424
|
+
* @param queryName - The query name
|
|
425
|
+
* @param parameters - Optional query parameters
|
|
426
|
+
* @param connectionPrefix - Prefix identifying the connection (e.g., server URL) to differentiate caches across connections
|
|
427
|
+
* @returns A unique, human-readable fingerprint string
|
|
428
|
+
*/
|
|
429
|
+
GenerateRunQueryFingerprint(queryId, queryName, parameters, connectionPrefix) {
|
|
430
|
+
const name = queryName?.trim() || 'Unknown';
|
|
431
|
+
const id = queryId || '_';
|
|
432
|
+
const params = parameters ? JSON.stringify(parameters) : '_';
|
|
433
|
+
const connection = connectionPrefix || '';
|
|
434
|
+
// Build human-readable fingerprint with pipe separators
|
|
435
|
+
// Format: QueryName|QueryID|Params|Connection
|
|
436
|
+
const parts = [name, id, params];
|
|
437
|
+
// Only include connection if provided
|
|
438
|
+
if (connection) {
|
|
439
|
+
parts.push(connection);
|
|
440
|
+
}
|
|
441
|
+
return parts.join('|');
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Stores a RunQuery result in the cache.
|
|
445
|
+
*
|
|
446
|
+
* @param fingerprint - The cache fingerprint
|
|
447
|
+
* @param queryName - The query name for display
|
|
448
|
+
* @param results - The results to cache
|
|
449
|
+
* @param maxUpdatedAt - The latest update timestamp (for smart cache validation)
|
|
450
|
+
* @param rowCount - Optional row count (defaults to results.length if not provided)
|
|
451
|
+
* @param queryId - Optional query ID for reference
|
|
452
|
+
* @param ttlMs - Optional TTL in milliseconds (for cache expiry tracking)
|
|
453
|
+
*/
|
|
454
|
+
async SetRunQueryResult(fingerprint, queryName, results, maxUpdatedAt, rowCount, queryId, ttlMs) {
|
|
455
|
+
if (!this._storageProvider || !this._config.enabled)
|
|
456
|
+
return;
|
|
457
|
+
const actualRowCount = rowCount ?? results.length;
|
|
458
|
+
const value = JSON.stringify({ results, maxUpdatedAt, rowCount: actualRowCount, queryId });
|
|
459
|
+
const sizeBytes = this.estimateSize(value);
|
|
460
|
+
// Check if we need to evict entries
|
|
461
|
+
await this.evictIfNeeded(sizeBytes);
|
|
462
|
+
const now = Date.now();
|
|
463
|
+
const expiresAt = ttlMs ? now + ttlMs : undefined;
|
|
464
|
+
try {
|
|
465
|
+
await this._storageProvider.SetItem(fingerprint, value, exports.CacheCategory.RunQueryCache);
|
|
466
|
+
this.registerEntry({
|
|
467
|
+
key: fingerprint,
|
|
468
|
+
type: 'runquery',
|
|
469
|
+
name: queryName,
|
|
470
|
+
fingerprint,
|
|
471
|
+
cachedAt: now,
|
|
472
|
+
lastAccessedAt: now,
|
|
473
|
+
accessCount: 1,
|
|
474
|
+
sizeBytes,
|
|
475
|
+
maxUpdatedAt,
|
|
476
|
+
rowCount: actualRowCount,
|
|
477
|
+
expiresAt
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
catch (e) {
|
|
481
|
+
(0, logging_1.LogError)(`LocalCacheManager.SetRunQueryResult failed: ${e}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Retrieves a cached RunQuery result.
|
|
486
|
+
*
|
|
487
|
+
* @param fingerprint - The cache fingerprint
|
|
488
|
+
* @returns The cached results, maxUpdatedAt, rowCount, and queryId, or null if not found
|
|
489
|
+
*/
|
|
490
|
+
async GetRunQueryResult(fingerprint) {
|
|
491
|
+
if (!this._storageProvider || !this._config.enabled)
|
|
492
|
+
return null;
|
|
493
|
+
// Check if entry has expired
|
|
494
|
+
const entry = this._registry.get(fingerprint);
|
|
495
|
+
if (entry?.expiresAt && Date.now() > entry.expiresAt) {
|
|
496
|
+
// Entry has expired, invalidate it
|
|
497
|
+
await this.InvalidateRunQueryResult(fingerprint);
|
|
498
|
+
this._stats.misses++;
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
try {
|
|
502
|
+
const value = await this._storageProvider.GetItem(fingerprint, exports.CacheCategory.RunQueryCache);
|
|
503
|
+
if (value) {
|
|
504
|
+
this.recordAccess(fingerprint);
|
|
505
|
+
this._stats.hits++;
|
|
506
|
+
const parsed = JSON.parse(value);
|
|
507
|
+
// Handle legacy entries that may not have rowCount
|
|
508
|
+
return {
|
|
509
|
+
results: parsed.results,
|
|
510
|
+
maxUpdatedAt: parsed.maxUpdatedAt,
|
|
511
|
+
rowCount: parsed.rowCount ?? parsed.results?.length ?? 0,
|
|
512
|
+
queryId: parsed.queryId
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
catch (e) {
|
|
517
|
+
(0, logging_1.LogError)(`LocalCacheManager.GetRunQueryResult failed: ${e}`);
|
|
518
|
+
}
|
|
519
|
+
this._stats.misses++;
|
|
520
|
+
return null;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Invalidates a cached RunQuery result.
|
|
524
|
+
*
|
|
525
|
+
* @param fingerprint - The cache fingerprint to invalidate
|
|
526
|
+
*/
|
|
527
|
+
async InvalidateRunQueryResult(fingerprint) {
|
|
528
|
+
if (!this._storageProvider)
|
|
529
|
+
return;
|
|
530
|
+
try {
|
|
531
|
+
await this._storageProvider.Remove(fingerprint, exports.CacheCategory.RunQueryCache);
|
|
532
|
+
this.unregisterEntry(fingerprint);
|
|
533
|
+
}
|
|
534
|
+
catch (e) {
|
|
535
|
+
(0, logging_1.LogError)(`LocalCacheManager.InvalidateRunQueryResult failed: ${e}`);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Invalidates all cached RunQuery results for a specific query.
|
|
540
|
+
* Useful when a query's underlying data changes and all related caches should be cleared.
|
|
541
|
+
*
|
|
542
|
+
* @param queryName - The query name to invalidate
|
|
543
|
+
*/
|
|
544
|
+
async InvalidateQueryCaches(queryName) {
|
|
545
|
+
if (!this._storageProvider)
|
|
546
|
+
return;
|
|
547
|
+
const normalizedName = queryName.toLowerCase().trim();
|
|
548
|
+
const toRemove = [];
|
|
549
|
+
for (const [key, entry] of this._registry.entries()) {
|
|
550
|
+
if (entry.type === 'runquery' && entry.name.toLowerCase().trim() === normalizedName) {
|
|
551
|
+
toRemove.push(key);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
for (const key of toRemove) {
|
|
555
|
+
try {
|
|
556
|
+
await this._storageProvider.Remove(key, exports.CacheCategory.RunQueryCache);
|
|
557
|
+
this._registry.delete(key);
|
|
558
|
+
}
|
|
559
|
+
catch (e) {
|
|
560
|
+
(0, logging_1.LogError)(`LocalCacheManager.InvalidateQueryCaches failed for key ${key}: ${e}`);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
await this.persistRegistry();
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Gets the cache status (fingerprint data) for a RunQuery result.
|
|
567
|
+
* Used for smart cache validation with the server.
|
|
568
|
+
*
|
|
569
|
+
* @param fingerprint - The cache fingerprint
|
|
570
|
+
* @returns The cache status with maxUpdatedAt and rowCount, or null if not found/expired
|
|
571
|
+
*/
|
|
572
|
+
async GetRunQueryCacheStatus(fingerprint) {
|
|
573
|
+
const cached = await this.GetRunQueryResult(fingerprint);
|
|
574
|
+
if (!cached)
|
|
575
|
+
return null;
|
|
576
|
+
return {
|
|
577
|
+
maxUpdatedAt: cached.maxUpdatedAt,
|
|
578
|
+
rowCount: cached.rowCount
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
// ========================================================================
|
|
582
|
+
// REGISTRY QUERIES (FOR DASHBOARD)
|
|
583
|
+
// ========================================================================
|
|
584
|
+
/**
|
|
585
|
+
* Returns all cache entries for dashboard display.
|
|
586
|
+
*/
|
|
587
|
+
GetAllEntries() {
|
|
588
|
+
return [...this._registry.values()];
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Returns cache entries filtered by type.
|
|
592
|
+
*
|
|
593
|
+
* @param type - The cache entry type to filter by
|
|
594
|
+
*/
|
|
595
|
+
GetEntriesByType(type) {
|
|
596
|
+
return this.GetAllEntries().filter(e => e.type === type);
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Returns comprehensive cache statistics.
|
|
600
|
+
*/
|
|
601
|
+
GetStats() {
|
|
602
|
+
const entries = this.GetAllEntries();
|
|
603
|
+
const byType = {
|
|
604
|
+
dataset: { count: 0, sizeBytes: 0 },
|
|
605
|
+
runview: { count: 0, sizeBytes: 0 },
|
|
606
|
+
runquery: { count: 0, sizeBytes: 0 }
|
|
607
|
+
};
|
|
608
|
+
for (const entry of entries) {
|
|
609
|
+
byType[entry.type].count++;
|
|
610
|
+
byType[entry.type].sizeBytes += entry.sizeBytes;
|
|
611
|
+
}
|
|
612
|
+
const timestamps = entries.map(e => e.cachedAt);
|
|
613
|
+
return {
|
|
614
|
+
totalEntries: entries.length,
|
|
615
|
+
totalSizeBytes: entries.reduce((sum, e) => sum + e.sizeBytes, 0),
|
|
616
|
+
byType,
|
|
617
|
+
oldestEntry: timestamps.length ? Math.min(...timestamps) : 0,
|
|
618
|
+
newestEntry: timestamps.length ? Math.max(...timestamps) : 0,
|
|
619
|
+
hits: this._stats.hits,
|
|
620
|
+
misses: this._stats.misses
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Calculates the cache hit rate as a percentage.
|
|
625
|
+
*/
|
|
626
|
+
GetHitRate() {
|
|
627
|
+
const total = this._stats.hits + this._stats.misses;
|
|
628
|
+
return total > 0 ? (this._stats.hits / total) * 100 : 0;
|
|
629
|
+
}
|
|
630
|
+
// ========================================================================
|
|
631
|
+
// BULK OPERATIONS
|
|
632
|
+
// ========================================================================
|
|
633
|
+
/**
|
|
634
|
+
* Clears all cache entries of a specific type.
|
|
635
|
+
*
|
|
636
|
+
* @param type - The cache entry type to clear
|
|
637
|
+
* @returns The number of entries cleared
|
|
638
|
+
*/
|
|
639
|
+
async ClearByType(type) {
|
|
640
|
+
if (!this._storageProvider)
|
|
641
|
+
return 0;
|
|
642
|
+
const entries = this.GetEntriesByType(type);
|
|
643
|
+
const category = this.getCategoryForType(type);
|
|
644
|
+
for (const entry of entries) {
|
|
645
|
+
try {
|
|
646
|
+
await this._storageProvider.Remove(entry.key, category);
|
|
647
|
+
if (entry.type === 'dataset') {
|
|
648
|
+
await this._storageProvider.Remove(entry.key + '_date', category);
|
|
649
|
+
}
|
|
650
|
+
this._registry.delete(entry.key);
|
|
651
|
+
}
|
|
652
|
+
catch (e) {
|
|
653
|
+
(0, logging_1.LogError)(`LocalCacheManager.ClearByType failed for key ${entry.key}: ${e}`);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
await this.persistRegistry();
|
|
657
|
+
return entries.length;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Clears all cache entries.
|
|
661
|
+
*
|
|
662
|
+
* @returns The number of entries cleared
|
|
663
|
+
*/
|
|
664
|
+
async ClearAll() {
|
|
665
|
+
if (!this._storageProvider)
|
|
666
|
+
return 0;
|
|
667
|
+
const count = this._registry.size;
|
|
668
|
+
for (const entry of this._registry.values()) {
|
|
669
|
+
try {
|
|
670
|
+
const category = this.getCategoryForType(entry.type);
|
|
671
|
+
await this._storageProvider.Remove(entry.key, category);
|
|
672
|
+
if (entry.type === 'dataset') {
|
|
673
|
+
await this._storageProvider.Remove(entry.key + '_date', category);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
catch (e) {
|
|
677
|
+
(0, logging_1.LogError)(`LocalCacheManager.ClearAll failed for key ${entry.key}: ${e}`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
this._registry.clear();
|
|
681
|
+
this._stats = { hits: 0, misses: 0 };
|
|
682
|
+
await this.persistRegistry();
|
|
683
|
+
return count;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Resets the hit/miss statistics.
|
|
687
|
+
*/
|
|
688
|
+
ResetStats() {
|
|
689
|
+
this._stats = { hits: 0, misses: 0 };
|
|
690
|
+
}
|
|
691
|
+
// ========================================================================
|
|
692
|
+
// INTERNAL HELPERS
|
|
693
|
+
// ========================================================================
|
|
694
|
+
/**
|
|
695
|
+
* Maps a cache entry type to its storage category.
|
|
696
|
+
*/
|
|
697
|
+
getCategoryForType(type) {
|
|
698
|
+
switch (type) {
|
|
699
|
+
case 'runview':
|
|
700
|
+
return exports.CacheCategory.RunViewCache;
|
|
701
|
+
case 'runquery':
|
|
702
|
+
return exports.CacheCategory.RunQueryCache;
|
|
703
|
+
case 'dataset':
|
|
704
|
+
return exports.CacheCategory.DatasetCache;
|
|
705
|
+
default:
|
|
706
|
+
return exports.CacheCategory.Default;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Builds a cache key for a dataset.
|
|
711
|
+
*/
|
|
712
|
+
buildDatasetKey(name, itemFilters, keyPrefix) {
|
|
713
|
+
const filterKey = itemFilters
|
|
714
|
+
? '{' + itemFilters.map(f => `"${f.ItemCode}":"${f.Filter}"`).join(',') + '}'
|
|
715
|
+
: '';
|
|
716
|
+
return keyPrefix + '__DATASET__' + name + filterKey;
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Registers a cache entry in the registry.
|
|
720
|
+
*/
|
|
721
|
+
registerEntry(entry) {
|
|
722
|
+
this._registry.set(entry.key, entry);
|
|
723
|
+
// Debounce registry persistence to avoid too many writes
|
|
724
|
+
this.debouncedPersistRegistry();
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Unregisters a cache entry from the registry.
|
|
728
|
+
*/
|
|
729
|
+
unregisterEntry(key) {
|
|
730
|
+
this._registry.delete(key);
|
|
731
|
+
this.debouncedPersistRegistry();
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Records an access to a cache entry (updates lastAccessedAt and accessCount).
|
|
735
|
+
*/
|
|
736
|
+
recordAccess(key) {
|
|
737
|
+
const entry = this._registry.get(key);
|
|
738
|
+
if (entry) {
|
|
739
|
+
entry.lastAccessedAt = Date.now();
|
|
740
|
+
entry.accessCount++;
|
|
741
|
+
// Don't persist on every access - too expensive
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Loads the registry from storage.
|
|
746
|
+
*/
|
|
747
|
+
async loadRegistry() {
|
|
748
|
+
if (!this._storageProvider)
|
|
749
|
+
return;
|
|
750
|
+
try {
|
|
751
|
+
const stored = await this._storageProvider.GetItem(this.REGISTRY_KEY, exports.CacheCategory.Metadata);
|
|
752
|
+
if (stored) {
|
|
753
|
+
const parsed = JSON.parse(stored);
|
|
754
|
+
this._registry = new Map(parsed.map(e => [e.key, e]));
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
catch (e) {
|
|
758
|
+
this._registry.clear();
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Debounced registry persistence to avoid too many writes.
|
|
763
|
+
*/
|
|
764
|
+
debouncedPersistRegistry() {
|
|
765
|
+
if (this._persistTimeout) {
|
|
766
|
+
clearTimeout(this._persistTimeout);
|
|
767
|
+
}
|
|
768
|
+
this._persistTimeout = setTimeout(() => {
|
|
769
|
+
this.persistRegistry();
|
|
770
|
+
}, 1000); // 1 second debounce
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Persists the registry to storage.
|
|
774
|
+
*/
|
|
775
|
+
async persistRegistry() {
|
|
776
|
+
if (!this._storageProvider)
|
|
777
|
+
return;
|
|
778
|
+
try {
|
|
779
|
+
const data = JSON.stringify(this.GetAllEntries());
|
|
780
|
+
await this._storageProvider.SetItem(this.REGISTRY_KEY, data, exports.CacheCategory.Metadata);
|
|
781
|
+
}
|
|
782
|
+
catch (e) {
|
|
783
|
+
// Ignore persistence errors - cache is still functional
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Estimates the size of a string in bytes.
|
|
788
|
+
*/
|
|
789
|
+
estimateSize(value) {
|
|
790
|
+
// Approximate size: UTF-16 strings are ~2 bytes per character
|
|
791
|
+
return value.length * 2;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Evicts entries if needed to make room for new data.
|
|
795
|
+
*/
|
|
796
|
+
async evictIfNeeded(neededBytes) {
|
|
797
|
+
if (!this._storageProvider)
|
|
798
|
+
return;
|
|
799
|
+
const stats = this.GetStats();
|
|
800
|
+
const wouldExceedSize = (stats.totalSizeBytes + neededBytes) > this._config.maxSizeBytes;
|
|
801
|
+
const wouldExceedCount = stats.totalEntries >= this._config.maxEntries;
|
|
802
|
+
if (!wouldExceedSize && !wouldExceedCount)
|
|
803
|
+
return;
|
|
804
|
+
// Calculate how much to free
|
|
805
|
+
const targetFreeBytes = Math.max(neededBytes, this._config.maxSizeBytes * 0.1); // At least 10% of max
|
|
806
|
+
const targetFreeCount = Math.max(1, Math.floor(this._config.maxEntries * 0.1)); // At least 10% of max
|
|
807
|
+
await this.evict(targetFreeBytes, targetFreeCount);
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Evicts entries based on the configured eviction policy.
|
|
811
|
+
*/
|
|
812
|
+
async evict(targetBytes, targetCount) {
|
|
813
|
+
if (!this._storageProvider)
|
|
814
|
+
return;
|
|
815
|
+
const entries = this.GetAllEntries();
|
|
816
|
+
// Sort by eviction policy
|
|
817
|
+
switch (this._config.evictionPolicy) {
|
|
818
|
+
case 'lru':
|
|
819
|
+
entries.sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);
|
|
820
|
+
break;
|
|
821
|
+
case 'lfu':
|
|
822
|
+
entries.sort((a, b) => a.accessCount - b.accessCount);
|
|
823
|
+
break;
|
|
824
|
+
case 'fifo':
|
|
825
|
+
entries.sort((a, b) => a.cachedAt - b.cachedAt);
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
let freedBytes = 0;
|
|
829
|
+
let freedCount = 0;
|
|
830
|
+
const toDelete = [];
|
|
831
|
+
for (const entry of entries) {
|
|
832
|
+
if (freedBytes >= targetBytes && freedCount >= targetCount)
|
|
833
|
+
break;
|
|
834
|
+
toDelete.push(entry.key);
|
|
835
|
+
freedBytes += entry.sizeBytes;
|
|
836
|
+
freedCount++;
|
|
837
|
+
}
|
|
838
|
+
for (const key of toDelete) {
|
|
839
|
+
try {
|
|
840
|
+
const entry = this._registry.get(key);
|
|
841
|
+
const category = this.getCategoryForType(entry?.type);
|
|
842
|
+
await this._storageProvider.Remove(key, category);
|
|
843
|
+
if (entry?.type === 'dataset') {
|
|
844
|
+
await this._storageProvider.Remove(key + '_date', category);
|
|
845
|
+
}
|
|
846
|
+
this._registry.delete(key);
|
|
847
|
+
}
|
|
848
|
+
catch (e) {
|
|
849
|
+
// Continue evicting other entries
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
await this.persistRegistry();
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
exports.LocalCacheManager = LocalCacheManager;
|
|
856
|
+
//# sourceMappingURL=localCacheManager.js.map
|