{"version":3,"sources":["../../src/pdp/constants.ts","../../src/pdp/scanner.ts","../../src/pdp/mask.ts","../../src/pdp/anonymize.ts"],"names":["DEFAULT_OPTIONS","escapeForLog"],"mappings":"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","file":"index.cjs","sourcesContent":["import { OPERATOR_PREFIXES } from '../phone/constants';\n\nexport const NIK_SCAN_PATTERN = /\\b(\\d{16})\\b/g;\nexport const NPWP_SCAN_PATTERN = /\\b(\\d{2}\\.\\d{3}\\.\\d{3}[. -]?\\d{1,2}[. -]?\\d{3}(?:[. -]?\\d{3})?)\\b/g;\nexport const PHONE_SCAN_PATTERN = /(?:\\+62|62|0)[2-9]\\d{7,11}/g;\nexport const EMAIL_SCAN_PATTERN = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*/gi;\nexport const PASSPORT_SCAN_PATTERN = /\\b([A-Z]{1,2}\\d{6,8})\\b/gi;\nexport const BPJS_SCAN_PATTERN = /(?:^|[^\\d])(0{3}\\d{8,11}|BPJS\\d{9,13})(?![^\\d])/gi;\nexport const BANK_ACCOUNT_SCAN_PATTERN = /\\b(\\d{7,16})\\b/g;\n\nexport const VALID_PHONE_PREFIXES = Object.keys(OPERATOR_PREFIXES);\nexport const MAX_DEPTH = 10;\nexport const CIRCULAR_PLACEHOLDER = '[Circular]';\nexport const NIK_STRICT = /^\\d{16}$/;\nexport const NPWP_STRICT = /^\\d{2}\\.\\d{3}\\.\\d{3}\\.\\d{1}\\.\\d{3}$/;\nexport const PHONE_STRICT = /^(?:\\+62|62|0)\\d{9,12}$/;\n","import { PIIFinding, PIIType, PIIConfidence } from './types';\nimport {\n  NIK_SCAN_PATTERN,\n  NPWP_SCAN_PATTERN,\n  PHONE_SCAN_PATTERN,\n  EMAIL_SCAN_PATTERN,\n  PASSPORT_SCAN_PATTERN,\n  BPJS_SCAN_PATTERN,\n  BANK_ACCOUNT_SCAN_PATTERN,\n} from './constants';\n\ninterface RawMatch {\n  type: PIIType;\n  value: string;\n  startIndex: number;\n  endIndex: number;\n}\n\n/**\n * Scans text for Personally Identifiable Information (PII).\n *\n * Detects Indonesian-specific PII types:\n * - NIK (National ID number)\n * - NPWP (Tax ID number)\n * - Phone numbers (Indonesian format)\n * - Email addresses\n * - Passport numbers\n * - BPJS ID numbers\n * - Bank account numbers\n *\n * @param text - Text to scan for PII\n * @returns Array of PII findings with type, value, position, and confidence\n *\n * @example\n * ```typescript\n * const text = \"NIK saya 3271054108750001, email budi@email.com\";\n * const findings = scanPII(text);\n * // [{ type: 'nik', value: '3271054108750001', startIndex: 10, ... },\n * //  { type: 'email', value: 'budi@email.com', startIndex: 34, ... }]\n * ```\n *\n * @example\n * For data privacy audit:\n * ```typescript\n * const doc = \"Kirim ke Jl. Sudirman 45, NIK 3271054108750001\";\n * const piiLocations = scanPII(doc);\n * // Use findings to mask/redact before sharing\n * ```\n */\nexport function scanPII(text: string): PIIFinding[] {\n  if (!text || typeof text !== 'string') {\n    return [];\n  }\n\n  const rawMatches: RawMatch[] = [];\n  const patterns: Array<{ type: PIIType; pattern: RegExp }> = [\n    { type: 'nik', pattern: NIK_SCAN_PATTERN },\n    { type: 'npwp', pattern: NPWP_SCAN_PATTERN },\n    { type: 'phone', pattern: PHONE_SCAN_PATTERN },\n    { type: 'email', pattern: EMAIL_SCAN_PATTERN },\n    { type: 'passport', pattern: PASSPORT_SCAN_PATTERN },\n    { type: 'bpjs', pattern: BPJS_SCAN_PATTERN },\n    { type: 'bank-account', pattern: BANK_ACCOUNT_SCAN_PATTERN },\n  ];\n\n  for (const { type, pattern } of patterns) {\n    for (const match of text.matchAll(pattern)) {\n      const value = match[1] || match[0];\n      rawMatches.push({\n        type,\n        value,\n        startIndex: match.index!,\n        endIndex: match.index! + value.length,\n      });\n    }\n  }\n\n  const resolved = resolveOverlaps(rawMatches);\n\n  return resolved.map(m => ({\n    type: m.type,\n    value: m.value,\n    startIndex: m.startIndex,\n    endIndex: m.endIndex,\n    confidence: calculateConfidence(m.type, m.value),\n  })).sort((a, b) => a.startIndex - b.startIndex);\n}\n\nfunction resolveOverlaps(matches: RawMatch[]): RawMatch[] {\n  if (matches.length === 0) return [];\n\n  const typePriority: Record<PIIType, number> = {\n    email: 1,\n    nik: 2,\n    npwp: 3,\n    phone: 4,\n    passport: 5,\n    bpjs: 6,\n    'bank-account': 7,\n  };\n\n  const sorted = [...matches].sort((a, b) => {\n    if (a.startIndex !== b.startIndex) {\n      return a.startIndex - b.startIndex;\n    }\n    if (a.endIndex !== b.endIndex) {\n      return b.endIndex - a.endIndex;\n    }\n    return (typePriority[a.type] || 999) - (typePriority[b.type] || 999);\n  });\n\n  const result: RawMatch[] = [];\n\n  for (const match of sorted) {\n    const last = result[result.length - 1];\n\n    if (last && match.startIndex < last.endIndex) {\n      const lastPriority = typePriority[last.type] || 999;\n      const matchPriority = typePriority[match.type] || 999;\n\n      if (matchPriority < lastPriority) {\n        result[result.length - 1] = match;\n      }\n    } else {\n      result.push(match);\n    }\n  }\n\n  return result;\n}\n\nfunction calculateConfidence(type: PIIType, value: string): PIIConfidence {\n  const cleanValue = value.replace(/[-.]+/g, '');\n\n  switch (type) {\n    case 'nik':\n      return /^\\d{16}$/.test(cleanValue) ? 'high' : 'low';\n\n    case 'npwp':\n      return /^\\d{15}$/.test(cleanValue) ? 'high' : 'medium';\n\n    case 'phone': {\n      return 'high';\n    }\n\n    case 'email':\n      return value.includes('@') && value.includes('.') ? 'high' : 'low';\n\n    case 'passport':\n      return /^[A-Z]{1,2}\\d{6,8}$/i.test(value) ? 'high' : 'low';\n\n    case 'bpjs':\n      return value.startsWith('000') || value.startsWith('BPJS') ? 'high' : 'medium';\n\n    case 'bank-account':\n      return /^\\d{10,16}$/.test(cleanValue) ? 'medium' : 'low';\n\n    default:\n      return 'low';\n  }\n}","import { PIIOptions } from './types';\n\nconst DEFAULT_OPTIONS: Required<PIIOptions> = {\n  explicitKeys: [],\n  autoDetectPII: true,\n  maskChar: '*',\n  maskStrategy: 'partial',\n  replacementText: '',\n  escapeForLog: false,\n};\n\n/**\n * Masks PII found in a string using specified mask character.\n *\n * Auto-detects PII types (NIK, NPWP, phone, email, etc.) and\n * replaces them with masked version. Use for logging, display,\n * or data sharing where original values shouldn't be visible.\n *\n * @param text - Text containing PII to mask\n * @param options - Masking options (strategy, char, etc.)\n * @returns Text with PII masked\n *\n * @example\n * ```typescript\n * maskStringPDP(\"NIK: 3271054108750001\");\n// \"NIK: 3271054108******\" (partial mask)\n * ```\n *\n * @example\n * Full mask for logs:\n * ```typescript\n * maskStringPDP(\"Email: budi@email.com\", { maskStrategy: 'full' });\n * // \"Email: ***************\"\n * ```\n *\n * @example\n * Custom replacement:\n * ```typescript\n * maskStringPDP(\"Phone: 081234567890\", { replacementText: '[REDACTED]' });\n * // \"Phone: [REDACTED]\"\n * ```\n */\nexport function maskStringPDP(text: string, options?: PIIOptions): string {\n  if (!text || typeof text !== 'string') {\n    return text;\n  }\n\n  const opts = { ...DEFAULT_OPTIONS, ...options };\n\n  if (!opts.autoDetectPII) {\n    return opts.escapeForLog ? escapeForLog(text) : text;\n  }\n\n  return text;\n}\n\nexport function applyMask(value: string, opts: Required<PIIOptions>): string {\n  if (opts.replacementText) {\n    return opts.replacementText;\n  }\n\n  if (opts.maskStrategy === 'full') {\n    return opts.maskChar.repeat(value.length);\n  }\n\n  const len = value.length;\n  if (len <= 4) {\n    return opts.maskChar.repeat(len);\n  }\n\n  const visibleStart = Math.min(4, Math.floor(len * 0.3));\n  const visibleEnd = visibleStart;\n  const maskedMiddle = len - visibleStart - visibleEnd;\n\n  if (maskedMiddle <= 0) {\n    return opts.maskChar.repeat(len);\n  }\n\n  return value.slice(0, visibleStart) + opts.maskChar.repeat(maskedMiddle) + value.slice(len - visibleEnd);\n}\n\nexport function escapeForLog(text: string): string {\n  return text\n    .replace(/\\\\/g, '\\\\\\\\')\n    .replace(/\\n/g, '\\\\n')\n    .replace(/\\t/g, '\\\\t')\n    .replace(/\"/g, '\\\\\"');\n}","import { PIIOptions } from './types';\nimport { scanPII } from './scanner';\nimport { MAX_DEPTH, CIRCULAR_PLACEHOLDER } from './constants';\n\nconst DEFAULT_OPTIONS: Required<PIIOptions> = {\n  explicitKeys: [],\n  autoDetectPII: true,\n  maskChar: '*',\n  maskStrategy: 'partial',\n  replacementText: '',\n  escapeForLog: false,\n};\n\n/**\n * Anonymizes PII in a string by replacing with masked version.\n *\n * Scans text for PII patterns and replaces detected values with\n * masked equivalents. Use for data sharing, logging, or display.\n *\n * @param text - Text containing PII to anonymize\n * @param options - Anonymization options\n * @returns Text with PII replaced (original value not recoverable)\n *\n * @example\n * ```typescript\n * anonymizeString(\"NIK 3271054108750001\");\n// \"NIK ***************\"\n * ```\n *\n * @example\n * For API response sanitization:\n * ```typescript\n * const response = { name: \"Budi\", nik: \"3271054108750001\" };\n * const sanitized = anonymizeString(JSON.stringify(response));\n * // Useful before logging or returning to client\n * ```\n */\nexport function anonymizeString(text: string, options?: PIIOptions): string {\n  if (!text || typeof text !== 'string') {\n    return text;\n  }\n\n  const opts = { ...DEFAULT_OPTIONS, ...options };\n\n  if (!opts.autoDetectPII && opts.explicitKeys.length === 0) {\n    return opts.escapeForLog ? escapeForLog(text) : text;\n  }\n\n  const findings = scanPII(text);\n\n  if (findings.length === 0) {\n    return opts.escapeForLog ? escapeForLog(text) : text;\n  }\n\n  let result = '';\n  let lastIndex = 0;\n\n  for (const finding of findings) {\n    result += text.slice(lastIndex, finding.startIndex);\n    result += applyMask(finding.value, opts);\n    lastIndex = finding.endIndex;\n  }\n\n  result += text.slice(lastIndex);\n\n  return opts.escapeForLog ? escapeForLog(result) : result;\n}\n\n/**\n * Recursively anonymizes PII in an object or array.\n *\n * Deep-scans nested structures (objects, arrays) for string values\n * containing PII and replaces them with masked version. Handles\n * circular references and preserves non-PII data types.\n *\n * @param payload - Object, array, or primitive to anonymize\n * @param options - Anonymization options\n * @returns Same structure with PII values replaced\n *\n * @example\n * ```typescript\n * const user = {\n *   name: \"Budi\",\n *   id: \"3271054108750001\",\n *   email: \"budi@email.com\"\n * };\n * anonymizePDP(user);\n * // { name: \"Budi\", id: \"***********\", email: \"***@***.com\" }\n * ```\n *\n * @example\n * For API response sanitization:\n * ```typescript\n * const response = {\n *   data: { name: \"Budi\", nik: \"3271054108750001\" },\n *   meta: { timestamp: \"2024-01-01\" }\n * };\n * anonymizePDP(response);\n * // Only string values with PII are masked, meta preserved\n * ```\n */\nexport function anonymizePDP<T>(payload: T, options?: PIIOptions): T {\n  if (payload === null || payload === undefined) {\n    return payload;\n  }\n\n  const opts = { ...DEFAULT_OPTIONS, ...options };\n  const seen = new WeakSet<object>();\n\n  return traverse(payload, opts, 0, seen);\n}\n\nfunction traverse<T>(value: T, opts: Required<PIIOptions>, depth: number, seen: WeakSet<object>): T {\n  if (depth > MAX_DEPTH) {\n    return value;\n  }\n\n  if (value === null || value === undefined) {\n    return value;\n  }\n\n  if (typeof value === 'string') {\n    return anonymizeString(value, opts) as unknown as T;\n  }\n\n  if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'function') {\n    return value;\n  }\n\n  if (Array.isArray(value)) {\n    return value.map(item => traverse(item, opts, depth + 1, seen)) as unknown as T;\n  }\n\n  if (typeof value === 'object') {\n    if (seen.has(value as object)) {\n      return CIRCULAR_PLACEHOLDER as unknown as T;\n    }\n    seen.add(value as object);\n\n    const result: Record<string, unknown> = {};\n    for (const key of Object.keys(value)) {\n      const keyMatch = opts.explicitKeys.some(k => {\n        if (typeof k === 'string') {\n          return key.toLowerCase().includes(k.toLowerCase());\n        }\n        return k.test(key);\n      });\n\n      if (opts.autoDetectPII || keyMatch) {\n        const val = (value as Record<string, unknown>)[key];\n\n        if (typeof val === 'string' && keyMatch) {\n          result[key] = opts.maskChar.repeat(val.length);\n        } else {\n          result[key] = traverse(val, opts, depth + 1, seen);\n        }\n      } else {\n        result[key] = traverse((value as Record<string, unknown>)[key], opts, depth + 1, seen);\n      }\n    }\n    return result as unknown as T;\n  }\n\n  return value;\n}\n\nfunction applyMask(value: string, opts: Required<PIIOptions>): string {\n  if (opts.replacementText) {\n    return opts.replacementText;\n  }\n\n  if (opts.maskStrategy === 'full') {\n    return opts.maskChar.repeat(value.length);\n  }\n\n  const len = value.length;\n  if (len <= 4) {\n    return opts.maskChar.repeat(len);\n  }\n\n  const visibleStart = Math.min(4, Math.floor(len * 0.3));\n  const visibleEnd = visibleStart;\n  const maskedMiddle = len - visibleStart - visibleEnd;\n\n  if (maskedMiddle <= 0) {\n    return opts.maskChar.repeat(len);\n  }\n\n  return value.slice(0, visibleStart) + opts.maskChar.repeat(maskedMiddle) + value.slice(len - visibleEnd);\n}\n\nfunction escapeForLog(text: string): string {\n  return text\n    .replace(/\\\\/g, '\\\\\\\\')\n    .replace(/\\n/g, '\\\\n')\n    .replace(/\\t/g, '\\\\t')\n    .replace(/\"/g, '\\\\\"');\n}"]}