@ekaone/mask-phone 1.0.0 → 1.0.1

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/dist/index.js CHANGED
@@ -1 +1,2 @@
1
1
  "use strict";var m=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var c=Object.prototype.hasOwnProperty;var k=(r,t)=>{for(var s in t)m(r,s,{get:t[s],enumerable:!0})},p=(r,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of b(t))!c.call(r,e)&&e!==s&&m(r,e,{get:()=>t[e],enumerable:!(n=f(t,e))||n.enumerable});return r};var w=r=>p(m({},"__esModule",{value:!0}),r);var C={};k(C,{maskPhone:()=>L});module.exports=w(C);var i={maskChar:"*",showLast:4,preserveFormat:!1};function F(r){return r==null?"":(typeof r=="object"&&r!==null&&"phone"in r&&(r=r.phone),String(r).trim())}function l(r){return r.replace(/[^\d+]/g,"")}function O(r){var n,e,a,u,o,h;if(!r)return{maskChar:i.maskChar,showFirst:0,showLast:i.showLast,preserveFormat:i.preserveFormat};let t=(e=(n=r.showFirst)!=null?n:r.showStart)!=null?e:0,s=(u=(a=r.showLast)!=null?a:r.showEnd)!=null?u:i.showLast;return{maskChar:(o=r.maskChar)!=null?o:i.maskChar,showFirst:t,showLast:s,visibleRanges:r.visibleRanges,preserveFormat:(h=r.preserveFormat)!=null?h:i.preserveFormat,customMask:r.customMask}}function g(r,t,s){return s.visibleRanges&&s.visibleRanges.length>0?s.visibleRanges.some(([n,e])=>r>=n&&r<=e):s.showFirst>0&&r<s.showFirst||s.showLast>0&&r>=t-s.showLast}function v(r){return/[\s\-().\+]/.test(r)}function d(r,t){let s=-1,e=r.replace(/\D/g,"").length;return r.split("").map((a,u)=>t.customMask?t.customMask(a,u,r):v(a)||(s++,g(s,e,t))?a:t.maskChar).join("")}function M(r,t){let s=l(r),n=s.length;return s.split("").map((e,a)=>t.customMask?t.customMask(e,a,s):e==="+"||g(a,n,t)?e:t.maskChar).join("")}var L=(r,t)=>{let s=F(r);if(!s)return"";let n=O(t),e=l(s),a=n.showFirst+n.showLast;return!(t!=null&&t.customMask)&&!(t!=null&&t.visibleRanges)&&e.length<=a?s:n.preserveFormat?d(s,n):M(s,n)};0&&(module.exports={maskPhone});
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/types.ts"],"sourcesContent":["import type { PhoneInput, MaskOptions, NormalizedOptions } from \"./types\";\nimport { DEFAULT_OPTIONS } from \"./types\";\n\n/**\n * Normalize phone input to string safely\n * Handles string, number, null, undefined\n */\nfunction normalizePhone(phone: PhoneInput): string {\n if (phone == null) return \"\";\n\n // Handle object\n if (typeof phone === \"object\" && phone !== null && \"phone\" in phone) {\n phone = (phone as any).phone;\n }\n\n // Convert to string safely\n const phoneStr = String(phone).trim();\n\n return phoneStr;\n}\n\n/**\n * Strip non-digit characters from phone string\n * Keeps only 0-9 and optionally '+'\n */\nfunction stripFormatting(phone: string): string {\n // Keep digits and plus sign for international format\n return phone.replace(/[^\\d+]/g, \"\");\n}\n\n/**\n * Normalize and resolve option aliases\n * Priority: customMask > visibleRanges > showFirst/Last > defaults\n */\nfunction normalizeOptions(options?: MaskOptions): NormalizedOptions {\n if (!options) {\n return {\n maskChar: DEFAULT_OPTIONS.maskChar,\n showFirst: 0,\n showLast: DEFAULT_OPTIONS.showLast,\n preserveFormat: DEFAULT_OPTIONS.preserveFormat,\n };\n }\n\n // Resolve aliases: showStart -> showFirst, showEnd -> showLast\n const showFirst = options.showFirst ?? options.showStart ?? 0;\n const showLast =\n options.showLast ?? options.showEnd ?? DEFAULT_OPTIONS.showLast;\n\n return {\n maskChar: options.maskChar ?? DEFAULT_OPTIONS.maskChar,\n showFirst,\n showLast,\n visibleRanges: options.visibleRanges,\n preserveFormat: options.preserveFormat ?? DEFAULT_OPTIONS.preserveFormat,\n customMask: options.customMask,\n };\n}\n\n/**\n * Check if character at index should be visible based on options\n */\nfunction isCharVisible(\n index: number,\n length: number,\n options: NormalizedOptions,\n): boolean {\n // Check visible ranges first (higher priority)\n if (options.visibleRanges && options.visibleRanges.length > 0) {\n return options.visibleRanges.some(\n ([start, end]) => index >= start && index <= end,\n );\n }\n\n // Check showFirst\n if (options.showFirst > 0 && index < options.showFirst) {\n return true;\n }\n\n // Check showLast\n if (options.showLast > 0 && index >= length - options.showLast) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Check if character is a formatting character\n */\nfunction isFormattingChar(char: string): boolean {\n // Common phone formatting: spaces, dashes, parentheses, dots, plus\n return /[\\s\\-().\\+]/.test(char);\n}\n\n/**\n * Mask a phone string with format preservation\n */\nfunction maskWithFormat(phone: string, options: NormalizedOptions): string {\n let digitIndex = -1; // Track position among digits only\n const digits = phone.replace(/\\D/g, \"\"); // All digits for length calculation\n const totalDigits = digits.length;\n\n return phone\n .split(\"\")\n .map((char, charIndex) => {\n // Custom mask function has highest priority\n if (options.customMask) {\n return options.customMask(char, charIndex, phone);\n }\n\n // If it's a formatting character, preserve it\n if (isFormattingChar(char)) {\n return char;\n }\n\n // It's a digit, increment our digit counter\n digitIndex++;\n\n // Check if this digit should be visible\n if (isCharVisible(digitIndex, totalDigits, options)) {\n return char;\n }\n\n // Mask the digit\n return options.maskChar;\n })\n .join(\"\");\n}\n\n/**\n * Mask a phone string without format (digits only)\n */\nfunction maskWithoutFormat(phone: string, options: NormalizedOptions): string {\n // Strip to digits only (keep + for international)\n const stripped = stripFormatting(phone);\n const length = stripped.length;\n\n return stripped\n .split(\"\")\n .map((char, index) => {\n // Custom mask function has highest priority\n if (options.customMask) {\n return options.customMask(char, index, stripped);\n }\n\n // Always preserve + sign\n if (char === \"+\") {\n return char;\n }\n\n // Check if this position should be visible\n if (isCharVisible(index, length, options)) {\n return char;\n }\n\n // Mask the character\n return options.maskChar;\n })\n .join(\"\");\n}\n\n/**\n * Main masking function\n * Masks phone numbers with flexible options\n *\n * @param phone - Phone number as string or number\n * @param options - Masking options\n * @returns Masked phone string\n *\n * @example\n * ```typescript\n * maskPhone('+1234567890', { showLast: 4 })\n * // Returns: '******7890'\n *\n * maskPhone('+1 (555) 123-4567', { showLast: 4, preserveFormat: true })\n * // Returns: '+* (***) ***-4567'\n *\n * maskPhone('628123456789', { showFirst: 3, showLast: 2 })\n * // Returns: '628*******89'\n * ```\n */\nexport const maskPhone = (phone: PhoneInput, options?: MaskOptions): string => {\n // Normalize input\n const phoneStr = normalizePhone(phone);\n\n // Handle empty input\n if (!phoneStr) return \"\";\n\n // Normalize options\n const normalizedOpts = normalizeOptions(options);\n\n // Edge case: if phone is shorter than or equal to showFirst + showLast, don't mask\n const stripped = stripFormatting(phoneStr);\n const totalVisible = normalizedOpts.showFirst + normalizedOpts.showLast;\n\n if (\n !options?.customMask &&\n !options?.visibleRanges &&\n stripped.length <= totalVisible\n ) {\n return phoneStr; // Return original if nothing to mask\n }\n\n // Choose masking strategy based on preserveFormat option\n if (normalizedOpts.preserveFormat) {\n return maskWithFormat(phoneStr, normalizedOpts);\n } else {\n return maskWithoutFormat(phoneStr, normalizedOpts);\n }\n};\n\n// Export types for consumers\nexport type { PhoneInput, MaskOptions } from \"./types\";\n","/**\n * Phone masking options\n * All options are optional and use flat structure for simplicity\n */\nexport interface MaskOptions {\n /**\n * Character used for masking\n * @default '*'\n * @example '#', 'X', '•'\n */\n maskChar?: string;\n\n /**\n * Number of characters to show from the start\n * @example 3 → '628*******'\n */\n showFirst?: number;\n\n /**\n * Number of characters to show from the end (most common pattern)\n * @default 4\n * @example 4 → '******7890'\n */\n showLast?: number;\n\n /**\n * Alias for showFirst (for clarity)\n * @example 2 → '62********'\n */\n showStart?: number;\n\n /**\n * Alias for showLast (for clarity)\n * @example 4 → '******7890'\n */\n showEnd?: number;\n\n /**\n * Specific ranges to keep visible\n * Array of [startIndex, endIndex] (inclusive)\n * @example [[0, 2], [8, 10]] → '628****789*'\n */\n visibleRanges?: Array<[number, number]>;\n\n /**\n * Preserve formatting characters (spaces, dashes, parentheses, etc.)\n * When true: '+1 (555) 123-4567' → '+* (***) ***-4567'\n * When false: '+1 (555) 123-4567' → '**************' (strips then masks)\n * @default false\n */\n preserveFormat?: boolean;\n\n /**\n * Custom masking function for full control\n * Overrides all other options\n * @param char - Current character\n * @param index - Position in the phone string\n * @param phone - Full phone string\n * @returns Masked character or original\n * @example (char, idx) => idx % 2 === 0 ? '*' : char\n */\n customMask?: (char: string, index: number, phone: string) => string;\n}\n\n/**\n * Input type for phone parameter\n * Accepts both string and number for flexibility\n */\nexport type PhoneInput = string | number;\n\n/**\n * Default masking options\n */\nexport const DEFAULT_OPTIONS: Required<\n Omit<\n MaskOptions,\n \"showFirst\" | \"showStart\" | \"showEnd\" | \"visibleRanges\" | \"customMask\"\n >\n> = {\n maskChar: \"*\",\n showLast: 4,\n preserveFormat: false,\n};\n\n/**\n * Type guard to check if value is a valid phone input\n */\nexport function isValidPhoneInput(value: unknown): value is PhoneInput {\n return typeof value === \"string\" || typeof value === \"number\";\n}\n\n/**\n * Normalized options after resolving aliases and defaults\n * Internal use only\n */\nexport interface NormalizedOptions {\n maskChar: string;\n showFirst: number;\n showLast: number;\n visibleRanges?: Array<[number, number]>;\n preserveFormat: boolean;\n customMask?: (char: string, index: number, phone: string) => string;\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,eAAAE,IAAA,eAAAC,EAAAH,GCyEO,IAAMI,EAKT,CACF,SAAU,IACV,SAAU,EACV,eAAgB,EAClB,ED3EA,SAASC,EAAeC,EAA2B,CACjD,OAAIA,GAAS,KAAa,IAGtB,OAAOA,GAAU,UAAYA,IAAU,MAAQ,UAAWA,IAC5DA,EAASA,EAAc,OAIR,OAAOA,CAAK,EAAE,KAAK,EAGtC,CAMA,SAASC,EAAgBD,EAAuB,CAE9C,OAAOA,EAAM,QAAQ,UAAW,EAAE,CACpC,CAMA,SAASE,EAAiBC,EAA0C,CAlCpE,IAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAmCE,GAAI,CAACN,EACH,MAAO,CACL,SAAUO,EAAgB,SAC1B,UAAW,EACX,SAAUA,EAAgB,SAC1B,eAAgBA,EAAgB,cAClC,EAIF,IAAMC,GAAYN,GAAAD,EAAAD,EAAQ,YAAR,KAAAC,EAAqBD,EAAQ,YAA7B,KAAAE,EAA0C,EACtDO,GACJL,GAAAD,EAAAH,EAAQ,WAAR,KAAAG,EAAoBH,EAAQ,UAA5B,KAAAI,EAAuCG,EAAgB,SAEzD,MAAO,CACL,UAAUF,EAAAL,EAAQ,WAAR,KAAAK,EAAoBE,EAAgB,SAC9C,UAAAC,EACA,SAAAC,EACA,cAAeT,EAAQ,cACvB,gBAAgBM,EAAAN,EAAQ,iBAAR,KAAAM,EAA0BC,EAAgB,eAC1D,WAAYP,EAAQ,UACtB,CACF,CAKA,SAASU,EACPC,EACAC,EACAZ,EACS,CAET,OAAIA,EAAQ,eAAiBA,EAAQ,cAAc,OAAS,EACnDA,EAAQ,cAAc,KAC3B,CAAC,CAACa,EAAOC,CAAG,IAAMH,GAASE,GAASF,GAASG,CAC/C,EAIEd,EAAQ,UAAY,GAAKW,EAAQX,EAAQ,WAKzCA,EAAQ,SAAW,GAAKW,GAASC,EAASZ,EAAQ,QAKxD,CAKA,SAASe,EAAiBC,EAAuB,CAE/C,MAAO,cAAc,KAAKA,CAAI,CAChC,CAKA,SAASC,EAAepB,EAAeG,EAAoC,CACzE,IAAIkB,EAAa,GAEXC,EADStB,EAAM,QAAQ,MAAO,EAAE,EACX,OAE3B,OAAOA,EACJ,MAAM,EAAE,EACR,IAAI,CAACmB,EAAMI,IAENpB,EAAQ,WACHA,EAAQ,WAAWgB,EAAMI,EAAWvB,CAAK,EAI9CkB,EAAiBC,CAAI,IAKzBE,IAGIR,EAAcQ,EAAYC,EAAanB,CAAO,GACzCgB,EAIFhB,EAAQ,QAChB,EACA,KAAK,EAAE,CACZ,CAKA,SAASqB,EAAkBxB,EAAeG,EAAoC,CAE5E,IAAMsB,EAAWxB,EAAgBD,CAAK,EAChCe,EAASU,EAAS,OAExB,OAAOA,EACJ,MAAM,EAAE,EACR,IAAI,CAACN,EAAML,IAENX,EAAQ,WACHA,EAAQ,WAAWgB,EAAML,EAAOW,CAAQ,EAI7CN,IAAS,KAKTN,EAAcC,EAAOC,EAAQZ,CAAO,EAC/BgB,EAIFhB,EAAQ,QAChB,EACA,KAAK,EAAE,CACZ,CAsBO,IAAMuB,EAAY,CAAC1B,EAAmBG,IAAkC,CAE7E,IAAMwB,EAAW5B,EAAeC,CAAK,EAGrC,GAAI,CAAC2B,EAAU,MAAO,GAGtB,IAAMC,EAAiB1B,EAAiBC,CAAO,EAGzCsB,EAAWxB,EAAgB0B,CAAQ,EACnCE,EAAeD,EAAe,UAAYA,EAAe,SAE/D,MACE,EAACzB,GAAA,MAAAA,EAAS,aACV,EAACA,GAAA,MAAAA,EAAS,gBACVsB,EAAS,QAAUI,EAEZF,EAILC,EAAe,eACVR,EAAeO,EAAUC,CAAc,EAEvCJ,EAAkBG,EAAUC,CAAc,CAErD","names":["index_exports","__export","maskPhone","__toCommonJS","DEFAULT_OPTIONS","normalizePhone","phone","stripFormatting","normalizeOptions","options","_a","_b","_c","_d","_e","_f","DEFAULT_OPTIONS","showFirst","showLast","isCharVisible","index","length","start","end","isFormattingChar","char","maskWithFormat","digitIndex","totalDigits","charIndex","maskWithoutFormat","stripped","maskPhone","phoneStr","normalizedOpts","totalVisible"]}
package/dist/index.mjs CHANGED
@@ -1 +1,2 @@
1
1
  var i={maskChar:"*",showLast:4,preserveFormat:!1};function g(r){return r==null?"":(typeof r=="object"&&r!==null&&"phone"in r&&(r=r.phone),String(r).trim())}function h(r){return r.replace(/[^\d+]/g,"")}function f(r){var e,n,a,u,m,o;if(!r)return{maskChar:i.maskChar,showFirst:0,showLast:i.showLast,preserveFormat:i.preserveFormat};let t=(n=(e=r.showFirst)!=null?e:r.showStart)!=null?n:0,s=(u=(a=r.showLast)!=null?a:r.showEnd)!=null?u:i.showLast;return{maskChar:(m=r.maskChar)!=null?m:i.maskChar,showFirst:t,showLast:s,visibleRanges:r.visibleRanges,preserveFormat:(o=r.preserveFormat)!=null?o:i.preserveFormat,customMask:r.customMask}}function l(r,t,s){return s.visibleRanges&&s.visibleRanges.length>0?s.visibleRanges.some(([e,n])=>r>=e&&r<=n):s.showFirst>0&&r<s.showFirst||s.showLast>0&&r>=t-s.showLast}function b(r){return/[\s\-().\+]/.test(r)}function c(r,t){let s=-1,n=r.replace(/\D/g,"").length;return r.split("").map((a,u)=>t.customMask?t.customMask(a,u,r):b(a)||(s++,l(s,n,t))?a:t.maskChar).join("")}function k(r,t){let s=h(r),e=s.length;return s.split("").map((n,a)=>t.customMask?t.customMask(n,a,s):n==="+"||l(a,e,t)?n:t.maskChar).join("")}var F=(r,t)=>{let s=g(r);if(!s)return"";let e=f(t),n=h(s),a=e.showFirst+e.showLast;return!(t!=null&&t.customMask)&&!(t!=null&&t.visibleRanges)&&n.length<=a?s:e.preserveFormat?c(s,e):k(s,e)};export{F as maskPhone};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/index.ts"],"sourcesContent":["/**\n * Phone masking options\n * All options are optional and use flat structure for simplicity\n */\nexport interface MaskOptions {\n /**\n * Character used for masking\n * @default '*'\n * @example '#', 'X', '•'\n */\n maskChar?: string;\n\n /**\n * Number of characters to show from the start\n * @example 3 → '628*******'\n */\n showFirst?: number;\n\n /**\n * Number of characters to show from the end (most common pattern)\n * @default 4\n * @example 4 → '******7890'\n */\n showLast?: number;\n\n /**\n * Alias for showFirst (for clarity)\n * @example 2 → '62********'\n */\n showStart?: number;\n\n /**\n * Alias for showLast (for clarity)\n * @example 4 → '******7890'\n */\n showEnd?: number;\n\n /**\n * Specific ranges to keep visible\n * Array of [startIndex, endIndex] (inclusive)\n * @example [[0, 2], [8, 10]] → '628****789*'\n */\n visibleRanges?: Array<[number, number]>;\n\n /**\n * Preserve formatting characters (spaces, dashes, parentheses, etc.)\n * When true: '+1 (555) 123-4567' → '+* (***) ***-4567'\n * When false: '+1 (555) 123-4567' → '**************' (strips then masks)\n * @default false\n */\n preserveFormat?: boolean;\n\n /**\n * Custom masking function for full control\n * Overrides all other options\n * @param char - Current character\n * @param index - Position in the phone string\n * @param phone - Full phone string\n * @returns Masked character or original\n * @example (char, idx) => idx % 2 === 0 ? '*' : char\n */\n customMask?: (char: string, index: number, phone: string) => string;\n}\n\n/**\n * Input type for phone parameter\n * Accepts both string and number for flexibility\n */\nexport type PhoneInput = string | number;\n\n/**\n * Default masking options\n */\nexport const DEFAULT_OPTIONS: Required<\n Omit<\n MaskOptions,\n \"showFirst\" | \"showStart\" | \"showEnd\" | \"visibleRanges\" | \"customMask\"\n >\n> = {\n maskChar: \"*\",\n showLast: 4,\n preserveFormat: false,\n};\n\n/**\n * Type guard to check if value is a valid phone input\n */\nexport function isValidPhoneInput(value: unknown): value is PhoneInput {\n return typeof value === \"string\" || typeof value === \"number\";\n}\n\n/**\n * Normalized options after resolving aliases and defaults\n * Internal use only\n */\nexport interface NormalizedOptions {\n maskChar: string;\n showFirst: number;\n showLast: number;\n visibleRanges?: Array<[number, number]>;\n preserveFormat: boolean;\n customMask?: (char: string, index: number, phone: string) => string;\n}\n","import type { PhoneInput, MaskOptions, NormalizedOptions } from \"./types\";\nimport { DEFAULT_OPTIONS } from \"./types\";\n\n/**\n * Normalize phone input to string safely\n * Handles string, number, null, undefined\n */\nfunction normalizePhone(phone: PhoneInput): string {\n if (phone == null) return \"\";\n\n // Handle object\n if (typeof phone === \"object\" && phone !== null && \"phone\" in phone) {\n phone = (phone as any).phone;\n }\n\n // Convert to string safely\n const phoneStr = String(phone).trim();\n\n return phoneStr;\n}\n\n/**\n * Strip non-digit characters from phone string\n * Keeps only 0-9 and optionally '+'\n */\nfunction stripFormatting(phone: string): string {\n // Keep digits and plus sign for international format\n return phone.replace(/[^\\d+]/g, \"\");\n}\n\n/**\n * Normalize and resolve option aliases\n * Priority: customMask > visibleRanges > showFirst/Last > defaults\n */\nfunction normalizeOptions(options?: MaskOptions): NormalizedOptions {\n if (!options) {\n return {\n maskChar: DEFAULT_OPTIONS.maskChar,\n showFirst: 0,\n showLast: DEFAULT_OPTIONS.showLast,\n preserveFormat: DEFAULT_OPTIONS.preserveFormat,\n };\n }\n\n // Resolve aliases: showStart -> showFirst, showEnd -> showLast\n const showFirst = options.showFirst ?? options.showStart ?? 0;\n const showLast =\n options.showLast ?? options.showEnd ?? DEFAULT_OPTIONS.showLast;\n\n return {\n maskChar: options.maskChar ?? DEFAULT_OPTIONS.maskChar,\n showFirst,\n showLast,\n visibleRanges: options.visibleRanges,\n preserveFormat: options.preserveFormat ?? DEFAULT_OPTIONS.preserveFormat,\n customMask: options.customMask,\n };\n}\n\n/**\n * Check if character at index should be visible based on options\n */\nfunction isCharVisible(\n index: number,\n length: number,\n options: NormalizedOptions,\n): boolean {\n // Check visible ranges first (higher priority)\n if (options.visibleRanges && options.visibleRanges.length > 0) {\n return options.visibleRanges.some(\n ([start, end]) => index >= start && index <= end,\n );\n }\n\n // Check showFirst\n if (options.showFirst > 0 && index < options.showFirst) {\n return true;\n }\n\n // Check showLast\n if (options.showLast > 0 && index >= length - options.showLast) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Check if character is a formatting character\n */\nfunction isFormattingChar(char: string): boolean {\n // Common phone formatting: spaces, dashes, parentheses, dots, plus\n return /[\\s\\-().\\+]/.test(char);\n}\n\n/**\n * Mask a phone string with format preservation\n */\nfunction maskWithFormat(phone: string, options: NormalizedOptions): string {\n let digitIndex = -1; // Track position among digits only\n const digits = phone.replace(/\\D/g, \"\"); // All digits for length calculation\n const totalDigits = digits.length;\n\n return phone\n .split(\"\")\n .map((char, charIndex) => {\n // Custom mask function has highest priority\n if (options.customMask) {\n return options.customMask(char, charIndex, phone);\n }\n\n // If it's a formatting character, preserve it\n if (isFormattingChar(char)) {\n return char;\n }\n\n // It's a digit, increment our digit counter\n digitIndex++;\n\n // Check if this digit should be visible\n if (isCharVisible(digitIndex, totalDigits, options)) {\n return char;\n }\n\n // Mask the digit\n return options.maskChar;\n })\n .join(\"\");\n}\n\n/**\n * Mask a phone string without format (digits only)\n */\nfunction maskWithoutFormat(phone: string, options: NormalizedOptions): string {\n // Strip to digits only (keep + for international)\n const stripped = stripFormatting(phone);\n const length = stripped.length;\n\n return stripped\n .split(\"\")\n .map((char, index) => {\n // Custom mask function has highest priority\n if (options.customMask) {\n return options.customMask(char, index, stripped);\n }\n\n // Always preserve + sign\n if (char === \"+\") {\n return char;\n }\n\n // Check if this position should be visible\n if (isCharVisible(index, length, options)) {\n return char;\n }\n\n // Mask the character\n return options.maskChar;\n })\n .join(\"\");\n}\n\n/**\n * Main masking function\n * Masks phone numbers with flexible options\n *\n * @param phone - Phone number as string or number\n * @param options - Masking options\n * @returns Masked phone string\n *\n * @example\n * ```typescript\n * maskPhone('+1234567890', { showLast: 4 })\n * // Returns: '******7890'\n *\n * maskPhone('+1 (555) 123-4567', { showLast: 4, preserveFormat: true })\n * // Returns: '+* (***) ***-4567'\n *\n * maskPhone('628123456789', { showFirst: 3, showLast: 2 })\n * // Returns: '628*******89'\n * ```\n */\nexport const maskPhone = (phone: PhoneInput, options?: MaskOptions): string => {\n // Normalize input\n const phoneStr = normalizePhone(phone);\n\n // Handle empty input\n if (!phoneStr) return \"\";\n\n // Normalize options\n const normalizedOpts = normalizeOptions(options);\n\n // Edge case: if phone is shorter than or equal to showFirst + showLast, don't mask\n const stripped = stripFormatting(phoneStr);\n const totalVisible = normalizedOpts.showFirst + normalizedOpts.showLast;\n\n if (\n !options?.customMask &&\n !options?.visibleRanges &&\n stripped.length <= totalVisible\n ) {\n return phoneStr; // Return original if nothing to mask\n }\n\n // Choose masking strategy based on preserveFormat option\n if (normalizedOpts.preserveFormat) {\n return maskWithFormat(phoneStr, normalizedOpts);\n } else {\n return maskWithoutFormat(phoneStr, normalizedOpts);\n }\n};\n\n// Export types for consumers\nexport type { PhoneInput, MaskOptions } from \"./types\";\n"],"mappings":"AAyEO,IAAMA,EAKT,CACF,SAAU,IACV,SAAU,EACV,eAAgB,EAClB,EC3EA,SAASC,EAAeC,EAA2B,CACjD,OAAIA,GAAS,KAAa,IAGtB,OAAOA,GAAU,UAAYA,IAAU,MAAQ,UAAWA,IAC5DA,EAASA,EAAc,OAIR,OAAOA,CAAK,EAAE,KAAK,EAGtC,CAMA,SAASC,EAAgBD,EAAuB,CAE9C,OAAOA,EAAM,QAAQ,UAAW,EAAE,CACpC,CAMA,SAASE,EAAiBC,EAA0C,CAlCpE,IAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAAAC,EAmCE,GAAI,CAACN,EACH,MAAO,CACL,SAAUO,EAAgB,SAC1B,UAAW,EACX,SAAUA,EAAgB,SAC1B,eAAgBA,EAAgB,cAClC,EAIF,IAAMC,GAAYN,GAAAD,EAAAD,EAAQ,YAAR,KAAAC,EAAqBD,EAAQ,YAA7B,KAAAE,EAA0C,EACtDO,GACJL,GAAAD,EAAAH,EAAQ,WAAR,KAAAG,EAAoBH,EAAQ,UAA5B,KAAAI,EAAuCG,EAAgB,SAEzD,MAAO,CACL,UAAUF,EAAAL,EAAQ,WAAR,KAAAK,EAAoBE,EAAgB,SAC9C,UAAAC,EACA,SAAAC,EACA,cAAeT,EAAQ,cACvB,gBAAgBM,EAAAN,EAAQ,iBAAR,KAAAM,EAA0BC,EAAgB,eAC1D,WAAYP,EAAQ,UACtB,CACF,CAKA,SAASU,EACPC,EACAC,EACAZ,EACS,CAET,OAAIA,EAAQ,eAAiBA,EAAQ,cAAc,OAAS,EACnDA,EAAQ,cAAc,KAC3B,CAAC,CAACa,EAAOC,CAAG,IAAMH,GAASE,GAASF,GAASG,CAC/C,EAIEd,EAAQ,UAAY,GAAKW,EAAQX,EAAQ,WAKzCA,EAAQ,SAAW,GAAKW,GAASC,EAASZ,EAAQ,QAKxD,CAKA,SAASe,EAAiBC,EAAuB,CAE/C,MAAO,cAAc,KAAKA,CAAI,CAChC,CAKA,SAASC,EAAepB,EAAeG,EAAoC,CACzE,IAAIkB,EAAa,GAEXC,EADStB,EAAM,QAAQ,MAAO,EAAE,EACX,OAE3B,OAAOA,EACJ,MAAM,EAAE,EACR,IAAI,CAACmB,EAAMI,IAENpB,EAAQ,WACHA,EAAQ,WAAWgB,EAAMI,EAAWvB,CAAK,EAI9CkB,EAAiBC,CAAI,IAKzBE,IAGIR,EAAcQ,EAAYC,EAAanB,CAAO,GACzCgB,EAIFhB,EAAQ,QAChB,EACA,KAAK,EAAE,CACZ,CAKA,SAASqB,EAAkBxB,EAAeG,EAAoC,CAE5E,IAAMsB,EAAWxB,EAAgBD,CAAK,EAChCe,EAASU,EAAS,OAExB,OAAOA,EACJ,MAAM,EAAE,EACR,IAAI,CAACN,EAAML,IAENX,EAAQ,WACHA,EAAQ,WAAWgB,EAAML,EAAOW,CAAQ,EAI7CN,IAAS,KAKTN,EAAcC,EAAOC,EAAQZ,CAAO,EAC/BgB,EAIFhB,EAAQ,QAChB,EACA,KAAK,EAAE,CACZ,CAsBO,IAAMuB,EAAY,CAAC1B,EAAmBG,IAAkC,CAE7E,IAAMwB,EAAW5B,EAAeC,CAAK,EAGrC,GAAI,CAAC2B,EAAU,MAAO,GAGtB,IAAMC,EAAiB1B,EAAiBC,CAAO,EAGzCsB,EAAWxB,EAAgB0B,CAAQ,EACnCE,EAAeD,EAAe,UAAYA,EAAe,SAE/D,MACE,EAACzB,GAAA,MAAAA,EAAS,aACV,EAACA,GAAA,MAAAA,EAAS,gBACVsB,EAAS,QAAUI,EAEZF,EAILC,EAAe,eACVR,EAAeO,EAAUC,CAAc,EAEvCJ,EAAkBG,EAAUC,CAAc,CAErD","names":["DEFAULT_OPTIONS","normalizePhone","phone","stripFormatting","normalizeOptions","options","_a","_b","_c","_d","_e","_f","DEFAULT_OPTIONS","showFirst","showLast","isCharVisible","index","length","start","end","isFormattingChar","char","maskWithFormat","digitIndex","totalDigits","charIndex","maskWithoutFormat","stripped","maskPhone","phoneStr","normalizedOpts","totalVisible"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekaone/mask-phone",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A lightweight, zero-dependency TypeScript library for masking phone numbers",
5
5
  "keywords": [
6
6
  "phone",
@@ -34,16 +34,6 @@
34
34
  "README.md",
35
35
  "LICENSE"
36
36
  ],
37
- "scripts": {
38
- "build": "tsup",
39
- "dev": "tsup --watch",
40
- "clean": "rimraf dist",
41
- "prepublishOnly": "npm run clean && npm run build && npm test",
42
- "test": "vitest run",
43
- "test:watch": "vitest",
44
- "test:ui": "vitest --ui",
45
- "test:coverage": "vitest run --coverage"
46
- },
47
37
  "repository": {
48
38
  "type": "git",
49
39
  "url": "https://github.com/ekaone/mask-phone"
@@ -60,5 +50,15 @@
60
50
  "tsup": "^8.5.1",
61
51
  "typescript": "^5.7.0",
62
52
  "vitest": "^4.0.18"
53
+ },
54
+ "scripts": {
55
+ "build": "pnpm clean && tsup",
56
+ "dev": "tsup --watch",
57
+ "clean": "rimraf dist",
58
+ "typecheck": "tsc --noEmit",
59
+ "test": "vitest run",
60
+ "test:watch": "vitest",
61
+ "test:ui": "vitest --ui",
62
+ "test:coverage": "vitest run --coverage"
63
63
  }
64
- }
64
+ }