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

albe / node-event-storage / 23896307326

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

Pull #257

github

web-flow
Merge 1b4959036 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.6 hits per line

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

97.74
/src/util.js
1
import fs from 'fs';
4✔
2
import { mkdirpSync } from 'mkdirp';
4✔
3

4✔
4
/**
4✔
5
 * Assert that actual and expected match or throw an Error with the given message appended by information about expected and actual value.
4✔
6
 *
4✔
7
 * @param {*} actual
4✔
8
 * @param {*} expected
4✔
9
 * @param {string} message
4✔
10
 */
4✔
11
function assertEqual(actual, expected, message) {
16,572✔
12
    if (actual !== expected) {
16,572✔
13
        throw new Error(message + (message ? ' ' : '') + `Expected "${expected}" but got "${actual}".`);
8!
14
    }
8✔
15
}
16,572✔
16

4✔
17
/**
4✔
18
 * Assert that the condition holds and if not, throw an error with the given message.
4✔
19
 *
4✔
20
 * @param {boolean} condition
4✔
21
 * @param {string} message
4✔
22
 * @param {typeof Error} ErrorType
4✔
23
 */
4✔
24
function assert(condition, message, ErrorType = Error) {
95,552✔
25
    if (!condition) {
95,552✔
26
        throw new ErrorType(message);
192✔
27
    }
192✔
28
}
95,552✔
29

4✔
30
/**
4✔
31
 * Return the amount required to align value to the given alignment.
4✔
32
 * It calculates the difference of the alignment and the modulo of value by alignment.
4✔
33
 * @param {number} value
4✔
34
 * @param {number} alignment
4✔
35
 * @returns {number}
4✔
36
 */
4✔
37
function alignTo(value, alignment) {
36,903✔
38
    return (alignment - (value % alignment)) % alignment;
36,903✔
39
}
36,903✔
40

4✔
41
/**
4✔
42
 * Method for hashing a string (e.g. a partition name) to a 32-bit unsigned integer.
4✔
43
 *
4✔
44
 * @param {string} str
4✔
45
 * @returns {number}
4✔
46
 */
4✔
47
function hash(str) {
4,420✔
48
    /* istanbul ignore if */
4,420✔
49
    if (str.length === 0) {
4,420!
UNCOV
50
        return 0;
×
UNCOV
51
    }
×
52
    let hash = 5381,
4,420✔
53
        i    = str.length;
4,420✔
54

4,420✔
55
    while(i) {
4,420✔
56
        hash = ((hash << 5) + hash) ^ str.charCodeAt(--i); // jshint ignore:line
51,140✔
57
    }
51,140✔
58

4,420✔
59
    /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed
4,420✔
60
     * integers. Since we want the results to be always positive, convert the
4,420✔
61
     * signed int to an unsigned by doing an unsigned bitshift. */
4,420✔
62
    return hash >>> 0; // jshint ignore:line
4,420✔
63
}
4,420✔
64

4✔
65
/**
4✔
66
 * Build a buffer containing the file magic header and a JSON stringified metadata block, padded to be a multiple of 16 bytes long.
4✔
67
 *
4✔
68
 * @param {string} magic
4✔
69
 * @param {object} metadata
4✔
70
 * @returns {Buffer} A buffer containing the header data
4✔
71
 */
4✔
72
function buildMetadataHeader(magic, metadata) {
3,064✔
73
    assertEqual(magic.length, 8, 'The header magic bytes length is wrong.');
3,064✔
74
    let metadataString = JSON.stringify(metadata);
3,064✔
75
    let metadataSize = Buffer.byteLength(metadataString, 'utf8');
3,064✔
76
    // 8 byte MAGIC, 4 byte metadata size, 1 byte line break
3,064✔
77
    const pad = (16 - ((8 + 4 + metadataSize + 1) % 16)) % 16;
3,064✔
78
    metadataString += ' '.repeat(pad) + "\n";
3,064✔
79
    metadataSize += pad + 1;
3,064✔
80
    const metadataBuffer = Buffer.allocUnsafe(8 + 4 + metadataSize);
3,064✔
81
    metadataBuffer.write(magic, 0, 8, 'utf8');
3,064✔
82
    metadataBuffer.writeUInt32BE(metadataSize, 8);
3,064✔
83
    metadataBuffer.write(metadataString, 8 + 4, metadataSize, 'utf8');
3,064✔
84
    return metadataBuffer;
3,064✔
85
}
3,064✔
86

4✔
87
/**
4✔
88
 * Do a binary search for number in the range 1-length with values retrieved via a provided getter.
4✔
89
 *
4✔
90
 * @param {number} number The value to search for
4✔
91
 * @param {number} length The upper position to search up to
4✔
92
 * @param {function(number)} get The getter function to retrieve the values at the specific position
4✔
93
 * @returns {Array<number>} An array of the low and high position that match the searched number
4✔
94
 */
