event-storage 1.1.0 → 1.3.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/README.md +49 -4
- package/index.js +1 -0
- package/package.json +4 -5
- package/src/Consumer.js +16 -20
- package/src/EventStore.js +176 -118
- package/src/EventStream.js +56 -38
- package/src/Index/ReadOnlyIndex.js +1 -1
- package/src/Index/ReadableIndex.js +9 -9
- package/src/Index/WritableIndex.js +6 -10
- package/src/IndexMatcher.js +2 -2
- package/src/JoinEventStream.js +33 -59
- package/src/Partition/ReadOnlyPartition.js +1 -1
- package/src/Partition/ReadablePartition.js +158 -90
- package/src/Partition/WritablePartition.js +38 -29
- package/src/Storage/ReadOnlyStorage.js +4 -4
- package/src/Storage/ReadableStorage.js +81 -113
- package/src/Storage/WritableStorage.js +52 -37
- package/src/Watcher.js +1 -1
- package/src/utils/apiHelpers.js +123 -0
- package/src/{fsUtil.js → utils/fsUtil.js} +27 -23
- package/src/utils/jsonUtil.js +302 -0
- package/src/utils/metadataUtil.js +517 -0
- package/src/{util.js → utils/util.js} +69 -31
- package/src/metadataUtil.js +0 -126
package/src/EventStream.js
CHANGED
|
@@ -1,26 +1,9 @@
|
|
|
1
1
|
import stream from 'stream';
|
|
2
|
-
import { assert } from './util.js';
|
|
2
|
+
import { assert } from './utils/util.js';
|
|
3
|
+
import { buildRawBufferMatcher, matches } from './utils/metadataUtil.js';
|
|
4
|
+
import { normalizeRevision, normalizeMaxRevision } from './utils/apiHelpers.js';
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
* Calculate the actual version number from a possibly relative (negative) version number.
|
|
6
|
-
*
|
|
7
|
-
* @param {number} version The version to normalize.
|
|
8
|
-
* @param {number} length The maximum version number
|
|
9
|
-
* @returns {number} The absolute version number.
|
|
10
|
-
*/
|
|
11
|
-
function normalizeVersion(version, length) {
|
|
12
|
-
return version < 0 ? version + length + 1 : version;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Return the lower absolute version given a version and a maxVersion constraint.
|
|
17
|
-
* @param {number} version
|
|
18
|
-
* @param {number} maxVersion
|
|
19
|
-
* @returns {number}
|
|
20
|
-
*/
|
|
21
|
-
function minVersion(version, maxVersion) {
|
|
22
|
-
return Math.min(version, maxVersion < 0 ? version + maxVersion + 1 : maxVersion);
|
|
23
|
-
}
|
|
6
|
+
const NDJSON_NEWLINE = Buffer.from('\n');
|
|
24
7
|
|
|
25
8
|
/**
|
|
26
9
|
* An event stream is a simple wrapper around an iterator over storage documents.
|
|
@@ -33,25 +16,31 @@ class EventStream extends stream.Readable {
|
|
|
33
16
|
* @param {EventStore} eventStore The event store to get the stream from.
|
|
34
17
|
* @param {number} [minRevision] The minimum revision to include in the events (inclusive).
|
|
35
18
|
* @param {number} [maxRevision] The maximum revision to include in the events (inclusive).
|
|
36
|
-
* @param {function
|
|
37
|
-
* `(payload, metadata) => boolean
|
|
19
|
+
* @param {function|object|null} [predicate] Optional matcher:
|
|
20
|
+
* - object mode: function `(payload, metadata) => boolean` or object matcher against `{ stream, payload, metadata }`
|
|
21
|
+
* - raw mode: function `(buffer) => boolean` or object matcher against compact NDJSON bytes.
|
|
22
|
+
* @param {boolean} [raw=false] If true, emit NDJSON Buffers instead of event payload objects.
|
|
38
23
|
*/
|
|
39
|
-
constructor(name, eventStore, minRevision = 1, maxRevision = -1, predicate = null) {
|
|
40
|
-
|
|
24
|
+
constructor(name, eventStore, minRevision = 1, maxRevision = -1, predicate = null, raw = false) {
|
|
25
|
+
if (typeof predicate === 'boolean' && raw === false) {
|
|
26
|
+
raw = predicate;
|
|
27
|
+
predicate = null;
|
|
28
|
+
}
|
|
29
|
+
super({ objectMode: !raw });
|
|
41
30
|
assert(typeof name === 'string' && name !== '', 'Need to specify a stream name.');
|
|
42
31
|
assert(typeof eventStore === 'object' && eventStore !== null, `Need to provide EventStore instance to create EventStream ${name}.`);
|
|
43
32
|
|
|
44
33
|
this.name = name;
|
|
34
|
+
this.raw = raw;
|
|
45
35
|
this.predicate = predicate || null;
|
|
36
|
+
this.rawMatcher = null;
|
|
46
37
|
if (eventStore.streams[name]) {
|
|
47
38
|
this.streamIndex = eventStore.streams[name].index;
|
|
48
|
-
this.minRevision =
|
|
49
|
-
this.maxRevision =
|
|
50
|
-
this.version =
|
|
39
|
+
this.minRevision = normalizeRevision(minRevision, this.streamIndex.length);
|
|
40
|
+
this.maxRevision = normalizeRevision(maxRevision, this.streamIndex.length);
|
|
41
|
+
this.version = normalizeMaxRevision(this.streamIndex.length, maxRevision);
|
|
51
42
|
this._iterator = null;
|
|
52
|
-
this.fetch =
|
|
53
|
-
return eventStore.storage.readRange(this.minRevision, this.maxRevision, this.streamIndex);
|
|
54
|
-
}
|
|
43
|
+
this.fetch = () => eventStore.storage.readRange(this.minRevision, this.maxRevision, this.streamIndex, raw);
|
|
55
44
|
} else {
|
|
56
45
|
this.streamIndex = { length: 0 };
|
|
57
46
|
this.version = -1;
|
|
@@ -65,7 +54,7 @@ class EventStream extends stream.Readable {
|
|
|
65
54
|
* @returns {EventStream}
|
|
66
55
|
*/
|
|
67
56
|
from(revision) {
|
|
68
|
-
this.minRevision =
|
|
57
|
+
this.minRevision = normalizeRevision(revision, this.streamIndex.length);
|
|
69
58
|
return this;
|
|
70
59
|
}
|
|
71
60
|
|
|
@@ -75,8 +64,8 @@ class EventStream extends stream.Readable {
|
|
|
75
64
|
* @returns {EventStream}
|
|
76
65
|
*/
|
|
77
66
|
until(revision) {
|
|
78
|
-
this.maxRevision =
|
|
79
|
-
this.version =
|
|
67
|
+
this.maxRevision = normalizeRevision(revision, this.streamIndex.length);
|
|
68
|
+
this.version = normalizeMaxRevision(this.streamIndex.length, this.maxRevision);
|
|
80
69
|
return this;
|
|
81
70
|
}
|
|
82
71
|
|
|
@@ -160,7 +149,7 @@ class EventStream extends stream.Readable {
|
|
|
160
149
|
let tmp = this.maxRevision;
|
|
161
150
|
this.maxRevision = this.minRevision;
|
|
162
151
|
this.minRevision = tmp;
|
|
163
|
-
this.version =
|
|
152
|
+
this.version = normalizeMaxRevision(this.streamIndex.length, this.maxRevision);
|
|
164
153
|
return this;
|
|
165
154
|
}
|
|
166
155
|
|
|
@@ -233,10 +222,18 @@ class EventStream extends stream.Readable {
|
|
|
233
222
|
*[Symbol.iterator]() {
|
|
234
223
|
let next;
|
|
235
224
|
while ((next = this.next()) !== false) {
|
|
236
|
-
yield next.payload;
|
|
225
|
+
yield this.raw ? this.toRawBuffer(next) : next.payload;
|
|
237
226
|
}
|
|
238
227
|
}
|
|
239
228
|
|
|
229
|
+
/**
|
|
230
|
+
* @param {{ buffer: Buffer }} entry
|
|
231
|
+
* @returns {Buffer}
|
|
232
|
+
*/
|
|
233
|
+
toRawBuffer(entry) {
|
|
234
|
+
return Buffer.concat([entry.buffer, NDJSON_NEWLINE]);
|
|
235
|
+
}
|
|
236
|
+
|
|
240
237
|
/**
|
|
241
238
|
* Reset this stream to the start so it can be iterated again.
|
|
242
239
|
* @returns {EventStream}
|
|
@@ -259,11 +256,32 @@ class EventStream extends stream.Readable {
|
|
|
259
256
|
*/
|
|
260
257
|
filter(predicate) {
|
|
261
258
|
this.predicate = predicate || null;
|
|
259
|
+
this.rawMatcher = null;
|
|
262
260
|
this._iterator = null;
|
|
263
261
|
this._events = null;
|
|
264
262
|
return this;
|
|
265
263
|
}
|
|
266
264
|
|
|
265
|
+
matchesPredicate(entry) {
|
|
266
|
+
if (!this.predicate) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
if (this.raw) {
|
|
270
|
+
if (typeof this.predicate === 'function') {
|
|
271
|
+
return this.predicate(entry.buffer);
|
|
272
|
+
}
|
|
273
|
+
if (!this.rawMatcher) {
|
|
274
|
+
this.rawMatcher = buildRawBufferMatcher(this.predicate);
|
|
275
|
+
}
|
|
276
|
+
return this.rawMatcher(entry.buffer);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (typeof this.predicate === 'function') {
|
|
280
|
+
return this.predicate(entry.payload, entry.metadata);
|
|
281
|
+
}
|
|
282
|
+
return matches(entry, this.predicate);
|
|
283
|
+
}
|
|
284
|
+
|
|
267
285
|
/**
|
|
268
286
|
* @returns {object|boolean} The next event or false if no more events in the stream.
|
|
269
287
|
*/
|
|
@@ -275,7 +293,7 @@ class EventStream extends stream.Readable {
|
|
|
275
293
|
while (true) {
|
|
276
294
|
const result = this._iterator.next();
|
|
277
295
|
if (result.done) return false;
|
|
278
|
-
if (
|
|
296
|
+
if (this.matchesPredicate(result.value)) {
|
|
279
297
|
return result.value;
|
|
280
298
|
}
|
|
281
299
|
}
|
|
@@ -291,7 +309,7 @@ class EventStream extends stream.Readable {
|
|
|
291
309
|
*/
|
|
292
310
|
_read() {
|
|
293
311
|
const next = this.next();
|
|
294
|
-
this.push(next ? next.payload : null);
|
|
312
|
+
this.push(next ? (this.raw ? this.toRawBuffer(next) : next.payload) : null);
|
|
295
313
|
}
|
|
296
314
|
|
|
297
315
|
}
|
|
@@ -2,7 +2,8 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import events from 'events';
|
|
4
4
|
import Entry, { assertValidEntryClass } from '../IndexEntry.js';
|
|
5
|
-
import { assert, wrapAndCheck, binarySearch } from '../util.js';
|
|
5
|
+
import { assert, wrapAndCheck, binarySearch } from '../utils/util.js';
|
|
6
|
+
import { normalizeNamedCtorArgs } from '../utils/apiHelpers.js';
|
|
6
7
|
|
|
7
8
|
// node-event-store-index V01
|
|
8
9
|
const HEADER_MAGIC = "nesidx01";
|
|
@@ -44,10 +45,7 @@ class ReadableIndex extends events.EventEmitter {
|
|
|
44
45
|
*/
|
|
45
46
|
constructor(name = '.index', options = {}) {
|
|
46
47
|
super();
|
|
47
|
-
|
|
48
|
-
options = name;
|
|
49
|
-
name = '.index';
|
|
50
|
-
}
|
|
48
|
+
({ name, options } = normalizeNamedCtorArgs(name, options, '.index'));
|
|
51
49
|
let defaults = {
|
|
52
50
|
dataDirectory: '.',
|
|
53
51
|
EntryClass: Entry
|
|
@@ -77,6 +75,7 @@ class ReadableIndex extends events.EventEmitter {
|
|
|
77
75
|
if (options.metadata) {
|
|
78
76
|
this.metadata = Object.assign({entryClass: options.EntryClass.name, entrySize: options.EntryClass.size}, options.metadata);
|
|
79
77
|
}
|
|
78
|
+
this.headerSize = 0;
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
/**
|
|
@@ -208,12 +207,13 @@ class ReadableIndex extends events.EventEmitter {
|
|
|
208
207
|
* @throws {Error} if the metadata size in the header is invalid.
|
|
209
208
|
*/
|
|
210
209
|
readMetadata() {
|
|
210
|
+
if (this.headerSize > 0) return this.headerSize;
|
|
211
211
|
const headerBuffer = Buffer.allocUnsafe(8 + 4);
|
|
212
212
|
fs.readSync(this.fd, headerBuffer, 0, 8 + 4, 0);
|
|
213
213
|
const headerMagic = headerBuffer.toString('utf8', 0, 8);
|
|
214
214
|
|
|
215
|
-
assert(headerMagic.
|
|
216
|
-
assert(headerMagic === HEADER_MAGIC, `Invalid file version. The index ${this.fileName} was created with a different library version (${headerMagic.
|
|
215
|
+
assert(headerMagic.substring(0, 6) === HEADER_MAGIC.substring(0, 6), 'Invalid file header.');
|
|
216
|
+
assert(headerMagic === HEADER_MAGIC, `Invalid file version. The index ${this.fileName} was created with a different library version (${headerMagic.substring(6)}).`);
|
|
217
217
|
|
|
218
218
|
const metadataSize = headerBuffer.readUInt32BE(8);
|
|
219
219
|
assert(metadataSize >= 3, 'Invalid metadata size.');
|
|
@@ -353,7 +353,7 @@ class ReadableIndex extends events.EventEmitter {
|
|
|
353
353
|
*
|
|
354
354
|
* @api
|
|
355
355
|
* @param {number} from The 1-based index position from where to get entries from (inclusive). If < 0 will start at that position from end.
|
|
356
|
-
* @param {number} [until] The 1-based index position until where to get entries to (inclusive). If < 0 will get until that position from the end. Defaults to this.length.
|
|
356
|
+
* @param {number} [until=-1] The 1-based index position until where to get entries to (inclusive). If < 0 will get until that position from the end. Defaults to this.length.
|
|
357
357
|
* @returns {Array<Entry>|boolean} An array of entries for the given range or false on error.
|
|
358
358
|
*/
|
|
359
359
|
range(from, until = -1) {
|
|
@@ -387,7 +387,7 @@ class ReadableIndex extends events.EventEmitter {
|
|
|
387
387
|
*
|
|
388
388
|
* @api
|
|
389
389
|
* @param {number} number The sequence number to search for.
|
|
390
|
-
* @param {boolean} [min] If set to true, will return the first entry that has a sequence number greater than or equal to `number`.
|
|
390
|
+
* @param {boolean} [min=false] If set to true, will return the first entry that has a sequence number greater than or equal to `number`.
|
|
391
391
|
* @returns {number} The last index entry position that is lower than or equal to the `number`. Returns 0 if no index matches.
|
|
392
392
|
*/
|
|
393
393
|
find(number, min = false) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import ReadableIndex, { Entry, CorruptedIndexError, HEADER_MAGIC } from './ReadableIndex.js';
|
|
3
|
-
import { assertEqual } from '../util.js';
|
|
4
|
-
import { buildMetadataHeader } from '../metadataUtil.js';
|
|
5
|
-
import { ensureDirectory } from '../fsUtil.js';
|
|
3
|
+
import { assert, assertEqual } from '../utils/util.js';
|
|
4
|
+
import { buildMetadataHeader } from '../utils/metadataUtil.js';
|
|
5
|
+
import { ensureDirectory } from '../utils/fsUtil.js';
|
|
6
|
+
import { normalizeNamedCtorArgs } from '../utils/apiHelpers.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* An index is a simple append-only file that stores an ordered list of entry elements pointing to the actual file position
|
|
@@ -27,10 +28,7 @@ class WritableIndex extends ReadableIndex {
|
|
|
27
28
|
* @param {object} [options.metadata] An object containing the metadata information for this index. Will be written on initial creation and checked on subsequent openings.
|
|
28
29
|
*/
|
|
29
30
|
constructor(name = '.index', options = {}) {
|
|
30
|
-
|
|
31
|
-
options = name;
|
|
32
|
-
name = '.index';
|
|
33
|
-
}
|
|
31
|
+
({ name, options } = normalizeNamedCtorArgs(name, options, '.index'));
|
|
34
32
|
let defaults = {
|
|
35
33
|
writeBufferSize: 4096,
|
|
36
34
|
flushDelay: 100,
|
|
@@ -191,9 +189,7 @@ class WritableIndex extends ReadableIndex {
|
|
|
191
189
|
assertEqual(entry.constructor.size, this.EntryClass.size, `Invalid entry size.`);
|
|
192
190
|
|
|
193
191
|
const dataLen = this.data.length;
|
|
194
|
-
|
|
195
|
-
throw new Error('Consistency error. Tried to add an index that should come before existing last entry.');
|
|
196
|
-
}
|
|
192
|
+
assert(dataLen === 0 || this.data.at(-1).number < entry.number, 'Consistency error. Tried to add an index that should come before existing last entry.');
|
|
197
193
|
|
|
198
194
|
if (this.readUntil === dataLen - 1) {
|
|
199
195
|
this.readUntil++;
|
package/src/IndexMatcher.js
CHANGED
package/src/JoinEventStream.js
CHANGED
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
import EventStream from './EventStream.js';
|
|
2
|
-
import {
|
|
2
|
+
import { assert, kWayMerge } from './utils/util.js';
|
|
3
|
+
import { normalizeRevision } from './utils/apiHelpers.js';
|
|
3
4
|
|
|
4
5
|
/** Reusable sentinel used for missing or empty per-stream iterators. */
|
|
5
6
|
const emptyIterator = Object.freeze({ next() { return { done: true }; } });
|
|
6
7
|
|
|
7
|
-
/**
|
|
8
|
-
* Calculate the actual version number from a possibly relative (negative) version number.
|
|
9
|
-
*
|
|
10
|
-
* @param {number} version The version to normalize.
|
|
11
|
-
* @param {number} length The maximum version number
|
|
12
|
-
* @returns {number} The absolute version number.
|
|
13
|
-
*/
|
|
14
|
-
function normalizeVersion(version, length) {
|
|
15
|
-
return version < 0 ? version + length + 1 : version;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
8
|
/**
|
|
19
9
|
* An event stream is a simple wrapper around an iterator over storage documents.
|
|
20
10
|
* It implements a node readable stream interface.
|
|
@@ -27,21 +17,18 @@ class JoinEventStream extends EventStream {
|
|
|
27
17
|
* @param {EventStore} eventStore The event store to get the stream from.
|
|
28
18
|
* @param {number} [minRevision] The 1-based minimum revision to include in the events (inclusive).
|
|
29
19
|
* @param {number} [maxRevision] The 1-based maximum revision to include in the events (inclusive).
|
|
30
|
-
* @param {function
|
|
31
|
-
*
|
|
20
|
+
* @param {function|object|null} [predicate] Optional matcher (see {@link EventStream}).
|
|
21
|
+
* @param {boolean} [raw=false] If true, emit NDJSON Buffers.
|
|
32
22
|
*/
|
|
33
|
-
constructor(name, streams, eventStore, minRevision = 1, maxRevision = -1, predicate = null) {
|
|
34
|
-
super(name, eventStore, minRevision, maxRevision, predicate);
|
|
35
|
-
|
|
36
|
-
throw new Error(`Invalid list of streams supplied to JoinStream ${name}.`);
|
|
37
|
-
}
|
|
23
|
+
constructor(name, streams, eventStore, minRevision = 1, maxRevision = -1, predicate = null, raw = false) {
|
|
24
|
+
super(name, eventStore, minRevision, maxRevision, predicate, raw);
|
|
25
|
+
assert(streams instanceof Array && streams.length > 0, `Invalid list of streams supplied to JoinStream ${name}.`);
|
|
38
26
|
|
|
39
27
|
this.streamIndex = eventStore.storage.index;
|
|
40
28
|
// Translate revisions to index numbers (1-based) and wrap around negatives
|
|
41
|
-
this.minRevision =
|
|
42
|
-
this.maxRevision =
|
|
29
|
+
this.minRevision = normalizeRevision(minRevision, eventStore.length);
|
|
30
|
+
this.maxRevision = normalizeRevision(maxRevision, eventStore.length);
|
|
43
31
|
this.fetch = function() {
|
|
44
|
-
this._next = new Array(streams.length).fill(undefined);
|
|
45
32
|
return streams.map(streamName => {
|
|
46
33
|
const streamIndex = eventStore.streams[streamName]?.index;
|
|
47
34
|
if (!streamIndex || streamIndex.length === 0) {
|
|
@@ -54,60 +41,47 @@ class JoinEventStream extends EventStream {
|
|
|
54
41
|
// (e.g. minRevision > all entries, or maxRevision < all entries).
|
|
55
42
|
return emptyIterator;
|
|
56
43
|
}
|
|
57
|
-
|
|
44
|
+
// Raw mode: get { buffer, time64, sequenceNumber } for binary-header ordering.
|
|
45
|
+
// Object mode: storage deserializes for us and we order by metadata.commitId.
|
|
46
|
+
return eventStore.storage.readRange(from, until, streamIndex, this.raw);
|
|
58
47
|
});
|
|
59
48
|
}
|
|
60
49
|
this._iterator = null;
|
|
61
50
|
}
|
|
62
51
|
|
|
63
52
|
/**
|
|
64
|
-
*
|
|
65
|
-
* @param {number} index The iterator position for which to return the next value
|
|
66
|
-
* @returns {*}
|
|
53
|
+
* @returns {Generator<object>}
|
|
67
54
|
*/
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
55
|
+
createMergedIterator() {
|
|
56
|
+
const ascending = this.minRevision <= this.maxRevision;
|
|
57
|
+
const raw = this.raw;
|
|
58
|
+
return kWayMerge(
|
|
59
|
+
this.fetch(),
|
|
60
|
+
entry => raw ? entry.sequenceNumber : entry.metadata.commitId,
|
|
61
|
+
ascending
|
|
62
|
+
);
|
|
71
63
|
}
|
|
72
64
|
|
|
73
65
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return (this.minRevision > this.maxRevision ? first < second : first > second);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* @private
|
|
85
|
-
* @returns {object|boolean} The next event or false if no more events in the stream.
|
|
66
|
+
* Returns the next event in merge order.
|
|
67
|
+
*
|
|
68
|
+
* In raw mode: returns `{ buffer, time64, sequenceNumber }` from the binary header — no JSON
|
|
69
|
+
* deserialization. In object mode: returns a deserialized `{ stream, payload, metadata }` document
|
|
70
|
+
* produced by the storage layer.
|
|
71
|
+
* @returns {object|false} The next event, or `false` when the stream is exhausted.
|
|
86
72
|
*/
|
|
87
73
|
next() {
|
|
88
74
|
if (!this._iterator) {
|
|
89
|
-
this._iterator = this.
|
|
75
|
+
this._iterator = this.createMergedIterator();
|
|
90
76
|
}
|
|
91
77
|
while (true) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (typeof value === 'undefined') {
|
|
95
|
-
value = this._next[index] = this.getValue(index);
|
|
96
|
-
}
|
|
97
|
-
if (value === false) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (nextIndex === -1 || this.follows(this._next[nextIndex].metadata.commitId, value.metadata.commitId)) {
|
|
101
|
-
nextIndex = index;
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
if (nextIndex === -1) {
|
|
78
|
+
const step = this._iterator.next();
|
|
79
|
+
if (step.done) {
|
|
106
80
|
return false;
|
|
107
81
|
}
|
|
108
|
-
const next =
|
|
109
|
-
|
|
110
|
-
if (
|
|
82
|
+
const next = step.value;
|
|
83
|
+
|
|
84
|
+
if (this.matchesPredicate(next)) {
|
|
111
85
|
return next;
|
|
112
86
|
}
|
|
113
87
|
}
|