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

albe / node-event-storage / 27159296142

08 Jun 2026 06:43PM UTC coverage: 98.787% (-0.06%) from 98.851%
27159296142

push

github

albe
Bump version

1338 of 1374 branches covered (97.38%)

Branch coverage included in aggregate %.

6560 of 6621 relevant lines covered (99.08%)

812.79 hits per line

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

97.06
/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);
116✔
16
        this.storageFilesFilter = this.storageFilesFilter.bind(this);
116✔
17
        this.onStorageFileChanged = this.onStorageFileChanged.bind(this);
116✔
18
    }
116✔
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.endsWith('.branch') && filename.substring(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
     * @param {function(): void} [callback] Called after indexes open, before `'opened'` is emitted.
4✔
35
     *   Can be used as a synchronous alternative to listening to the `'opened'` event.
4✔
36
     * @returns {boolean}
4✔
37
     */
4✔
38
    open(callback) {
4✔
39
        if (!this.watcher) {
116✔
40
            this.watcher = new Watcher([this.dataDirectory, this.indexDirectory], this.storageFilesFilter);
112✔
41
            this.watcher.on('rename', this.onStorageFileChanged);
112✔
42
        }
112✔
43
        return super.open(callback);
116✔
44
    }
116✔
45

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

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

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

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

4✔
118
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