4✔
95
function binarySearch(number, length, get) {
604✔
96
    let low = 1;
604✔
97
    let high = length;
604✔
98

604✔
99
    if (get(low) > number) {
604✔
100
        return [low, 0];
152✔
101
    }
152✔
102
    if (get(high) < number) {
604✔
103
        return [0, high];
144✔
104
    }
144✔
105

308✔
106
    while (low <= high) {
604✔
107
        const mid = low + ((high - low) >> 1);
792✔
108
        const value = get(mid);
792✔
109
        if (value === number) {
792✔
110
            return [mid, mid];
260✔
111
        }
260✔
112
        if (value < number) {
792✔
113
            low = mid + 1;
320✔
114
        } else {
792✔
115
            high = mid - 1;
212✔
116
        }
212✔
117
    }
792✔
118
    return [low, high];
48✔
119
}
604✔
120

4✔
121
/**
4✔
122
 * @param {number} index The 1-based index position to wrap around if < 0 and check against the bounds.
4✔
123
 * @param {number} length The length of the index and upper bound.
4✔
124
 * @returns {number} The wrapped index position or -1 if index out of bounds.
4✔
125
 */
4✔
126
function wrapAndCheck(index, length) {
4,647✔
127
    if (typeof index !== 'number') {
4,647✔
128
        return -1;
4✔
129
    }
4✔
130

4,643✔
131
    if (index < 0) {
4,647✔
132
        index += length + 1;
108✔
133
    }
108✔
134
    if (index < 1 || index > length) {
4,647✔
135
        return -1;
72✔
136
    }
72✔
137
    return index;
4,571✔
138
}
4,647✔
139

4✔
140
/**
4✔
141
 * Ensure that the given directory exists.
4✔
142
 * @param {string} dirName
4✔
143
 * @return {boolean} true if the directory existed already
4✔
144
 */
4✔
145
function ensureDirectory(dirName) {
4,504✔
146
    if (!fs.existsSync(dirName)) {
4,504✔
147
        try {
472✔
148
            mkdirpSync(dirName);
472✔
149
        } catch (e) {
472!
UNCOV
150
        }
×
151
        return false;
472✔
152
    }
472✔
153
    return true;
4,032✔
154
}
4,504✔
155

4✔
156
/**
4✔
157
 * Perform a k-way merge over multiple streams, invoking a callback for each item in ascending key order.
4✔
158
 * Each stream object is mutated in place by the `advance` function.
4✔
159
 *
4✔
160
 * @param {object[]} streams Array of stream state objects; entries are removed when exhausted.
4✔
161
 * @param {function(object): number} getKey Returns the current sort key for a stream state.
4✔
162
 * @param {function(object): boolean} advance Advances the stream to its next item.
4✔
163
 *   Returns true if the stream has more items within range, false if exhausted.
4✔
164
 * @param {function(object): void} visit Called for each stream state in merged order.
4✔
165
 */
4✔
166
function kWayMerge(streams, getKey, advance, visit) {
64✔
167
    while (streams.length > 0) {
64✔
168
        let minIdx = 0;
228✔
169
        for (let i = 1; i < streams.length; i++) {
228✔
170
            if (getKey(streams[i]) < getKey(streams[minIdx])) {
212✔
171
                minIdx = i;
104✔
172
            }
104✔
173
        }
212✔
174
        visit(streams[minIdx]);
228✔
175
        if (!advance(streams[minIdx])) {
228✔
176
            streams.splice(minIdx, 1);
92✔
177
        }
92✔
178
    }
228✔
179
}
64✔
180

4✔
181
/**
4✔
182
 * Scan a directory for files whose names match a regex pattern, calling a callback for each match.
4✔
183
 * The `onEach` callback receives the first capturing group of the match (`match[1]`), or the full
4✔
184
 * match (`match[0]`) when no capturing group is defined in the pattern.
4✔
185
 *
4✔
186
 * @param {string} directory The directory to scan.
4✔
187
 * @param {RegExp} regexPattern The pattern to match file names against.
4✔
188
 * @param {function(string)} onEach Called with the first capturing group (or full match) for each matching file name.
4✔
189
 * @param {function(Error?)} onDone Called when the scan is complete, or with an error if one occurred.
4✔
190
 */
4✔
191
function scanForFiles(directory, regexPattern, onEach, onDone) {
424✔
192
    fs.readdir(directory, (err, files) => {
424✔
193
        if (err) {
424✔
194
            return onDone(err);
8✔
195
        }
8✔
196
        let match;
416✔
197
        for (let file of files) {
424✔
198
            if ((match = file.match(regexPattern)) !== null) {
498✔
199
                onEach(match[1] !== undefined ? match[1] : match[0]);
90✔
200
            }
90✔
201
        }
498✔
202
        onDone(null);
416✔
203
    });
424✔
204
}
424✔
205

4✔
206

4✔
207
export {
4✔
208
    assert,
4✔
209
    assertEqual,
4✔
210
    hash,
4✔
211
    wrapAndCheck,
4✔
212
    binarySearch,
4✔
213
    buildMetadataHeader,
4✔
214
    alignTo,
4✔
215
    ensureDirectory,
4✔
216
    scanForFiles,
4✔
217
    kWayMerge
4✔
218
};
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