0
# Storage System
1
2
Storage system provides pluggable backends for coverage data with memory, filesystem, and temporary storage options. The Store system enables flexible data persistence during coverage collection and processing.
3
4
## Capabilities
5
6
### Store Factory
7
8
Factory class for creating and registering storage implementations.
9
10
```javascript { .api }
11
/**
12
* Store factory and base class
13
*/
14
class Store {
15
/**
16
* Creates a store of the specified type
17
* @param {string} type - Store type ('memory', 'fslookup', 'tmp')
18
* @param {Object} opts - Store-specific options
19
* @returns {Store} Store instance
20
*/
21
static create(type: string, opts?: Object): Store;
22
23
/**
24
* Registers a new store implementation
25
* @param {Function} constructor - Store constructor function
26
*/
27
static register(constructor: Function): void;
28
}
29
```
30
31
### Store Interface
32
33
All store implementations provide a consistent interface for data persistence.
34
35
```javascript { .api }
36
interface Store {
37
/**
38
* Sets content for a key
39
* @param {string} key - Storage key
40
* @param {string} contents - Content to store
41
*/
42
set(key: string, contents: string): void;
43
44
/**
45
* Gets content for a key
46
* @param {string} key - Storage key
47
* @returns {string} Stored content
48
*/
49
get(key: string): string;
50
51
/**
52
* Returns array of all known keys
53
* @returns {string[]} Array of keys
54
*/
55
keys(): string[];
56
57
/**
58
* Checks if a key exists in storage
59
* @param {string} key - Storage key
60
* @returns {boolean} True if key exists
61
*/
62
hasKey(key: string): boolean;
63
64
/**
65
* Disposes store and reclaims temporary resources
66
*/
67
dispose(): void;
68
69
/**
70
* Gets JSON object for a key
71
* @param {string} key - Storage key
72
* @returns {Object} Parsed JSON object
73
*/
74
getObject(key: string): Object;
75
76
/**
77
* Sets JSON object for a key
78
* @param {string} key - Storage key
79
* @param {Object} object - Object to serialize and store
80
*/
81
setObject(key: string, object: Object): void;
82
}
83
```
84
85
**Usage Examples:**
86
87
```javascript
88
const { Store } = require('istanbul');
89
90
// Create different store types
91
const memoryStore = Store.create('memory');
92
const fsStore = Store.create('fslookup', {
93
dir: './coverage-data'
94
});
95
const tmpStore = Store.create('tmp');
96
97
// Basic operations
98
memoryStore.set('coverage-1', JSON.stringify(coverageData));
99
const retrieved = JSON.parse(memoryStore.get('coverage-1'));
100
101
// Object operations (automatic JSON handling)
102
memoryStore.setObject('coverage-2', coverageData);
103
const coverageObj = memoryStore.getObject('coverage-2');
104
105
// Key management
106
console.log('All keys:', memoryStore.keys());
107
console.log('Has coverage-1:', memoryStore.hasKey('coverage-1'));
108
109
// Cleanup
110
memoryStore.dispose();
111
```
112
113
### Built-in Store Types
114
115
#### Memory Store
116
117
In-memory storage for coverage data with fast access and automatic cleanup.
118
119
```javascript { .api }
120
/**
121
* In-memory store implementation
122
*/
123
class MemoryStore extends Store {
124
constructor(opts?: MemoryStoreOptions);
125
}
126
127
interface MemoryStoreOptions {
128
/** No specific options for memory store */
129
}
130
```
131
132
**Usage Examples:**
133
134
```javascript
135
// Create memory store
136
const memoryStore = Store.create('memory');
137
138
// Store coverage data from multiple files
139
memoryStore.setObject('file1.js', {
140
path: 'file1.js',
141
s: { '1': 1, '2': 0 },
142
b: { '1': [1, 0] },
143
f: { '1': 1 }
144
});
145
146
memoryStore.setObject('file2.js', {
147
path: 'file2.js',
148
s: { '1': 2, '2': 1 },
149
b: { '1': [2, 1] },
150
f: { '1': 2 }
151
});
152
153
// Retrieve and process
154
const allKeys = memoryStore.keys();
155
allKeys.forEach(key => {
156
const coverage = memoryStore.getObject(key);
157
console.log(`Coverage for ${key}:`, coverage);
158
});
159
160
// Memory stores are automatically cleaned up
161
memoryStore.dispose();
162
```
163
164
#### Filesystem Lookup Store
165
166
File system-based storage for persistent coverage data with directory organization.
167
168
```javascript { .api }
169
/**
170
* Filesystem-based store implementation
171
*/
172
class FsLookupStore extends Store {
173
constructor(opts: FsStoreOptions);
174
}
175
176
interface FsStoreOptions {
177
/** Directory to store files */
178
dir: string;
179
180
/** Whether to create directory if it doesn't exist */
181
create?: boolean;
182
}
183
```
184
185
**Usage Examples:**
186
187
```javascript
188
const path = require('path');
189
190
// Create filesystem store
191
const fsStore = Store.create('fslookup', {
192
dir: path.resolve('./coverage-store'),
193
create: true
194
});
195
196
// Store coverage data (persisted to disk)
197
fsStore.setObject('src/app.js', {
198
path: 'src/app.js',
199
s: { '1': 5, '2': 3, '3': 1 },
200
b: { '1': [5, 2], '2': [3, 0] },
201
f: { '1': 5, '2': 3 }
202
});
203
204
// Data persists across program restarts
205
const storedCoverage = fsStore.getObject('src/app.js');
206
console.log('Persisted coverage:', storedCoverage);
207
208
// Cleanup removes temporary files
209
fsStore.dispose();
210
```
211
212
#### Temporary Store
213
214
Temporary directory storage with automatic cleanup for intermediate processing.
215
216
```javascript { .api }
217
/**
218
* Temporary directory store implementation
219
*/
220
class TmpStore extends Store {
221
constructor(opts?: TmpStoreOptions);
222
}
223
224
interface TmpStoreOptions {
225
/** Prefix for temporary directory name */
226
prefix?: string;
227
}
228
```
229
230
**Usage Examples:**
231
232
```javascript
233
const os = require('os');
234
235
// Create temporary store
236
const tmpStore = Store.create('tmp', {
237
prefix: 'istanbul-coverage-'
238
});
239
240
// Store data in temporary location
241
tmpStore.set('intermediate-data', JSON.stringify({
242
processed: true,
243
timestamp: Date.now()
244
}));
245
246
// Temporary directory location
247
console.log('Temp store keys:', tmpStore.keys());
248
249
// Automatic cleanup on dispose
250
tmpStore.dispose(); // Removes temporary directory and all files
251
```
252
253
### Store Usage Patterns
254
255
#### With Collector
256
257
Stores are commonly used with Collector for large datasets:
258
259
```javascript
260
const { Collector, Store } = require('istanbul');
261
262
// Use filesystem store for large coverage collection
263
const collector = new Collector({
264
store: Store.create('fslookup', {
265
dir: './coverage-temp',
266
create: true
267
})
268
});
269
270
// Add multiple coverage objects
271
collector.add(coverage1);
272
collector.add(coverage2);
273
collector.add(coverage3);
274
275
// Store handles persistence automatically
276
const finalCoverage = collector.getFinalCoverage();
277
278
// Cleanup
279
collector.dispose(); // Also disposes the store
280
```
281
282
#### Batch Processing
283
284
Use stores for batch processing large coverage datasets:
285
286
```javascript
287
async function processCoverageFiles(coverageFiles) {
288
const store = Store.create('tmp', { prefix: 'batch-coverage-' });
289
290
try {
291
// Store all coverage data
292
for (let i = 0; i < coverageFiles.length; i++) {
293
const coverage = require(coverageFiles[i]);
294
store.setObject(`batch-${i}`, coverage);
295
}
296
297
// Process stored data
298
const allKeys = store.keys();
299
const processedResults = allKeys.map(key => {
300
const coverage = store.getObject(key);
301
return processCoverage(coverage);
302
});
303
304
return processedResults;
305
} finally {
306
// Always cleanup
307
store.dispose();
308
}
309
}
310
```
311
312
### Custom Store Implementation
313
314
Create custom store implementations for specialized storage needs:
315
316
```javascript
317
class DatabaseStore extends Store {
318
constructor(opts) {
319
super();
320
this.db = opts.database;
321
this.table = opts.table || 'coverage_data';
322
}
323
324
set(key, contents) {
325
this.db.query(
326
`INSERT OR REPLACE INTO ${this.table} (key, contents) VALUES (?, ?)`,
327
[key, contents]
328
);
329
}
330
331
get(key) {
332
const result = this.db.query(
333
`SELECT contents FROM ${this.table} WHERE key = ?`,
334
[key]
335
);
336
return result.length > 0 ? result[0].contents : null;
337
}
338
339
keys() {
340
const results = this.db.query(`SELECT key FROM ${this.table}`);
341
return results.map(row => row.key);
342
}
343
344
hasKey(key) {
345
const result = this.db.query(
346
`SELECT 1 FROM ${this.table} WHERE key = ? LIMIT 1`,
347
[key]
348
);
349
return result.length > 0;
350
}
351
352
dispose() {
353
// Clean up database resources
354
if (this.db) {
355
this.db.close();
356
}
357
}
358
}
359
360
// Register custom store
361
Store.register(DatabaseStore);
362
363
// Use custom store
364
const dbStore = Store.create('DatabaseStore', {
365
database: myDatabase,
366
table: 'istanbul_coverage'
367
});
368
```
369
370
### Error Handling
371
372
Handle storage errors appropriately:
373
374
```javascript
375
const store = Store.create('fslookup', { dir: './coverage-data' });
376
377
try {
378
store.setObject('test-coverage', coverageData);
379
} catch (error) {
380
if (error.code === 'ENOENT') {
381
console.error('Storage directory does not exist');
382
} else if (error.code === 'EACCES') {
383
console.error('Permission denied writing to storage');
384
} else {
385
console.error('Storage error:', error.message);
386
}
387
}
388
389
// Safe retrieval
390
function safeCoverageGet(store, key) {
391
try {
392
if (store.hasKey(key)) {
393
return store.getObject(key);
394
}
395
return null;
396
} catch (error) {
397
console.warn(`Failed to retrieve coverage for ${key}:`, error.message);
398
return null;
399
}
400
}
401
```
402
403
### Performance Considerations
404
405
Choose appropriate store types based on usage patterns:
406
407
```javascript
408
// For small datasets or short-lived processes
409
const quickStore = Store.create('memory');
410
411
// For large datasets that need persistence
412
const persistentStore = Store.create('fslookup', {
413
dir: './coverage-cache'
414
});
415
416
// For intermediate processing with automatic cleanup
417
const tempStore = Store.create('tmp');
418
419
// Monitor store performance
420
function benchmarkStore(store, operations = 1000) {
421
const start = Date.now();
422
423
for (let i = 0; i < operations; i++) {
424
store.set(`key-${i}`, `value-${i}`);
425
}
426
427
for (let i = 0; i < operations; i++) {
428
store.get(`key-${i}`);
429
}
430
431
const elapsed = Date.now() - start;
432
console.log(`${operations} operations took ${elapsed}ms`);
433
434
store.dispose();
435
}
436
```
437
438
### Store Lifecycle Management
439
440
Proper lifecycle management for stores:
441
442
```javascript
443
class CoverageProcessor {
444
constructor(storeType = 'memory', storeOpts = {}) {
445
this.store = Store.create(storeType, storeOpts);
446
this.disposed = false;
447
}
448
449
addCoverage(key, coverage) {
450
if (this.disposed) {
451
throw new Error('Cannot use disposed store');
452
}
453
this.store.setObject(key, coverage);
454
}
455
456
getCoverage(key) {
457
if (this.disposed) {
458
throw new Error('Cannot use disposed store');
459
}
460
return this.store.getObject(key);
461
}
462
463
getAllCoverage() {
464
if (this.disposed) {
465
throw new Error('Cannot use disposed store');
466
}
467
468
const coverage = {};
469
this.store.keys().forEach(key => {
470
coverage[key] = this.store.getObject(key);
471
});
472
return coverage;
473
}
474
475
dispose() {
476
if (!this.disposed) {
477
this.store.dispose();
478
this.disposed = true;
479
}
480
}
481
}
482
483
// Usage with proper cleanup
484
const processor = new CoverageProcessor('tmp');
485
try {
486
processor.addCoverage('file1.js', coverage1);
487
processor.addCoverage('file2.js', coverage2);
488
489
const allCoverage = processor.getAllCoverage();
490
// Process coverage...
491
492
} finally {
493
processor.dispose(); // Always cleanup
494
}
495
```