confusablematcher-js-interop
Version:
TypeScript NAPI Interop for ConfusableMatcher.
324 lines • 13.1 kB
JavaScript
;
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