string-masking
Advanced tools
Sorry, the diff of this file is not supported yet
+98
-57
@@ -59,2 +59,62 @@ const NOTE = | ||
| function getMaskableIndexes(string, preserveFormat) { | ||
| if (!preserveFormat) { | ||
| return Array.from({ length: string.length }, (_, index) => index); | ||
| } | ||
| const maskableIndexes = []; | ||
| for (let index = 0; index < string.length; index += 1) { | ||
| if (isMaskableCharacter(string[index])) { | ||
| maskableIndexes.push(index); | ||
| } | ||
| } | ||
| return maskableIndexes; | ||
| } | ||
| function applyMaskToIndexes(string, maskableIndexes, options) { | ||
| const { | ||
| visibleStart = 0, | ||
| visibleEnd = 0, | ||
| maskChar = "X", | ||
| alternateMask = false, | ||
| } = options; | ||
| if (maskableIndexes.length === 0) { | ||
| return buildFailure("Entered string does not contain maskable characters"); | ||
| } | ||
| const safeVisibleStart = Math.min(visibleStart, maskableIndexes.length); | ||
| const safeVisibleEnd = Math.min( | ||
| visibleEnd, | ||
| Math.max(maskableIndexes.length - safeVisibleStart, 0) | ||
| ); | ||
| const visibleStartIndexes = new Set( | ||
| maskableIndexes.slice(0, safeVisibleStart) | ||
| ); | ||
| const visibleEndIndexes = new Set( | ||
| safeVisibleEnd === 0 ? [] : maskableIndexes.slice(-safeVisibleEnd) | ||
| ); | ||
| const indexesToMask = maskableIndexes.filter( | ||
| (index) => !visibleStartIndexes.has(index) && !visibleEndIndexes.has(index) | ||
| ); | ||
| if (indexesToMask.length === 0) { | ||
| return buildSuccess(string); | ||
| } | ||
| const maskedCharacters = string.split(""); | ||
| for (let index = 0; index < indexesToMask.length; index += 1) { | ||
| if (alternateMask && index % 2 === 1) { | ||
| continue; | ||
| } | ||
| maskedCharacters[indexesToMask[index]] = maskChar; | ||
| } | ||
| return buildSuccess(maskedCharacters.join("")); | ||
| } | ||
| function legacyMiddleMask(string, maskChar) { | ||
@@ -81,2 +141,14 @@ if (string.length < 3) { | ||
| if (digit === 0) { | ||
| if (options.alternateMask) { | ||
| const visibleEachSide = Math.max(1, Math.floor((string.length / 35) * 10)); | ||
| return formatAwareMask(string, { | ||
| visibleStart: visibleEachSide, | ||
| visibleEnd: visibleEachSide, | ||
| preserveFormat: options.preserveFormat, | ||
| maskChar, | ||
| alternateMask: true, | ||
| }); | ||
| } | ||
| return legacyMiddleMask(string, maskChar); | ||
@@ -105,5 +177,16 @@ } | ||
| maskChar, | ||
| alternateMask: options.alternateMask, | ||
| }); | ||
| } | ||
| if (options.alternateMask) { | ||
| return formatAwareMask(string, { | ||
| visibleStart: 0, | ||
| visibleEnd: digit, | ||
| preserveFormat: false, | ||
| maskChar, | ||
| alternateMask: true, | ||
| }); | ||
| } | ||
| return buildSuccess(repeatMask(maskChar, maskedLength) + suffix); | ||
@@ -126,5 +209,16 @@ } | ||
| maskChar, | ||
| alternateMask: options.alternateMask, | ||
| }); | ||
| } | ||
| if (options.alternateMask) { | ||
| return formatAwareMask(string, { | ||
| visibleStart, | ||
| visibleEnd: 0, | ||
| preserveFormat: false, | ||
| maskChar, | ||
| alternateMask: true, | ||
| }); | ||
| } | ||
| return buildSuccess( | ||
@@ -142,62 +236,7 @@ string.slice(0, visibleStart) + | ||
| const { | ||
| visibleStart = 0, | ||
| visibleEnd = 0, | ||
| preserveFormat = false, | ||
| maskChar = "X", | ||
| } = options; | ||
| const maskableIndexes = getMaskableIndexes(string, preserveFormat); | ||
| if (!preserveFormat) { | ||
| const safeVisibleStart = Math.min(visibleStart, string.length); | ||
| const safeVisibleEnd = Math.min( | ||
| visibleEnd, | ||
| Math.max(string.length - safeVisibleStart, 0) | ||
| ); | ||
| const maskedLength = string.length - safeVisibleStart - safeVisibleEnd; | ||
| if (maskedLength <= 0) { | ||
| return buildSuccess(string); | ||
| } | ||
| return buildSuccess( | ||
| string.slice(0, safeVisibleStart) + | ||
| repeatMask(maskChar, maskedLength) + | ||
| string.slice(string.length - safeVisibleEnd) | ||
| ); | ||
| } | ||
| const maskableIndexes = []; | ||
| for (let index = 0; index < string.length; index += 1) { | ||
| if (isMaskableCharacter(string[index])) { | ||
| maskableIndexes.push(index); | ||
| } | ||
| } | ||
| if (maskableIndexes.length === 0) { | ||
| return buildFailure("Entered string does not contain maskable characters"); | ||
| } | ||
| const safeVisibleStart = Math.min(visibleStart, maskableIndexes.length); | ||
| const safeVisibleEnd = Math.min( | ||
| visibleEnd, | ||
| Math.max(maskableIndexes.length - safeVisibleStart, 0) | ||
| ); | ||
| const visibleStartIndexes = new Set( | ||
| maskableIndexes.slice(0, safeVisibleStart) | ||
| ); | ||
| const visibleEndIndexes = new Set( | ||
| safeVisibleEnd === 0 ? [] : maskableIndexes.slice(-safeVisibleEnd) | ||
| ); | ||
| const maskedCharacters = string.split(""); | ||
| for (const index of maskableIndexes) { | ||
| if (visibleStartIndexes.has(index) || visibleEndIndexes.has(index)) { | ||
| continue; | ||
| } | ||
| maskedCharacters[index] = maskChar; | ||
| } | ||
| return buildSuccess(maskedCharacters.join("")); | ||
| return applyMaskToIndexes(string, maskableIndexes, options); | ||
| } | ||
@@ -215,2 +254,3 @@ | ||
| maskChar: options.maskChar ?? "X", | ||
| alternateMask: Boolean(options.alternateMask), | ||
| }; | ||
@@ -285,2 +325,3 @@ | ||
| maskChar: normalizedOptions.maskChar, | ||
| alternateMask: normalizedOptions.alternateMask, | ||
| }; | ||
@@ -287,0 +328,0 @@ } |
+1
-1
| { | ||
| "name": "string-masking", | ||
| "version": "1.2.0", | ||
| "version": "1.2.1", | ||
| "description": "Mask strings while optionally preserving formatting characters", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
+17
-0
@@ -96,2 +96,16 @@ # string-masking | ||
| ### Mask alternate characters | ||
| ```js | ||
| const stringMask = require("string-masking"); | ||
| const output = stringMask("ABCDEFGH", { | ||
| alternateMask: true | ||
| }); | ||
| console.log(output); | ||
| // { status: 'success', response: 'XBXDXFXH' } | ||
| ``` | ||
| ### Use the legacy `digit` argument with formatting support | ||
@@ -120,5 +134,8 @@ | ||
| | `maskChar` | string | `"X"` | Single character used for masking | | ||
| | `alternateMask` | boolean | `false` | Masks every other eligible character inside the maskable region | | ||
| When `preserveFormat` is `true`, letters and numbers are masked, while characters such as spaces, `-`, `.`, `@`, and `+` stay in place. | ||
| When `alternateMask` is `true`, alternating starts from the first character that would otherwise be masked after `visibleStart` and `visibleEnd` are applied. | ||
| ## Suggestions For Further Improvement | ||
@@ -125,0 +142,0 @@ |
@@ -83,2 +83,41 @@ const test = require("node:test"); | ||
| test("supports alternate masking in options mode", () => { | ||
| assert.deepEqual( | ||
| stringMask("ABCDEFGH", { | ||
| alternateMask: true, | ||
| }), | ||
| { | ||
| status: "success", | ||
| response: "XBXDXFXH", | ||
| } | ||
| ); | ||
| }); | ||
| test("supports alternate masking with preserveFormat enabled", () => { | ||
| assert.deepEqual( | ||
| stringMask("+91 98765 43210", { | ||
| visibleStart: 2, | ||
| visibleEnd: 2, | ||
| preserveFormat: true, | ||
| alternateMask: true, | ||
| }), | ||
| { | ||
| status: "success", | ||
| response: "+91 X8X6X 4X210", | ||
| } | ||
| ); | ||
| }); | ||
| test("supports alternate masking through the legacy API options", () => { | ||
| assert.deepEqual( | ||
| stringMask("1234567890", 4, { | ||
| alternateMask: true, | ||
| }), | ||
| { | ||
| status: "success", | ||
| response: "X2X4X67890", | ||
| } | ||
| ); | ||
| }); | ||
| test("returns a failure model for blank strings", () => { | ||
@@ -85,0 +124,0 @@ assert.deepEqual(stringMask("", 2), { |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
19536
15.93%386
22.54%151
12.69%0
-100%