• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

albe / node-event-storage / 23897068196

02 Apr 2026 10:56AM UTC coverage: 98.151% (+0.1%) from 98.054%
23897068196

Pull #257

github

web-flow
Merge b52671064 into 50d3642f2
Pull Request #257: Move to ES Modules (ESM)

890 of 929 branches covered (95.8%)

Branch coverage included in aggregate %.

118 of 118 new or added lines in 21 files covered. (100.0%)

55 existing lines in 12 files now uncovered.

4684 of 4750 relevant lines covered (98.61%)

787.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.78
/src/Storage/ReadOnlyStorage.js
1
import ReadableStorage from './ReadableStorage.js';
4✔
2
import ReadablePartition from '../Partition/ReadablePartition.js';
4✔
3
import Watcher from '../Watcher.js';
4✔
4

4✔
5
/**
4✔
6
 * An append-only storage with highly performant positional range scans.
4✔
7
 * It's highly optimized for an event-store and hence does not support compaction or data-rewrite, nor any querying
4✔
8
 */
4✔
9
class ReadOnlyStorage extends ReadableStorage {
4✔
10

4✔
11
    /**
4✔
12
     * @inheritdoc
4✔
13
     */
4✔
14
    constructor(storageName = 'storage', config = {}) {
4✔
15
        super(storageName, config);
96✔
16
        this.storageFilesFilter = this.storageFilesFilter.bind(this);
96✔
17
        this.onStorageFileChanged = this.onStorageFileChanged.bind(this);
96✔
18
    }
96✔
19

4✔
20
    /**
4✔
21
     * Returns true if the given filename belongs to this storage.
4✔
22
     * @param {string} filename
4✔
23
     * @returns {boolean}
4✔
24
     */
4✔
25
    storageFilesFilter(filename) {
4✔
26
        return filename.substr(-7) !== '.branch' && filename.substr(0, this.storageFile.length) === this.storageFile;
48✔
27
    }
48✔
28

4✔
29
    /**
4✔
30
     * Open the storage and indexes and create read and write buffers eagerly.
4✔
31
     * Will emit an 'opened' event if finished.
4✔
32
     *
4✔
33
     * @api
4✔
34
     * @returns {boolean}
4✔
35
     */
4✔
36
    open() {
4✔
37
        if (!this.watcher) {
96✔
38
            this.watcher = new Watcher([this.dataDirectory, this.indexDirectory], this.storageFilesFilter);
92✔
39
            this.watcher.on('rename', this.onStorageFileChanged);
92✔
40
        }
92✔
41
        return super.open();
96✔
42
    }
96✔
43

4✔
44
    /**
4✔
45
     * @private
4✔
46
     * @param {string} filename
4✔
47
     */
4✔
48
    onStorageFileChanged(filename) {
4✔
49
        if (filename.substr(-6) === '.index') {
28✔
50
            const indexName = filename.substr(this.storageFile.length + 1, filename.length - this.storageFile.length - 7);
20✔
51
            // New indexes are not automatically opened in the reader
20✔
52
            this.emit('index-created', indexName);
20✔
53
            return;
20✔
54
        }
20✔
55

8✔
56
        const partitionId = ReadablePartition.idFor(filename);
8✔
57
        if (!this.partitions[partitionId]) {
8✔
58
            const partition = this.createPartition(filename, this.partitionConfig);
8✔
59
            this.partitions[partition.id] = partition;
8✔
60
            this.emit('partition-created', partition.id);
8✔
61
        }
8✔
62
    }
28✔
63

4✔
64
    /**
4✔
65
     * Close the storage and frees up all resources.
4✔
66
     * Will emit a 'closed' event when finished.
4✔
67
     *
4✔
68
     * @api
4✔
69
     * @returns void
4✔
70
     */
4✔
71
    close() {
4✔
72
        if (this.watcher) {
148✔
73
            this.watcher.close();
92✔
74
            this.watcher = null;
92✔
75
        }
92✔
76
        super.close();
148✔
77
    }
148✔
78

4✔
79
    /**
4✔
80
     * @protected
4✔
81
     * @param {string} name
4✔
82
     * @param {object} [options]
4✔
83
     * @returns {{ index: ReadableIndex, matcher?: object|function }}
4✔
84
     */
4✔
85
    createIndex(name, options = {}) {
4✔
86
        const { index } = super.createIndex(name, options);
136✔
87
        const indexShortName = name.replace(this.storageFile + '.', '').replace('.index', '');
136✔
88
        index.on('append', (prevLength, newLength) => {
136✔
89
            if (!this.watcher) {
12!
UNCOV
90
                // If the watcher has been removed, this means this storage was closed and we don't want to handle events any more
×
91
                return;
×
UNCOV
92
            }
×
93
            const entries = index.range(prevLength + 1, newLength);
12✔
94
            /* istanbul ignore if */
12✔
95
            if (entries === false) {
12!
UNCOV
96
                return;
×
UNCOV
97
            }
×
98
            for (let entry of entries) {
12✔
99
                const document = this.readFrom(entry.partition, entry.position, entry.size);
12✔
100
                if (index === this.index) {
12✔
101
                    this.emit('wrote', document, entry, entry.position);
8✔
102
                } else {
12✔
103
                    this.emit('index-add', indexShortName, entry.number, document);
4✔
104
                }
4✔
105
            }
12✔
106
        });
136✔
107
        index.on('truncate', (prevLength, newLength) => {
136✔
108
            if (index === this.index) {
8✔
109
                this.emit('truncate', prevLength, newLength);
8✔
110
            }
8✔
111
        });
136✔
112
        return { index };
136✔
113
    }
136✔
114
}
4✔
115

4✔
116
export default ReadOnlyStorage;
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc