# phone strategy ## Purpose The `phone` masking strategy anonymises phone numbers while preserving their formatting separators, making masked values easy to read in logs and audit trails. Only the last 4 digits are kept; all other digit positions are replaced with `*`. Non-digit characters (hyphens, spaces, parentheses, dots, `+`) pass through unchanged, so the output retains the same visual structure as the input regardless of locale or formatting convention. ## Features - Last-4-digit preservation regardless of phone format. - Separator characters (`-`, ` `, `(`, `)`, `.`, `+`) are preserved verbatim. - Handles international formats (`+1-…`), US formats (`(555) 123-4567`), and unformatted digit strings (`5551234567`). - No regex-based capture groups or lookaheads — single linear pass. ## Usage: Basic Example ```ts import { maskPhone } from '@suaveplan/data-masking/strategies/phone'; const masked = maskPhone('+1-555-123-4567', { fields: ['phone'], strategy: 'phone' }); // masked → '*-***-***-4567' ``` ## Usage: Advanced Example ```ts import { maskPhone } from '@suaveplan/data-masking/strategies/phone'; // US format with parentheses maskPhone('(555) 123-4567', { fields: ['phone'], strategy: 'phone' }); // → '(***) ***-4567' // E.164 international maskPhone('+447911123456', { fields: ['phone'], strategy: 'phone' }); // → '+*********3456' // Unformatted maskPhone('5551234567', { fields: ['phone'], strategy: 'phone' }); // → '******4567' // No digits at all → passthrough maskPhone('N/A', { fields: ['phone'], strategy: 'phone' }); // → 'N/A' ``` ## API Reference ### `maskPhone(value, rule)` ```ts function maskPhone(value: string, rule: MaskRule): string; ``` | Parameter | Type | Description | |-----------|------------|----------------------------------------------------------| | `value` | `string` | The original phone number in any format. | | `rule` | `MaskRule` | Driving rule (no fields are consumed by phone strategy). | **Returns** the masked phone string with last 4 digits preserved and all other digit positions replaced by `*`. **Does not throw.** Returns the original value unchanged when no digit characters are found. ## Implementation Notes - The strategy extracts all digits first to determine the `keepFrom` boundary (total digits minus 4), then makes a single linear pass over the original string to rebuild the result. This avoids multi-pass scanning and keeps the logic O(n). - `Math.max(0, digits.length - 4)` ensures that strings with 4 or fewer digits are returned with all digits preserved (no masking applied), matching the principle of least surprise. - The `rule` parameter is accepted to satisfy the uniform strategy interface but none of its fields are consumed.