UNPKG

confusablematcher-js-interop

Version:

TypeScript NAPI Interop for ConfusableMatcher.

324 lines 13.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfusableMatcher = void 0; const binding_1 = require("./binding"); const DEFAULT_OPTIONS = { matchOnWordBoundary: false, matchRepeating: true, needlePosPointers: null, startFromEnd: false, startIndex: 0, timeoutNs: 5e6, }; class ConfusableMatcher { constructor(maps = [], skips = [], addDefaultValues = true) { this._maps = maps; this._skips = new Set(skips); if (addDefaultValues) { const spaces = [ ' ', '\u00A0', '᠎', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '​', ' ', ' ', ' ', ]; this._addMappings(spaces.map((s) => [' ', s]), false); for (let c = 'A'.charCodeAt(0); c <= 'Z'.charCodeAt(0); c++) { const lc = String.fromCharCode(c); const uc = String.fromCharCode(c + 0x20); this._addMappings([ [lc, lc], [lc, uc], [uc, uc], [uc, lc], ], false); } for (let c = '0'.charCodeAt(0); c <= '9'.charCodeAt(0); c++) { const char = String.fromCharCode(c); this._addMappings([[char, char]], false); } } this._rebuildInstance(); } addMapping(key, value) { this._addMappings([[key, value]], true); } addMappings(mappings) { this._addMappings(mappings, true); } removeMapping(key, value) { this._removeMappings([[key, value]], true); } removeMappings(mappings) { this._removeMappings(mappings, true); } getMappings() { return this._maps; } getKeyMappings(value) { this._validateValue(value, 'Value'); return this._instance.getKeyMappings(value); } addSkip(skip) { this._addSkips([skip], true); } addSkips(skips) { this._addSkips(skips, true); } removeSkip(skip) { this._removeSkips([skip], true); } removeSkips(skips) { this._removeSkips(skips, true); } getSkips() { return [...this._skips]; } computeStringPosPointers(needle) { return this._instance.computeStringPosPointers(needle); } freeStringPosPointers(pointer) { this._instance.freeStringPosPointers(pointer); } indexOfDebugFailuresSync(input, needle, options) { const { failures } = this.indexOfDebugFailuresExSync(input, needle, options); return this._formatDebugFailures(input, needle, failures); } async indexOfDebugFailures(input, needle, options) { const { failures } = await this.indexOfDebugFailuresEx(input, needle, options); return this._formatDebugFailures(input, needle, failures); } indexOfDebugFailuresExSync(input, needle, options) { let utf8; if (typeof (options === null || options === void 0 ? void 0 : options.startIndex) === 'number' && options.startIndex !== 0) { utf8 = Buffer.from(input); options.startIndex = utf8.toString('utf-8', 0, options.startIndex).length; } const value = this._instance.indexOfDebugFailures(input, needle, this._fillDefaultOptions(options)); const { result } = value; if (result.start >= 0) { if (!utf8) { utf8 = Buffer.from(input); } const start = utf8.toString('utf-8', 0, result.start); const size = utf8.toString('utf-8', result.start, result.start + result.size); return Object.assign(Object.assign({}, value), { result: { size: size.length, start: start.length, status: result.status, } }); } return value; } indexOfDebugFailuresEx(input, needle, options) { let utf8; if (typeof (options === null || options === void 0 ? void 0 : options.startIndex) === 'number' && options.startIndex !== 0) { utf8 = Buffer.from(input); options.startIndex = utf8.toString('utf-8', 0, options.startIndex).length; } return new Promise((resolve) => { this._instance.indexOfDebugFailuresAsync((value) => { const { result } = value; if (result.start >= 0) { if (!utf8) { utf8 = Buffer.from(input); } const start = utf8.toString('utf-8', 0, result.start); const size = utf8.toString('utf-8', result.start, result.start + result.size); resolve(Object.assign(Object.assign({}, value), { result: { size: size.length, start: start.length, status: result.status, } })); return; } resolve(value); }, input, needle, this._fillDefaultOptions(options)); }); } indexOfSync(input, needle, options) { let utf8; if (typeof (options === null || options === void 0 ? void 0 : options.startIndex) === 'number' && options.startIndex !== 0) { utf8 = Buffer.from(input); options.startIndex = utf8.toString('utf-8', 0, options.startIndex).length; } const result = this._instance.indexOf(input, needle, this._fillDefaultOptions(options)); if (result.start >= 0) { if (!utf8) { utf8 = Buffer.from(input); } const start = utf8.toString('utf-8', 0, result.start); const size = utf8.toString('utf-8', result.start, result.start + result.size); return { size: size.length, start: start.length, status: result.status, }; } return result; } indexOf(input, needle, options) { let utf8; if (typeof (options === null || options === void 0 ? void 0 : options.startIndex) === 'number' && options.startIndex !== 0) { utf8 = Buffer.from(input); options.startIndex = utf8.toString('utf-8', 0, options.startIndex).length; } return new Promise((resolve) => { this._instance.indexOfAsync((result) => { if (result.start >= 0) { if (!utf8) { utf8 = Buffer.from(input); } const start = utf8.toString('utf-8', 0, result.start); const size = utf8.toString('utf-8', result.start, result.start + result.size); resolve({ size: size.length, start: start.length, status: result.status, }); } }, input, needle, this._fillDefaultOptions(options)); }); } lastIndexOfSync(input, needle, options) { return this.indexOfSync(input, needle, Object.assign(Object.assign({}, options), { startFromEnd: true, startIndex: input.length - 1 })); } lastIndexOf(input, needle, options) { return this.indexOf(input, needle, Object.assign(Object.assign({}, options), { startFromEnd: true, startIndex: input.length - 1 })); } containsSync(input, needle, options) { const { status } = this._instance.indexOf(input, needle, this._fillDefaultOptions(options)); return status === binding_1.EReturnStatus.MATCH; } async contains(input, needle, options) { return new Promise((resolve) => { this._instance.indexOfAsync(({ status }) => resolve(status === binding_1.EReturnStatus.MATCH), input, needle, this._fillDefaultOptions(options)); }); } _validateValue(value, name) { if (value.length === 0) { throw new Error(`${name} cannot be empty.`); } if (value[0] == '\x00' || value[0] == '\x01') { throw new Error(String.raw `${name} cannot start with \x00 or \x01.`); } } _addMappings(mappings, rebuild) { for (const [key, value] of mappings) { this._validateValue(key, 'Key'); this._validateValue(value, 'Value'); this._maps.push([key, value]); } if (rebuild) { this._rebuildInstance(); } } _removeMappings(mappings, rebuild) { let didRemoveAny = false; for (const [key, value] of mappings) { this._validateValue(key, 'Key'); this._validateValue(value, 'Value'); for (let i = 0; i < this._maps.length; i++) { const mapping = this._maps[i]; if (mapping[0] !== key) { continue; } if (mapping[1] !== value) { continue; } this._maps.splice(i, 1); didRemoveAny = true; break; } } if (didRemoveAny && rebuild) { this._rebuildInstance(); } } _addSkips(skips, rebuild) { for (const skip of skips) { this._validateValue(skip, 'Skip'); this._skips.add(skip); } if (rebuild) { this._rebuildInstance(); } } _removeSkips(skips, rebuild) { let didRemoveAny = false; for (const skip of skips) { this._validateValue(skip, 'Skip'); if (this._skips.delete(skip)) { didRemoveAny = true; } } if (didRemoveAny && rebuild) { this._rebuildInstance(); } } _rebuildInstance() { this._instance = new binding_1.ConfusableMatcherInterop([...this._maps], [...this._skips], false); } _fillDefaultOptions(options) { return Object.assign(Object.assign({}, DEFAULT_OPTIONS), options); } _formatDebugFailures(input, needle, debugFailures) { const formatted = []; const UNDERLINE = '\u0332'; const FRAGMENT_LEN = 3; const getReason = (failure, inFragment, needleFragment) => { switch (failure.reason) { case binding_1.EDebugFailureReason.NO_PATH: return `No path in input ${inFragment} (${failure.inPos}) comparing ${needleFragment} (${failure.containsPos})`; case binding_1.EDebugFailureReason.NO_NEW_PATHS: return `No new paths in input ${inFragment} (${failure.inPos}) comparing ${needleFragment} (${failure.containsPos})`; case binding_1.EDebugFailureReason.TIMEOUT: return `Timeout in input ${inFragment} (${failure.inPos}) comparing ${needleFragment} (${failure.containsPos})`; case binding_1.EDebugFailureReason.WORD_BOUNDARY_FAIL_START: return `Word boundary failure at start of the match in input ${inFragment} (${failure.inPos}) comparing ${needleFragment} (${failure.containsPos})`; case binding_1.EDebugFailureReason.WORD_BOUNDARY_FAIL_END: return `Word boundary failure at end of the match in input ${inFragment} (${failure.inPos}) comparing ${needleFragment} (${failure.containsPos})`; } }; const inLength = Buffer.from(input).toString('utf-8').length; const needleLength = Buffer.from(needle).toString('utf-8').length; for (const failure of debugFailures) { let inPos = failure.inPos + 1; let inFragment; if (inPos > inLength) { inPos--; inFragment = ''.concat(input.slice(Math.max(0, inPos - FRAGMENT_LEN), inPos), ' ', UNDERLINE); } else { inFragment = ''.concat(input.slice(Math.max(0, inPos - FRAGMENT_LEN), inPos), UNDERLINE, input.slice(inPos, Math.min(inLength, inPos + FRAGMENT_LEN))); } let needlePos = failure.containsPos + 1; let needleFragment; if (needlePos > needleLength) { needlePos--; needleFragment = ''.concat(needle.slice(Math.max(0, needlePos - FRAGMENT_LEN), needlePos), ' ', UNDERLINE); } else { needleFragment = ''.concat(needle.slice(Math.max(0, needlePos - FRAGMENT_LEN), needlePos), UNDERLINE, needle.slice(needlePos, Math.min(needleLength, needlePos + FRAGMENT_LEN))); } const reason = getReason(failure, inFragment, needleFragment); formatted.push(reason); } return formatted; } } exports.ConfusableMatcher = ConfusableMatcher; //# sourceMappingURL=wrapper.js.map