import { MatcherBaseClass } from '../../types.mjs'; import Scoring from '../../scoring/index.mjs'; /* *------------------------------------------------------------------------------- * repeats (aaa, abcabcabc) ------------------------------ *------------------------------------------------------------------------------- */ class MatchRepeat extends MatcherBaseClass { constructor(options) { super(options); this.scoring = new Scoring(options); } // eslint-disable-next-line max-statements match({ password, omniMatch }) { const matches = []; let lastIndex = 0; while (lastIndex < password.length) { const greedyMatch = this.getGreedyMatch(password, lastIndex); const lazyMatch = this.getLazyMatch(password, lastIndex); if (greedyMatch == null) { break; } const { match, baseToken } = this.setMatchToken(greedyMatch, lazyMatch); if (match) { const j = match.index + match[0].length - 1; const baseGuesses = this.getBaseGuesses(baseToken, omniMatch); matches.push(this.normalizeMatch(baseToken, j, match, baseGuesses)); lastIndex = j + 1; } } const hasPromises = matches.some(match => { return match instanceof Promise; }); if (hasPromises) { // eslint-disable-next-line @typescript-eslint/await-thenable return Promise.all(matches); } return matches; } // eslint-disable-next-line max-params normalizeMatch(baseToken, j, match, baseGuesses) { const baseMatch = { pattern: 'repeat', i: match.index, j, token: match[0], baseToken, baseGuesses: 0, repeatCount: match[0].length / baseToken.length }; if (baseGuesses instanceof Promise) { return baseGuesses.then(resolvedBaseGuesses => { return { ...baseMatch, baseGuesses: resolvedBaseGuesses }; }); } return { ...baseMatch, baseGuesses }; } getGreedyMatch(password, lastIndex) { const greedy = /(.+)\1+/g; greedy.lastIndex = lastIndex; return greedy.exec(password); } getLazyMatch(password, lastIndex) { const lazy = /(.+?)\1+/g; lazy.lastIndex = lastIndex; return lazy.exec(password); } setMatchToken(greedyMatch, lazyMatch) { const lazyAnchored = /^(.+?)\1+$/; let match; let baseToken = ''; if (lazyMatch && greedyMatch[0].length > lazyMatch[0].length) { // greedy beats lazy for 'aabaab' // greedy: [aabaab, aab] // lazy: [aa, a] match = greedyMatch; // greedy's repeated string might itself be repeated, eg. // aabaab in aabaabaabaab. // run an anchored lazy match on greedy's repeated string // to find the shortest repeated string const temp = lazyAnchored.exec(match[0]); if (temp) { baseToken = temp[1]; } } else { // lazy beats greedy for 'aaaaa' // greedy: [aaaa, aa] // lazy: [aaaaa, a] match = lazyMatch; if (match) { baseToken = match[1]; } } return { match, baseToken }; } getBaseGuesses(baseToken, omniMatch) { const matches = omniMatch.match(baseToken); if (matches instanceof Promise) { return matches.then(resolvedMatches => { const baseAnalysis = this.scoring.mostGuessableMatchSequence(baseToken, resolvedMatches); return baseAnalysis.guesses; }); } const baseAnalysis = this.scoring.mostGuessableMatchSequence(baseToken, matches); return baseAnalysis.guesses; } } export { MatchRepeat as default }; //# sourceMappingURL=matching.mjs.map