@indodev/toolkit 0.2.0 → 0.3.0
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/{compare-B1MKSOWV.d.cts → compare-BIodyGn7.d.cts} +54 -1
- package/dist/{compare-B1MKSOWV.d.ts → compare-BIodyGn7.d.ts} +54 -1
- package/dist/currency/index.cjs +23 -0
- package/dist/currency/index.cjs.map +1 -1
- package/dist/currency/index.d.cts +367 -3
- package/dist/currency/index.d.ts +367 -3
- package/dist/currency/index.js +21 -1
- package/dist/currency/index.js.map +1 -1
- package/dist/index.cjs +1117 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +110 -4
- package/dist/index.d.ts +110 -4
- package/dist/index.js +1095 -1
- package/dist/index.js.map +1 -1
- package/dist/nik/index.cjs +44 -0
- package/dist/nik/index.cjs.map +1 -1
- package/dist/nik/index.d.cts +44 -1
- package/dist/nik/index.d.ts +44 -1
- package/dist/nik/index.js +41 -1
- package/dist/nik/index.js.map +1 -1
- package/dist/phone/index.cjs +42 -0
- package/dist/phone/index.cjs.map +1 -1
- package/dist/phone/index.d.cts +57 -1
- package/dist/phone/index.d.ts +57 -1
- package/dist/phone/index.js +39 -1
- package/dist/phone/index.js.map +1 -1
- package/dist/text/index.cjs +811 -0
- package/dist/text/index.cjs.map +1 -1
- package/dist/text/index.d.cts +1 -1
- package/dist/text/index.d.ts +1 -1
- package/dist/text/index.js +808 -1
- package/dist/text/index.js.map +1 -1
- package/package.json +38 -18
- package/LICENCE +0 -21
- package/dist/words-Dy8iYkbv.d.cts +0 -333
- package/dist/words-Dy8iYkbv.d.ts +0 -333
package/dist/nik/index.cjs
CHANGED
|
@@ -210,7 +210,51 @@ function maskNIK(nik, options = {}) {
|
|
|
210
210
|
return startPart + char.repeat(maskLength) + endPart;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
// src/nik/utils.ts
|
|
214
|
+
function getAge(nik, referenceDate = /* @__PURE__ */ new Date()) {
|
|
215
|
+
const info = parseNIK(nik);
|
|
216
|
+
if (!info || !info.birthDate) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
const birthDate = info.birthDate;
|
|
220
|
+
let age = referenceDate.getFullYear() - birthDate.getFullYear();
|
|
221
|
+
const m = referenceDate.getMonth() - birthDate.getMonth();
|
|
222
|
+
if (m < 0 || m === 0 && referenceDate.getDate() < birthDate.getDate()) {
|
|
223
|
+
age--;
|
|
224
|
+
}
|
|
225
|
+
return age;
|
|
226
|
+
}
|
|
227
|
+
function formatBirthDate(nik, options = {
|
|
228
|
+
day: "numeric",
|
|
229
|
+
month: "long",
|
|
230
|
+
year: "numeric"
|
|
231
|
+
}, locale = "id-ID") {
|
|
232
|
+
const info = parseNIK(nik);
|
|
233
|
+
if (!info || !info.birthDate) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
return new Intl.DateTimeFormat(locale, options).format(info.birthDate);
|
|
237
|
+
}
|
|
238
|
+
function isValidForGender(nik, gender) {
|
|
239
|
+
const info = parseNIK(nik);
|
|
240
|
+
if (!info) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
return info.gender === gender;
|
|
244
|
+
}
|
|
245
|
+
function isValidForBirthDate(nik, birthDate) {
|
|
246
|
+
const info = parseNIK(nik);
|
|
247
|
+
if (!info || !info.birthDate) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
return info.birthDate.getFullYear() === birthDate.getFullYear() && info.birthDate.getMonth() === birthDate.getMonth() && info.birthDate.getDate() === birthDate.getDate();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
exports.formatBirthDate = formatBirthDate;
|
|
213
254
|
exports.formatNIK = formatNIK;
|
|
255
|
+
exports.getAge = getAge;
|
|
256
|
+
exports.isValidForBirthDate = isValidForBirthDate;
|
|
257
|
+
exports.isValidForGender = isValidForGender;
|
|
214
258
|
exports.maskNIK = maskNIK;
|
|
215
259
|
exports.parseNIK = parseNIK;
|
|
216
260
|
exports.validateNIK = validateNIK;
|
package/dist/nik/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/nik/constants.ts","../../src/nik/validate.ts","../../src/nik/parse.ts","../../src/nik/format.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Indonesian province codes and names\n * Based on Dukcapil Kemendagri data\n */\nexport const PROVINCES: Record<string, string> = {\n '11': 'Aceh',\n '12': 'Sumatera Utara',\n '13': 'Sumatera Barat',\n '14': 'Riau',\n '15': 'Jambi',\n '16': 'Sumatera Selatan',\n '17': 'Bengkulu',\n '18': 'Lampung',\n '19': 'Kepulauan Bangka Belitung',\n '21': 'Kepulauan Riau',\n '31': 'DKI Jakarta',\n '32': 'Jawa Barat',\n '33': 'Jawa Tengah',\n '34': 'DI Yogyakarta',\n '35': 'Jawa Timur',\n '36': 'Banten',\n '51': 'Bali',\n '52': 'Nusa Tenggara Barat',\n '53': 'Nusa Tenggara Timur',\n '61': 'Kalimantan Barat',\n '62': 'Kalimantan Tengah',\n '63': 'Kalimantan Selatan',\n '64': 'Kalimantan Timur',\n '65': 'Kalimantan Utara',\n '71': 'Sulawesi Utara',\n '72': 'Sulawesi Tengah',\n '73': 'Sulawesi Selatan',\n '74': 'Sulawesi Tenggara',\n '75': 'Gorontalo',\n '76': 'Sulawesi Barat',\n '81': 'Maluku',\n '82': 'Maluku Utara',\n '91': 'Papua',\n '92': 'Papua Barat',\n '93': 'Papua Selatan',\n '94': 'Papua Tengah',\n '95': 'Papua Pegunungan',\n '96': 'Papua Barat Daya',\n};\n\n/**\n * Regency codes for each province (simplified - only major ones)\n * In a real implementation, you'd have complete data\n */\nexport const REGENCIES: Record<string, Record<string, string>> = {\n '32': {\n '01': 'Kab. Bogor',\n '02': 'Kab. Sukabumi',\n '03': 'Kab. Cianjur',\n '71': 'Kota Bandung',\n '72': 'Kota Bekasi',\n '73': 'Kota Depok',\n },\n '31': { \n '01': 'Kota Jakarta Selatan',\n '02': 'Kota Jakarta Timur',\n '03': 'Kota Jakarta Pusat',\n '04': 'Kota Jakarta Barat',\n '05': 'Kota Jakarta Utara',\n },\n};","import { PROVINCES } from './constants';\n\n/**\n * Validates a NIK (Nomor Induk Kependudukan) format.\n *\n * A valid NIK must:\n * - Be exactly 16 digits\n * - Have a valid province code (positions 1-2)\n * - Have a valid date (positions 7-12)\n * - Not be in the future\n * - Not be before 1900\n *\n * For female NIKs, the day is encoded as (actual day + 40).\n * For example, a female born on the 15th would have day = 55.\n *\n * @param nik - The 16-digit NIK string to validate\n * @returns `true` if the NIK is valid, `false` otherwise\n *\n * @example\n * ```typescript\n * validateNIK('3201234567890123'); // true - valid NIK\n * validateNIK('1234'); // false - wrong length\n * validateNIK('9912345678901234'); // false - invalid province\n * ```\n *\n * @public\n */\nexport function validateNIK(nik: string): boolean {\n if (!/^\\d{16}$/.test(nik)) {\n return false;\n }\n\n const provinceCode = nik.substring(0, 2);\n if (!PROVINCES[provinceCode]) {\n return false;\n }\n\n const yearStr = nik.substring(6, 8);\n const monthStr = nik.substring(8, 10);\n const dayStr = nik.substring(10, 12);\n\n const year = parseInt(yearStr, 10);\n const fullYear = year > 30 ? 1900 + year : 2000 + year;\n\n const month = parseInt(monthStr, 10);\n let day = parseInt(dayStr, 10);\n\n if (day > 40) {\n day = day - 40;\n }\n\n if (month < 1 || month > 12) {\n return false;\n }\n\n if (day < 1 || day > 31) {\n return false;\n }\n\n const testDate = new Date(fullYear, month - 1, day);\n if (\n testDate.getFullYear() !== fullYear ||\n testDate.getMonth() !== month - 1 ||\n testDate.getDate() !== day\n ) {\n return false;\n }\n\n const now = new Date();\n if (testDate > now || testDate < new Date(1900, 0, 1)) {\n return false;\n }\n\n return true;\n}\n","import { PROVINCES, REGENCIES } from './constants';\nimport { NIKInfo } from './types';\n\n/**\n * Parses a NIK and extracts all embedded information.\n *\n * Extracts province, regency, district codes, birth date, gender,\n * and serial number from a 16-digit NIK string.\n *\n * @param nik - The 16-digit NIK string to parse\n * @returns Parsed NIK information, or `null` if the NIK format is invalid\n *\n * @example\n * Parse a valid male NIK:\n * ```typescript\n * const info = parseNIK('3201018901310123');\n * console.log(info);\n * // {\n * // province: { code: '32', name: 'Jawa Barat' },\n * // regency: { code: '01', name: 'Kab. Bogor' },\n * // district: { code: '01', name: null },\n * // birthDate: Date(1989, 0, 31), // Jan 31, 1989\n * // gender: 'male',\n * // serialNumber: '0123',\n * // isValid: true\n * // }\n * ```\n *\n * @example\n * Parse a female NIK (day + 40):\n * ```typescript\n * const info = parseNIK('3201019508550123');\n * console.log(info.gender); // 'female'\n * console.log(info.birthDate); // Date(1995, 7, 15) - Aug 15, 1995\n * ```\n *\n * @example\n * Invalid NIK returns null:\n * ```typescript\n * const info = parseNIK('invalid');\n * console.log(info); // null\n * ```\n *\n * @public\n */\nexport function parseNIK(nik: string): NIKInfo | null {\n if (!/^\\d{16}$/.test(nik)) {\n return null;\n }\n\n const provinceCode = nik.substring(0, 2);\n const regencyCode = nik.substring(2, 4);\n const districtCode = nik.substring(4, 6);\n const yearStr = nik.substring(6, 8);\n const monthStr = nik.substring(8, 10);\n const dayStr = nik.substring(10, 12);\n const serialNumber = nik.substring(12, 16);\n\n const province = PROVINCES[provinceCode];\n if (!province) {\n return null;\n }\n\n const regencies = REGENCIES[provinceCode] || {};\n const regency = regencies[regencyCode] || 'Unknown';\n\n let day = parseInt(dayStr, 10);\n const month = parseInt(monthStr, 10);\n const year = parseInt(yearStr, 10);\n\n let gender: 'male' | 'female' | null = null;\n if (day > 40) {\n gender = 'female';\n day -= 40;\n } else {\n gender = 'male';\n }\n\n const fullYear = year > 30 ? 1900 + year : 2000 + year;\n\n const birthDate = new Date(fullYear, month - 1, day);\n if (\n birthDate.getFullYear() !== fullYear ||\n birthDate.getMonth() !== month - 1 ||\n birthDate.getDate() !== day\n ) {\n return null;\n }\n\n return {\n province: {\n code: provinceCode,\n name: province,\n },\n regency: {\n code: regencyCode,\n name: regency,\n },\n district: {\n code: districtCode,\n name: null,\n },\n birthDate,\n gender,\n serialNumber,\n isValid: true,\n };\n}","import { MaskOptions } from './types';\n\n/**\n * Formats a NIK with separators for better readability.\n *\n * Groups the NIK into logical segments: province, regency, district,\n * year, month, day, and serial number.\n *\n * @param nik - The 16-digit NIK string to format\n * @param separator - Character to use as separator\n * @returns Formatted NIK string, or original string if invalid format\n *\n * @example\n * Default separator (dash):\n * ```typescript\n * formatNIK('3201234567890123');\n * // '32-01-23-45-67-89-0123'\n * ```\n *\n * @example\n * Custom separator:\n * ```typescript\n * formatNIK('3201234567890123', ' ');\n * // '32 01 23 45 67 89 0123'\n * ```\n *\n * @example\n * Invalid NIK returns as-is:\n * ```typescript\n * formatNIK('1234');\n * // '1234'\n * ```\n *\n * @public\n */\nexport function formatNIK(nik: string, separator: string = '-'): string {\n if (!/^\\d{16}$/.test(nik)) {\n return nik;\n }\n\n // Format: PP-KK-DD-YY-MM-DD-XXXX\n // PP = Province (2 digits)\n // KK = Regency (2 digits)\n // DD = District (2 digits)\n // YY = Year (2 digits)\n // MM = Month (2 digits)\n // DD = Day (2 digits, +40 for female)\n // XXXX = Serial number (4 digits)\n return [\n nik.substring(0, 2), // Province\n nik.substring(2, 4), // Regency\n nik.substring(4, 6), // District\n nik.substring(6, 8), // Year\n nik.substring(8, 10), // Month\n nik.substring(10, 12), // Day\n nik.substring(12, 16), // Serial\n ].join(separator);\n}\n\n/**\n * Masks a NIK to protect privacy while keeping partial visibility.\n *\n * By default, shows the first 4 and last 4 digits, masking the middle 8.\n * Optionally formats the masked NIK with separators.\n *\n * @param nik - The 16-digit NIK string to mask\n * @param options - Masking configuration options\n * @returns Masked NIK string, or original string if invalid format\n *\n * @example\n * Default masking (first 4, last 4):\n * ```typescript\n * maskNIK('3201234567890123');\n * // '3201********0123'\n * ```\n *\n * @example\n * Custom mask character:\n * ```typescript\n * maskNIK('3201234567890123', { char: 'X' });\n * // '3201XXXXXXXX0123'\n * ```\n *\n * @example\n * With separator:\n * ```typescript\n * maskNIK('3201234567890123', { separator: '-' });\n * // '32-01-**-**-**-**-0123'\n * ```\n *\n * @example\n * Custom start and end:\n * ```typescript\n * maskNIK('3201234567890123', { start: 6, end: 4 });\n * // '320123******0123'\n * ```\n *\n * @public\n */\nexport function maskNIK(nik: string, options: MaskOptions = {}): string {\n if (!/^\\d{16}$/.test(nik)) {\n return nik;\n }\n\n const { start = 4, end = 4, char = '*', separator } = options;\n\n if (start + end >= 16) {\n return nik;\n }\n\n if (separator) {\n // Format with separator first, then apply masking\n const formatted = formatNIK(nik, separator);\n const parts = formatted.split(separator);\n\n // Calculate which parts to mask\n // Format: PP-KK-DD-YY-MM-DD-XXXX (7 parts)\n // Mask parts based on character positions\n let charCount = 0;\n const maskedParts = parts.map((part) => {\n const partStart = charCount;\n const partEnd = charCount + part.length;\n charCount += part.length;\n\n // Check if this part should be fully/partially masked\n if (partEnd <= start) {\n // Fully visible (before start)\n return part;\n } else if (partStart >= 16 - end) {\n // Fully visible (after end)\n return part;\n } else if (partStart >= start && partEnd <= 16 - end) {\n // Fully masked\n return char.repeat(part.length);\n } else {\n // Partially masked\n return part\n .split('')\n .map((ch, idx) => {\n const pos = partStart + idx;\n if (pos < start || pos >= 16 - end) {\n return ch;\n }\n return char;\n })\n .join('');\n }\n });\n\n return maskedParts.join(separator);\n }\n\n // Without separator: simple masking\n const startPart = nik.substring(0, start);\n const endPart = nik.substring(16 - end);\n const maskLength = 16 - start - end;\n return startPart + char.repeat(maskLength) + endPart;\n}"]}
|
|
1
|
+
{"version":3,"sources":["../../src/nik/constants.ts","../../src/nik/validate.ts","../../src/nik/parse.ts","../../src/nik/format.ts","../../src/nik/utils.ts"],"names":[],"mappings":";;;AAIO,IAAM,SAAA,GAAoC;AAAA,EAC/C,IAAA,EAAM,MAAA;AAAA,EACN,IAAA,EAAM,gBAAA;AAAA,EACN,IAAA,EAAM,gBAAA;AAAA,EACN,IAAA,EAAM,MAAA;AAAA,EACN,IAAA,EAAM,OAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM,UAAA;AAAA,EACN,IAAA,EAAM,SAAA;AAAA,EACN,IAAA,EAAM,2BAAA;AAAA,EACN,IAAA,EAAM,gBAAA;AAAA,EACN,IAAA,EAAM,aAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA,EACN,IAAA,EAAM,aAAA;AAAA,EACN,IAAA,EAAM,eAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA,EACN,IAAA,EAAM,QAAA;AAAA,EACN,IAAA,EAAM,MAAA;AAAA,EACN,IAAA,EAAM,qBAAA;AAAA,EACN,IAAA,EAAM,qBAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM,mBAAA;AAAA,EACN,IAAA,EAAM,oBAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM,gBAAA;AAAA,EACN,IAAA,EAAM,iBAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM,mBAAA;AAAA,EACN,IAAA,EAAM,WAAA;AAAA,EACN,IAAA,EAAM,gBAAA;AAAA,EACN,IAAA,EAAM,QAAA;AAAA,EACN,IAAA,EAAM,cAAA;AAAA,EACN,IAAA,EAAM,OAAA;AAAA,EACN,IAAA,EAAM,aAAA;AAAA,EACN,IAAA,EAAM,eAAA;AAAA,EACN,IAAA,EAAM,cAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM;AACR,CAAA;AAMO,IAAM,SAAA,GAAoD;AAAA,EAC/D,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,IAAA,EAAM,eAAA;AAAA,IACN,IAAA,EAAM,cAAA;AAAA,IACN,IAAA,EAAM,cAAA;AAAA,IACN,IAAA,EAAM,aAAA;AAAA,IACN,IAAA,EAAM;AAAA,GACR;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,sBAAA;AAAA,IACN,IAAA,EAAM,oBAAA;AAAA,IACN,IAAA,EAAM,oBAAA;AAAA,IACN,IAAA,EAAM,oBAAA;AAAA,IACN,IAAA,EAAM;AAAA;AAEV,CAAA;;;ACtCO,SAAS,YAAY,GAAA,EAAsB;AAChD,EAAA,IAAI,CAAC,UAAA,CAAW,IAAA,CAAK,GAAG,CAAA,EAAG;AACzB,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAA,GAAe,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AACvC,EAAA,IAAI,CAAC,SAAA,CAAU,YAAY,CAAA,EAAG;AAC5B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AAClC,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,EAAE,CAAA;AACpC,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,SAAA,CAAU,EAAA,EAAI,EAAE,CAAA;AAEnC,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,OAAA,EAAS,EAAE,CAAA;AACjC,EAAA,MAAM,QAAA,GAAW,IAAA,GAAO,EAAA,GAAK,IAAA,GAAO,OAAO,GAAA,GAAO,IAAA;AAElD,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,EAAU,EAAE,CAAA;AACnC,EAAA,IAAI,GAAA,GAAM,QAAA,CAAS,MAAA,EAAQ,EAAE,CAAA;AAE7B,EAAA,IAAI,MAAM,EAAA,EAAI;AACZ,IAAA,GAAA,GAAM,GAAA,GAAM,EAAA;AAAA,EACd;AAEA,EAAA,IAAI,KAAA,GAAQ,CAAA,IAAK,KAAA,GAAQ,EAAA,EAAI;AAC3B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,GAAA,GAAM,CAAA,IAAK,GAAA,GAAM,EAAA,EAAI;AACvB,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,WAAW,IAAI,IAAA,CAAK,QAAA,EAAU,KAAA,GAAQ,GAAG,GAAG,CAAA;AAClD,EAAA,IACE,QAAA,CAAS,WAAA,EAAY,KAAM,QAAA,IAC3B,QAAA,CAAS,QAAA,EAAS,KAAM,KAAA,GAAQ,CAAA,IAChC,QAAA,CAAS,OAAA,EAAQ,KAAM,GAAA,EACvB;AACA,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AACrB,EAAA,IAAI,QAAA,GAAW,OAAO,QAAA,GAAW,IAAI,KAAK,IAAA,EAAM,CAAA,EAAG,CAAC,CAAA,EAAG;AACrD,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAA;AACT;;;AC7BO,SAAS,SAAS,GAAA,EAA6B;AACpD,EAAA,IAAI,CAAC,UAAA,CAAW,IAAA,CAAK,GAAG,CAAA,EAAG;AACzB,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAA,GAAe,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AACvC,EAAA,MAAM,WAAA,GAAc,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AACtC,EAAA,MAAM,YAAA,GAAe,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AACvC,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AAClC,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,EAAE,CAAA;AACpC,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,SAAA,CAAU,EAAA,EAAI,EAAE,CAAA;AACnC,EAAA,MAAM,YAAA,GAAe,GAAA,CAAI,SAAA,CAAU,EAAA,EAAI,EAAE,CAAA;AAEzC,EAAA,MAAM,QAAA,GAAW,UAAU,YAAY,CAAA;AACvC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,SAAA,GAAY,SAAA,CAAU,YAAY,CAAA,IAAK,EAAC;AAC9C,EAAA,MAAM,OAAA,GAAU,SAAA,CAAU,WAAW,CAAA,IAAK,SAAA;AAE1C,EAAA,IAAI,GAAA,GAAM,QAAA,CAAS,MAAA,EAAQ,EAAE,CAAA;AAC7B,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,EAAU,EAAE,CAAA;AACnC,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,OAAA,EAAS,EAAE,CAAA;AAEjC,EAAA,IAAI,MAAA,GAAmC,IAAA;AACvC,EAAA,IAAI,MAAM,EAAA,EAAI;AACZ,IAAA,MAAA,GAAS,QAAA;AACT,IAAA,GAAA,IAAO,EAAA;AAAA,EACT,CAAA,MAAO;AACL,IAAA,MAAA,GAAS,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,QAAA,GAAW,IAAA,GAAO,EAAA,GAAK,IAAA,GAAO,OAAO,GAAA,GAAO,IAAA;AAElD,EAAA,MAAM,YAAY,IAAI,IAAA,CAAK,QAAA,EAAU,KAAA,GAAQ,GAAG,GAAG,CAAA;AACnD,EAAA,IACE,SAAA,CAAU,WAAA,EAAY,KAAM,QAAA,IAC5B,SAAA,CAAU,QAAA,EAAS,KAAM,KAAA,GAAQ,CAAA,IACjC,SAAA,CAAU,OAAA,EAAQ,KAAM,GAAA,EACxB;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO;AAAA,IACL,QAAA,EAAU;AAAA,MACR,IAAA,EAAM,YAAA;AAAA,MACN,IAAA,EAAM;AAAA,KACR;AAAA,IACA,OAAA,EAAS;AAAA,MACP,IAAA,EAAM,WAAA;AAAA,MACN,IAAA,EAAM;AAAA,KACR;AAAA,IACA,QAAA,EAAU;AAAA,MACR,IAAA,EAAM,YAAA;AAAA,MACN,IAAA,EAAM;AAAA,KACR;AAAA,IACA,SAAA;AAAA,IACA,MAAA;AAAA,IACA,YAAA;AAAA,IACA,OAAA,EAAS;AAAA,GACX;AACF;;;ACxEO,SAAS,SAAA,CAAU,GAAA,EAAa,SAAA,GAAoB,GAAA,EAAa;AACtE,EAAA,IAAI,CAAC,UAAA,CAAW,IAAA,CAAK,GAAG,CAAA,EAAG;AACzB,IAAA,OAAO,GAAA;AAAA,EACT;AAUA,EAAA,OAAO;AAAA,IACL,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AAAA;AAAA,IAClB,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AAAA;AAAA,IAClB,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AAAA;AAAA,IAClB,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AAAA;AAAA,IAClB,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,EAAE,CAAA;AAAA;AAAA,IACnB,GAAA,CAAI,SAAA,CAAU,EAAA,EAAI,EAAE,CAAA;AAAA;AAAA,IACpB,GAAA,CAAI,SAAA,CAAU,EAAA,EAAI,EAAE;AAAA;AAAA,GACtB,CAAE,KAAK,SAAS,CAAA;AAClB;AA0CO,SAAS,OAAA,CAAQ,GAAA,EAAa,OAAA,GAAuB,EAAC,EAAW;AACtE,EAAA,IAAI,CAAC,UAAA,CAAW,IAAA,CAAK,GAAG,CAAA,EAAG;AACzB,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,MAAM,EAAE,QAAQ,CAAA,EAAG,GAAA,GAAM,GAAG,IAAA,GAAO,GAAA,EAAK,WAAU,GAAI,OAAA;AAEtD,EAAA,IAAI,KAAA,GAAQ,OAAO,EAAA,EAAI;AACrB,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,IAAI,SAAA,EAAW;AAEb,IAAA,MAAM,SAAA,GAAY,SAAA,CAAU,GAAA,EAAK,SAAS,CAAA;AAC1C,IAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,KAAA,CAAM,SAAS,CAAA;AAKvC,IAAA,IAAI,SAAA,GAAY,CAAA;AAChB,IAAA,MAAM,WAAA,GAAc,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACtC,MAAA,MAAM,SAAA,GAAY,SAAA;AAClB,MAAA,MAAM,OAAA,GAAU,YAAY,IAAA,CAAK,MAAA;AACjC,MAAA,SAAA,IAAa,IAAA,CAAK,MAAA;AAGlB,MAAA,IAAI,WAAW,KAAA,EAAO;AAEpB,QAAA,OAAO,IAAA;AAAA,MACT,CAAA,MAAA,IAAW,SAAA,IAAa,EAAA,GAAK,GAAA,EAAK;AAEhC,QAAA,OAAO,IAAA;AAAA,MACT,CAAA,MAAA,IAAW,SAAA,IAAa,KAAA,IAAS,OAAA,IAAW,KAAK,GAAA,EAAK;AAEpD,QAAA,OAAO,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA;AAAA,MAChC,CAAA,MAAO;AAEL,QAAA,OAAO,KACJ,KAAA,CAAM,EAAE,EACR,GAAA,CAAI,CAAC,IAAI,GAAA,KAAQ;AAChB,UAAA,MAAM,MAAM,SAAA,GAAY,GAAA;AACxB,UAAA,IAAI,GAAA,GAAM,KAAA,IAAS,GAAA,IAAO,EAAA,GAAK,GAAA,EAAK;AAClC,YAAA,OAAO,EAAA;AAAA,UACT;AACA,UAAA,OAAO,IAAA;AAAA,QACT,CAAC,CAAA,CACA,IAAA,CAAK,EAAE,CAAA;AAAA,MACZ;AAAA,IACF,CAAC,CAAA;AAED,IAAA,OAAO,WAAA,CAAY,KAAK,SAAS,CAAA;AAAA,EACnC;AAGA,EAAA,MAAM,SAAA,GAAY,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,KAAK,CAAA;AACxC,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,SAAA,CAAU,EAAA,GAAK,GAAG,CAAA;AACtC,EAAA,MAAM,UAAA,GAAa,KAAK,KAAA,GAAQ,GAAA;AAChC,EAAA,OAAO,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,UAAU,CAAA,GAAI,OAAA;AAC/C;;;AC/IO,SAAS,MAAA,CACd,GAAA,EACA,aAAA,mBAAsB,IAAI,MAAK,EAChB;AACf,EAAA,MAAM,IAAA,GAAO,SAAS,GAAG,CAAA;AACzB,EAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,IAAA,CAAK,SAAA,EAAW;AAC5B,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAY,IAAA,CAAK,SAAA;AACvB,EAAA,IAAI,GAAA,GAAM,aAAA,CAAc,WAAA,EAAY,GAAI,UAAU,WAAA,EAAY;AAC9D,EAAA,MAAM,CAAA,GAAI,aAAA,CAAc,QAAA,EAAS,GAAI,UAAU,QAAA,EAAS;AAExD,EAAA,IAAI,CAAA,GAAI,KAAM,CAAA,KAAM,CAAA,IAAK,cAAc,OAAA,EAAQ,GAAI,SAAA,CAAU,OAAA,EAAQ,EAAI;AACvE,IAAA,GAAA,EAAA;AAAA,EACF;AAEA,EAAA,OAAO,GAAA;AACT;AAcO,SAAS,eAAA,CACd,KACA,OAAA,GAAsC;AAAA,EACpC,GAAA,EAAK,SAAA;AAAA,EACL,KAAA,EAAO,MAAA;AAAA,EACP,IAAA,EAAM;AACR,CAAA,EACA,SAAiB,OAAA,EACF;AACf,EAAA,MAAM,IAAA,GAAO,SAAS,GAAG,CAAA;AACzB,EAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,IAAA,CAAK,SAAA,EAAW;AAC5B,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAI,KAAK,cAAA,CAAe,MAAA,EAAQ,OAAO,CAAA,CAAE,MAAA,CAAO,KAAK,SAAS,CAAA;AACvE;AASO,SAAS,gBAAA,CACd,KACA,MAAA,EACS;AACT,EAAA,MAAM,IAAA,GAAO,SAAS,GAAG,CAAA;AACzB,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAK,MAAA,KAAW,MAAA;AACzB;AASO,SAAS,mBAAA,CAAoB,KAAa,SAAA,EAA0B;AACzE,EAAA,MAAM,IAAA,GAAO,SAAS,GAAG,CAAA;AACzB,EAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,IAAA,CAAK,SAAA,EAAW;AAC5B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,OACE,KAAK,SAAA,CAAU,WAAA,OAAkB,SAAA,CAAU,WAAA,MAC3C,IAAA,CAAK,SAAA,CAAU,UAAS,KAAM,SAAA,CAAU,UAAS,IACjD,IAAA,CAAK,UAAU,OAAA,EAAQ,KAAM,UAAU,OAAA,EAAQ;AAEnD","file":"index.cjs","sourcesContent":["/**\n * Indonesian province codes and names\n * Based on Dukcapil Kemendagri data\n */\nexport const PROVINCES: Record<string, string> = {\n '11': 'Aceh',\n '12': 'Sumatera Utara',\n '13': 'Sumatera Barat',\n '14': 'Riau',\n '15': 'Jambi',\n '16': 'Sumatera Selatan',\n '17': 'Bengkulu',\n '18': 'Lampung',\n '19': 'Kepulauan Bangka Belitung',\n '21': 'Kepulauan Riau',\n '31': 'DKI Jakarta',\n '32': 'Jawa Barat',\n '33': 'Jawa Tengah',\n '34': 'DI Yogyakarta',\n '35': 'Jawa Timur',\n '36': 'Banten',\n '51': 'Bali',\n '52': 'Nusa Tenggara Barat',\n '53': 'Nusa Tenggara Timur',\n '61': 'Kalimantan Barat',\n '62': 'Kalimantan Tengah',\n '63': 'Kalimantan Selatan',\n '64': 'Kalimantan Timur',\n '65': 'Kalimantan Utara',\n '71': 'Sulawesi Utara',\n '72': 'Sulawesi Tengah',\n '73': 'Sulawesi Selatan',\n '74': 'Sulawesi Tenggara',\n '75': 'Gorontalo',\n '76': 'Sulawesi Barat',\n '81': 'Maluku',\n '82': 'Maluku Utara',\n '91': 'Papua',\n '92': 'Papua Barat',\n '93': 'Papua Selatan',\n '94': 'Papua Tengah',\n '95': 'Papua Pegunungan',\n '96': 'Papua Barat Daya',\n};\n\n/**\n * Regency codes for each province (simplified - only major ones)\n * In a real implementation, you'd have complete data\n */\nexport const REGENCIES: Record<string, Record<string, string>> = {\n '32': {\n '01': 'Kab. Bogor',\n '02': 'Kab. Sukabumi',\n '03': 'Kab. Cianjur',\n '71': 'Kota Bandung',\n '72': 'Kota Bekasi',\n '73': 'Kota Depok',\n },\n '31': { \n '01': 'Kota Jakarta Selatan',\n '02': 'Kota Jakarta Timur',\n '03': 'Kota Jakarta Pusat',\n '04': 'Kota Jakarta Barat',\n '05': 'Kota Jakarta Utara',\n },\n};","import { PROVINCES } from './constants';\n\n/**\n * Validates a NIK (Nomor Induk Kependudukan) format.\n *\n * A valid NIK must:\n * - Be exactly 16 digits\n * - Have a valid province code (positions 1-2)\n * - Have a valid date (positions 7-12)\n * - Not be in the future\n * - Not be before 1900\n *\n * For female NIKs, the day is encoded as (actual day + 40).\n * For example, a female born on the 15th would have day = 55.\n *\n * @param nik - The 16-digit NIK string to validate\n * @returns `true` if the NIK is valid, `false` otherwise\n *\n * @example\n * ```typescript\n * validateNIK('3201234567890123'); // true - valid NIK\n * validateNIK('1234'); // false - wrong length\n * validateNIK('9912345678901234'); // false - invalid province\n * ```\n *\n * @public\n */\nexport function validateNIK(nik: string): boolean {\n if (!/^\\d{16}$/.test(nik)) {\n return false;\n }\n\n const provinceCode = nik.substring(0, 2);\n if (!PROVINCES[provinceCode]) {\n return false;\n }\n\n const yearStr = nik.substring(6, 8);\n const monthStr = nik.substring(8, 10);\n const dayStr = nik.substring(10, 12);\n\n const year = parseInt(yearStr, 10);\n const fullYear = year > 30 ? 1900 + year : 2000 + year;\n\n const month = parseInt(monthStr, 10);\n let day = parseInt(dayStr, 10);\n\n if (day > 40) {\n day = day - 40;\n }\n\n if (month < 1 || month > 12) {\n return false;\n }\n\n if (day < 1 || day > 31) {\n return false;\n }\n\n const testDate = new Date(fullYear, month - 1, day);\n if (\n testDate.getFullYear() !== fullYear ||\n testDate.getMonth() !== month - 1 ||\n testDate.getDate() !== day\n ) {\n return false;\n }\n\n const now = new Date();\n if (testDate > now || testDate < new Date(1900, 0, 1)) {\n return false;\n }\n\n return true;\n}\n","import { PROVINCES, REGENCIES } from './constants';\nimport { NIKInfo } from './types';\n\n/**\n * Parses a NIK and extracts all embedded information.\n *\n * Extracts province, regency, district codes, birth date, gender,\n * and serial number from a 16-digit NIK string.\n *\n * @param nik - The 16-digit NIK string to parse\n * @returns Parsed NIK information, or `null` if the NIK format is invalid\n *\n * @example\n * Parse a valid male NIK:\n * ```typescript\n * const info = parseNIK('3201018901310123');\n * console.log(info);\n * // {\n * // province: { code: '32', name: 'Jawa Barat' },\n * // regency: { code: '01', name: 'Kab. Bogor' },\n * // district: { code: '01', name: null },\n * // birthDate: Date(1989, 0, 31), // Jan 31, 1989\n * // gender: 'male',\n * // serialNumber: '0123',\n * // isValid: true\n * // }\n * ```\n *\n * @example\n * Parse a female NIK (day + 40):\n * ```typescript\n * const info = parseNIK('3201019508550123');\n * console.log(info.gender); // 'female'\n * console.log(info.birthDate); // Date(1995, 7, 15) - Aug 15, 1995\n * ```\n *\n * @example\n * Invalid NIK returns null:\n * ```typescript\n * const info = parseNIK('invalid');\n * console.log(info); // null\n * ```\n *\n * @public\n */\nexport function parseNIK(nik: string): NIKInfo | null {\n if (!/^\\d{16}$/.test(nik)) {\n return null;\n }\n\n const provinceCode = nik.substring(0, 2);\n const regencyCode = nik.substring(2, 4);\n const districtCode = nik.substring(4, 6);\n const yearStr = nik.substring(6, 8);\n const monthStr = nik.substring(8, 10);\n const dayStr = nik.substring(10, 12);\n const serialNumber = nik.substring(12, 16);\n\n const province = PROVINCES[provinceCode];\n if (!province) {\n return null;\n }\n\n const regencies = REGENCIES[provinceCode] || {};\n const regency = regencies[regencyCode] || 'Unknown';\n\n let day = parseInt(dayStr, 10);\n const month = parseInt(monthStr, 10);\n const year = parseInt(yearStr, 10);\n\n let gender: 'male' | 'female' | null = null;\n if (day > 40) {\n gender = 'female';\n day -= 40;\n } else {\n gender = 'male';\n }\n\n const fullYear = year > 30 ? 1900 + year : 2000 + year;\n\n const birthDate = new Date(fullYear, month - 1, day);\n if (\n birthDate.getFullYear() !== fullYear ||\n birthDate.getMonth() !== month - 1 ||\n birthDate.getDate() !== day\n ) {\n return null;\n }\n\n return {\n province: {\n code: provinceCode,\n name: province,\n },\n regency: {\n code: regencyCode,\n name: regency,\n },\n district: {\n code: districtCode,\n name: null,\n },\n birthDate,\n gender,\n serialNumber,\n isValid: true,\n };\n}","import { MaskOptions } from './types';\n\n/**\n * Formats a NIK with separators for better readability.\n *\n * Groups the NIK into logical segments: province, regency, district,\n * year, month, day, and serial number.\n *\n * @param nik - The 16-digit NIK string to format\n * @param separator - Character to use as separator\n * @returns Formatted NIK string, or original string if invalid format\n *\n * @example\n * Default separator (dash):\n * ```typescript\n * formatNIK('3201234567890123');\n * // '32-01-23-45-67-89-0123'\n * ```\n *\n * @example\n * Custom separator:\n * ```typescript\n * formatNIK('3201234567890123', ' ');\n * // '32 01 23 45 67 89 0123'\n * ```\n *\n * @example\n * Invalid NIK returns as-is:\n * ```typescript\n * formatNIK('1234');\n * // '1234'\n * ```\n *\n * @public\n */\nexport function formatNIK(nik: string, separator: string = '-'): string {\n if (!/^\\d{16}$/.test(nik)) {\n return nik;\n }\n\n // Format: PP-KK-DD-YY-MM-DD-XXXX\n // PP = Province (2 digits)\n // KK = Regency (2 digits)\n // DD = District (2 digits)\n // YY = Year (2 digits)\n // MM = Month (2 digits)\n // DD = Day (2 digits, +40 for female)\n // XXXX = Serial number (4 digits)\n return [\n nik.substring(0, 2), // Province\n nik.substring(2, 4), // Regency\n nik.substring(4, 6), // District\n nik.substring(6, 8), // Year\n nik.substring(8, 10), // Month\n nik.substring(10, 12), // Day\n nik.substring(12, 16), // Serial\n ].join(separator);\n}\n\n/**\n * Masks a NIK to protect privacy while keeping partial visibility.\n *\n * By default, shows the first 4 and last 4 digits, masking the middle 8.\n * Optionally formats the masked NIK with separators.\n *\n * @param nik - The 16-digit NIK string to mask\n * @param options - Masking configuration options\n * @returns Masked NIK string, or original string if invalid format\n *\n * @example\n * Default masking (first 4, last 4):\n * ```typescript\n * maskNIK('3201234567890123');\n * // '3201********0123'\n * ```\n *\n * @example\n * Custom mask character:\n * ```typescript\n * maskNIK('3201234567890123', { char: 'X' });\n * // '3201XXXXXXXX0123'\n * ```\n *\n * @example\n * With separator:\n * ```typescript\n * maskNIK('3201234567890123', { separator: '-' });\n * // '32-01-**-**-**-**-0123'\n * ```\n *\n * @example\n * Custom start and end:\n * ```typescript\n * maskNIK('3201234567890123', { start: 6, end: 4 });\n * // '320123******0123'\n * ```\n *\n * @public\n */\nexport function maskNIK(nik: string, options: MaskOptions = {}): string {\n if (!/^\\d{16}$/.test(nik)) {\n return nik;\n }\n\n const { start = 4, end = 4, char = '*', separator } = options;\n\n if (start + end >= 16) {\n return nik;\n }\n\n if (separator) {\n // Format with separator first, then apply masking\n const formatted = formatNIK(nik, separator);\n const parts = formatted.split(separator);\n\n // Calculate which parts to mask\n // Format: PP-KK-DD-YY-MM-DD-XXXX (7 parts)\n // Mask parts based on character positions\n let charCount = 0;\n const maskedParts = parts.map((part) => {\n const partStart = charCount;\n const partEnd = charCount + part.length;\n charCount += part.length;\n\n // Check if this part should be fully/partially masked\n if (partEnd <= start) {\n // Fully visible (before start)\n return part;\n } else if (partStart >= 16 - end) {\n // Fully visible (after end)\n return part;\n } else if (partStart >= start && partEnd <= 16 - end) {\n // Fully masked\n return char.repeat(part.length);\n } else {\n // Partially masked\n return part\n .split('')\n .map((ch, idx) => {\n const pos = partStart + idx;\n if (pos < start || pos >= 16 - end) {\n return ch;\n }\n return char;\n })\n .join('');\n }\n });\n\n return maskedParts.join(separator);\n }\n\n // Without separator: simple masking\n const startPart = nik.substring(0, start);\n const endPart = nik.substring(16 - end);\n const maskLength = 16 - start - end;\n return startPart + char.repeat(maskLength) + endPart;\n}","import { parseNIK } from './parse';\n\n/**\n * Calculates the age of a person based on their NIK.\n *\n * @param nik - The 16-digit NIK string\n * @param referenceDate - The date to calculate age from (default: current date)\n * @returns The age in years, or null if the NIK is invalid or birth date cannot be parsed\n *\n * @example\n * ```typescript\n * getAge('3201018901310123'); // 35 (as of 2024)\n * ```\n */\nexport function getAge(\n nik: string,\n referenceDate: Date = new Date()\n): number | null {\n const info = parseNIK(nik);\n if (!info || !info.birthDate) {\n return null;\n }\n\n const birthDate = info.birthDate;\n let age = referenceDate.getFullYear() - birthDate.getFullYear();\n const m = referenceDate.getMonth() - birthDate.getMonth();\n\n if (m < 0 || (m === 0 && referenceDate.getDate() < birthDate.getDate())) {\n age--;\n }\n\n return age;\n}\n\n/**\n * Formats the birth date from a NIK into a human-readable string.\n *\n * @param nik - The 16-digit NIK string\n * @param locale - The locale to use for formatting (default: 'id-ID')\n * @returns Formatted birth date string, or null if invalid\n *\n * @example\n * ```typescript\n * formatBirthDate('3201018901310123'); // '31 Januari 1989'\n * ```\n */\nexport function formatBirthDate(\n nik: string,\n options: Intl.DateTimeFormatOptions = {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n },\n locale: string = 'id-ID'\n): string | null {\n const info = parseNIK(nik);\n if (!info || !info.birthDate) {\n return null;\n }\n\n return new Intl.DateTimeFormat(locale, options).format(info.birthDate);\n}\n\n/**\n * Checks if a NIK matches a specific gender.\n *\n * @param nik - The 16-digit NIK string\n * @param gender - The gender to check ('male' | 'female')\n * @returns True if the NIK matches the gender, false otherwise\n */\nexport function isValidForGender(\n nik: string,\n gender: 'male' | 'female'\n): boolean {\n const info = parseNIK(nik);\n if (!info) {\n return false;\n }\n return info.gender === gender;\n}\n\n/**\n * Checks if a NIK matches a specific birth date.\n *\n * @param nik - The 16-digit NIK string\n * @param birthDate - The birth date to check\n * @returns True if the NIK matches the birth date, false otherwise\n */\nexport function isValidForBirthDate(nik: string, birthDate: Date): boolean {\n const info = parseNIK(nik);\n if (!info || !info.birthDate) {\n return false;\n }\n\n return (\n info.birthDate.getFullYear() === birthDate.getFullYear() &&\n info.birthDate.getMonth() === birthDate.getMonth() &&\n info.birthDate.getDate() === birthDate.getDate()\n );\n}\n"]}
|
package/dist/nik/index.d.cts
CHANGED
|
@@ -285,4 +285,47 @@ declare function formatNIK(nik: string, separator?: string): string;
|
|
|
285
285
|
*/
|
|
286
286
|
declare function maskNIK(nik: string, options?: MaskOptions): string;
|
|
287
287
|
|
|
288
|
-
|
|
288
|
+
/**
|
|
289
|
+
* Calculates the age of a person based on their NIK.
|
|
290
|
+
*
|
|
291
|
+
* @param nik - The 16-digit NIK string
|
|
292
|
+
* @param referenceDate - The date to calculate age from (default: current date)
|
|
293
|
+
* @returns The age in years, or null if the NIK is invalid or birth date cannot be parsed
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* getAge('3201018901310123'); // 35 (as of 2024)
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
300
|
+
declare function getAge(nik: string, referenceDate?: Date): number | null;
|
|
301
|
+
/**
|
|
302
|
+
* Formats the birth date from a NIK into a human-readable string.
|
|
303
|
+
*
|
|
304
|
+
* @param nik - The 16-digit NIK string
|
|
305
|
+
* @param locale - The locale to use for formatting (default: 'id-ID')
|
|
306
|
+
* @returns Formatted birth date string, or null if invalid
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```typescript
|
|
310
|
+
* formatBirthDate('3201018901310123'); // '31 Januari 1989'
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
declare function formatBirthDate(nik: string, options?: Intl.DateTimeFormatOptions, locale?: string): string | null;
|
|
314
|
+
/**
|
|
315
|
+
* Checks if a NIK matches a specific gender.
|
|
316
|
+
*
|
|
317
|
+
* @param nik - The 16-digit NIK string
|
|
318
|
+
* @param gender - The gender to check ('male' | 'female')
|
|
319
|
+
* @returns True if the NIK matches the gender, false otherwise
|
|
320
|
+
*/
|
|
321
|
+
declare function isValidForGender(nik: string, gender: 'male' | 'female'): boolean;
|
|
322
|
+
/**
|
|
323
|
+
* Checks if a NIK matches a specific birth date.
|
|
324
|
+
*
|
|
325
|
+
* @param nik - The 16-digit NIK string
|
|
326
|
+
* @param birthDate - The birth date to check
|
|
327
|
+
* @returns True if the NIK matches the birth date, false otherwise
|
|
328
|
+
*/
|
|
329
|
+
declare function isValidForBirthDate(nik: string, birthDate: Date): boolean;
|
|
330
|
+
|
|
331
|
+
export { type MaskOptions, type NIKInfo, formatBirthDate, formatNIK, getAge, isValidForBirthDate, isValidForGender, maskNIK, parseNIK, validateNIK };
|
package/dist/nik/index.d.ts
CHANGED
|
@@ -285,4 +285,47 @@ declare function formatNIK(nik: string, separator?: string): string;
|
|
|
285
285
|
*/
|
|
286
286
|
declare function maskNIK(nik: string, options?: MaskOptions): string;
|
|
287
287
|
|
|
288
|
-
|
|
288
|
+
/**
|
|
289
|
+
* Calculates the age of a person based on their NIK.
|
|
290
|
+
*
|
|
291
|
+
* @param nik - The 16-digit NIK string
|
|
292
|
+
* @param referenceDate - The date to calculate age from (default: current date)
|
|
293
|
+
* @returns The age in years, or null if the NIK is invalid or birth date cannot be parsed
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* getAge('3201018901310123'); // 35 (as of 2024)
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
300
|
+
declare function getAge(nik: string, referenceDate?: Date): number | null;
|
|
301
|
+
/**
|
|
302
|
+
* Formats the birth date from a NIK into a human-readable string.
|
|
303
|
+
*
|
|
304
|
+
* @param nik - The 16-digit NIK string
|
|
305
|
+
* @param locale - The locale to use for formatting (default: 'id-ID')
|
|
306
|
+
* @returns Formatted birth date string, or null if invalid
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```typescript
|
|
310
|
+
* formatBirthDate('3201018901310123'); // '31 Januari 1989'
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
declare function formatBirthDate(nik: string, options?: Intl.DateTimeFormatOptions, locale?: string): string | null;
|
|
314
|
+
/**
|
|
315
|
+
* Checks if a NIK matches a specific gender.
|
|
316
|
+
*
|
|
317
|
+
* @param nik - The 16-digit NIK string
|
|
318
|
+
* @param gender - The gender to check ('male' | 'female')
|
|
319
|
+
* @returns True if the NIK matches the gender, false otherwise
|
|
320
|
+
*/
|
|
321
|
+
declare function isValidForGender(nik: string, gender: 'male' | 'female'): boolean;
|
|
322
|
+
/**
|
|
323
|
+
* Checks if a NIK matches a specific birth date.
|
|
324
|
+
*
|
|
325
|
+
* @param nik - The 16-digit NIK string
|
|
326
|
+
* @param birthDate - The birth date to check
|
|
327
|
+
* @returns True if the NIK matches the birth date, false otherwise
|
|
328
|
+
*/
|
|
329
|
+
declare function isValidForBirthDate(nik: string, birthDate: Date): boolean;
|
|
330
|
+
|
|
331
|
+
export { type MaskOptions, type NIKInfo, formatBirthDate, formatNIK, getAge, isValidForBirthDate, isValidForGender, maskNIK, parseNIK, validateNIK };
|
package/dist/nik/index.js
CHANGED
|
@@ -208,6 +208,46 @@ function maskNIK(nik, options = {}) {
|
|
|
208
208
|
return startPart + char.repeat(maskLength) + endPart;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
// src/nik/utils.ts
|
|
212
|
+
function getAge(nik, referenceDate = /* @__PURE__ */ new Date()) {
|
|
213
|
+
const info = parseNIK(nik);
|
|
214
|
+
if (!info || !info.birthDate) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
const birthDate = info.birthDate;
|
|
218
|
+
let age = referenceDate.getFullYear() - birthDate.getFullYear();
|
|
219
|
+
const m = referenceDate.getMonth() - birthDate.getMonth();
|
|
220
|
+
if (m < 0 || m === 0 && referenceDate.getDate() < birthDate.getDate()) {
|
|
221
|
+
age--;
|
|
222
|
+
}
|
|
223
|
+
return age;
|
|
224
|
+
}
|
|
225
|
+
function formatBirthDate(nik, options = {
|
|
226
|
+
day: "numeric",
|
|
227
|
+
month: "long",
|
|
228
|
+
year: "numeric"
|
|
229
|
+
}, locale = "id-ID") {
|
|
230
|
+
const info = parseNIK(nik);
|
|
231
|
+
if (!info || !info.birthDate) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
return new Intl.DateTimeFormat(locale, options).format(info.birthDate);
|
|
235
|
+
}
|
|
236
|
+
function isValidForGender(nik, gender) {
|
|
237
|
+
const info = parseNIK(nik);
|
|
238
|
+
if (!info) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
return info.gender === gender;
|
|
242
|
+
}
|
|
243
|
+
function isValidForBirthDate(nik, birthDate) {
|
|
244
|
+
const info = parseNIK(nik);
|
|
245
|
+
if (!info || !info.birthDate) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
return info.birthDate.getFullYear() === birthDate.getFullYear() && info.birthDate.getMonth() === birthDate.getMonth() && info.birthDate.getDate() === birthDate.getDate();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export { formatBirthDate, formatNIK, getAge, isValidForBirthDate, isValidForGender, maskNIK, parseNIK, validateNIK };
|
|
212
252
|
//# sourceMappingURL=index.js.map
|
|
213
253
|
//# sourceMappingURL=index.js.map
|
package/dist/nik/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/nik/constants.ts","../../src/nik/validate.ts","../../src/nik/parse.ts","../../src/nik/format.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Indonesian province codes and names\n * Based on Dukcapil Kemendagri data\n */\nexport const PROVINCES: Record<string, string> = {\n '11': 'Aceh',\n '12': 'Sumatera Utara',\n '13': 'Sumatera Barat',\n '14': 'Riau',\n '15': 'Jambi',\n '16': 'Sumatera Selatan',\n '17': 'Bengkulu',\n '18': 'Lampung',\n '19': 'Kepulauan Bangka Belitung',\n '21': 'Kepulauan Riau',\n '31': 'DKI Jakarta',\n '32': 'Jawa Barat',\n '33': 'Jawa Tengah',\n '34': 'DI Yogyakarta',\n '35': 'Jawa Timur',\n '36': 'Banten',\n '51': 'Bali',\n '52': 'Nusa Tenggara Barat',\n '53': 'Nusa Tenggara Timur',\n '61': 'Kalimantan Barat',\n '62': 'Kalimantan Tengah',\n '63': 'Kalimantan Selatan',\n '64': 'Kalimantan Timur',\n '65': 'Kalimantan Utara',\n '71': 'Sulawesi Utara',\n '72': 'Sulawesi Tengah',\n '73': 'Sulawesi Selatan',\n '74': 'Sulawesi Tenggara',\n '75': 'Gorontalo',\n '76': 'Sulawesi Barat',\n '81': 'Maluku',\n '82': 'Maluku Utara',\n '91': 'Papua',\n '92': 'Papua Barat',\n '93': 'Papua Selatan',\n '94': 'Papua Tengah',\n '95': 'Papua Pegunungan',\n '96': 'Papua Barat Daya',\n};\n\n/**\n * Regency codes for each province (simplified - only major ones)\n * In a real implementation, you'd have complete data\n */\nexport const REGENCIES: Record<string, Record<string, string>> = {\n '32': {\n '01': 'Kab. Bogor',\n '02': 'Kab. Sukabumi',\n '03': 'Kab. Cianjur',\n '71': 'Kota Bandung',\n '72': 'Kota Bekasi',\n '73': 'Kota Depok',\n },\n '31': { \n '01': 'Kota Jakarta Selatan',\n '02': 'Kota Jakarta Timur',\n '03': 'Kota Jakarta Pusat',\n '04': 'Kota Jakarta Barat',\n '05': 'Kota Jakarta Utara',\n },\n};","import { PROVINCES } from './constants';\n\n/**\n * Validates a NIK (Nomor Induk Kependudukan) format.\n *\n * A valid NIK must:\n * - Be exactly 16 digits\n * - Have a valid province code (positions 1-2)\n * - Have a valid date (positions 7-12)\n * - Not be in the future\n * - Not be before 1900\n *\n * For female NIKs, the day is encoded as (actual day + 40).\n * For example, a female born on the 15th would have day = 55.\n *\n * @param nik - The 16-digit NIK string to validate\n * @returns `true` if the NIK is valid, `false` otherwise\n *\n * @example\n * ```typescript\n * validateNIK('3201234567890123'); // true - valid NIK\n * validateNIK('1234'); // false - wrong length\n * validateNIK('9912345678901234'); // false - invalid province\n * ```\n *\n * @public\n */\nexport function validateNIK(nik: string): boolean {\n if (!/^\\d{16}$/.test(nik)) {\n return false;\n }\n\n const provinceCode = nik.substring(0, 2);\n if (!PROVINCES[provinceCode]) {\n return false;\n }\n\n const yearStr = nik.substring(6, 8);\n const monthStr = nik.substring(8, 10);\n const dayStr = nik.substring(10, 12);\n\n const year = parseInt(yearStr, 10);\n const fullYear = year > 30 ? 1900 + year : 2000 + year;\n\n const month = parseInt(monthStr, 10);\n let day = parseInt(dayStr, 10);\n\n if (day > 40) {\n day = day - 40;\n }\n\n if (month < 1 || month > 12) {\n return false;\n }\n\n if (day < 1 || day > 31) {\n return false;\n }\n\n const testDate = new Date(fullYear, month - 1, day);\n if (\n testDate.getFullYear() !== fullYear ||\n testDate.getMonth() !== month - 1 ||\n testDate.getDate() !== day\n ) {\n return false;\n }\n\n const now = new Date();\n if (testDate > now || testDate < new Date(1900, 0, 1)) {\n return false;\n }\n\n return true;\n}\n","import { PROVINCES, REGENCIES } from './constants';\nimport { NIKInfo } from './types';\n\n/**\n * Parses a NIK and extracts all embedded information.\n *\n * Extracts province, regency, district codes, birth date, gender,\n * and serial number from a 16-digit NIK string.\n *\n * @param nik - The 16-digit NIK string to parse\n * @returns Parsed NIK information, or `null` if the NIK format is invalid\n *\n * @example\n * Parse a valid male NIK:\n * ```typescript\n * const info = parseNIK('3201018901310123');\n * console.log(info);\n * // {\n * // province: { code: '32', name: 'Jawa Barat' },\n * // regency: { code: '01', name: 'Kab. Bogor' },\n * // district: { code: '01', name: null },\n * // birthDate: Date(1989, 0, 31), // Jan 31, 1989\n * // gender: 'male',\n * // serialNumber: '0123',\n * // isValid: true\n * // }\n * ```\n *\n * @example\n * Parse a female NIK (day + 40):\n * ```typescript\n * const info = parseNIK('3201019508550123');\n * console.log(info.gender); // 'female'\n * console.log(info.birthDate); // Date(1995, 7, 15) - Aug 15, 1995\n * ```\n *\n * @example\n * Invalid NIK returns null:\n * ```typescript\n * const info = parseNIK('invalid');\n * console.log(info); // null\n * ```\n *\n * @public\n */\nexport function parseNIK(nik: string): NIKInfo | null {\n if (!/^\\d{16}$/.test(nik)) {\n return null;\n }\n\n const provinceCode = nik.substring(0, 2);\n const regencyCode = nik.substring(2, 4);\n const districtCode = nik.substring(4, 6);\n const yearStr = nik.substring(6, 8);\n const monthStr = nik.substring(8, 10);\n const dayStr = nik.substring(10, 12);\n const serialNumber = nik.substring(12, 16);\n\n const province = PROVINCES[provinceCode];\n if (!province) {\n return null;\n }\n\n const regencies = REGENCIES[provinceCode] || {};\n const regency = regencies[regencyCode] || 'Unknown';\n\n let day = parseInt(dayStr, 10);\n const month = parseInt(monthStr, 10);\n const year = parseInt(yearStr, 10);\n\n let gender: 'male' | 'female' | null = null;\n if (day > 40) {\n gender = 'female';\n day -= 40;\n } else {\n gender = 'male';\n }\n\n const fullYear = year > 30 ? 1900 + year : 2000 + year;\n\n const birthDate = new Date(fullYear, month - 1, day);\n if (\n birthDate.getFullYear() !== fullYear ||\n birthDate.getMonth() !== month - 1 ||\n birthDate.getDate() !== day\n ) {\n return null;\n }\n\n return {\n province: {\n code: provinceCode,\n name: province,\n },\n regency: {\n code: regencyCode,\n name: regency,\n },\n district: {\n code: districtCode,\n name: null,\n },\n birthDate,\n gender,\n serialNumber,\n isValid: true,\n };\n}","import { MaskOptions } from './types';\n\n/**\n * Formats a NIK with separators for better readability.\n *\n * Groups the NIK into logical segments: province, regency, district,\n * year, month, day, and serial number.\n *\n * @param nik - The 16-digit NIK string to format\n * @param separator - Character to use as separator\n * @returns Formatted NIK string, or original string if invalid format\n *\n * @example\n * Default separator (dash):\n * ```typescript\n * formatNIK('3201234567890123');\n * // '32-01-23-45-67-89-0123'\n * ```\n *\n * @example\n * Custom separator:\n * ```typescript\n * formatNIK('3201234567890123', ' ');\n * // '32 01 23 45 67 89 0123'\n * ```\n *\n * @example\n * Invalid NIK returns as-is:\n * ```typescript\n * formatNIK('1234');\n * // '1234'\n * ```\n *\n * @public\n */\nexport function formatNIK(nik: string, separator: string = '-'): string {\n if (!/^\\d{16}$/.test(nik)) {\n return nik;\n }\n\n // Format: PP-KK-DD-YY-MM-DD-XXXX\n // PP = Province (2 digits)\n // KK = Regency (2 digits)\n // DD = District (2 digits)\n // YY = Year (2 digits)\n // MM = Month (2 digits)\n // DD = Day (2 digits, +40 for female)\n // XXXX = Serial number (4 digits)\n return [\n nik.substring(0, 2), // Province\n nik.substring(2, 4), // Regency\n nik.substring(4, 6), // District\n nik.substring(6, 8), // Year\n nik.substring(8, 10), // Month\n nik.substring(10, 12), // Day\n nik.substring(12, 16), // Serial\n ].join(separator);\n}\n\n/**\n * Masks a NIK to protect privacy while keeping partial visibility.\n *\n * By default, shows the first 4 and last 4 digits, masking the middle 8.\n * Optionally formats the masked NIK with separators.\n *\n * @param nik - The 16-digit NIK string to mask\n * @param options - Masking configuration options\n * @returns Masked NIK string, or original string if invalid format\n *\n * @example\n * Default masking (first 4, last 4):\n * ```typescript\n * maskNIK('3201234567890123');\n * // '3201********0123'\n * ```\n *\n * @example\n * Custom mask character:\n * ```typescript\n * maskNIK('3201234567890123', { char: 'X' });\n * // '3201XXXXXXXX0123'\n * ```\n *\n * @example\n * With separator:\n * ```typescript\n * maskNIK('3201234567890123', { separator: '-' });\n * // '32-01-**-**-**-**-0123'\n * ```\n *\n * @example\n * Custom start and end:\n * ```typescript\n * maskNIK('3201234567890123', { start: 6, end: 4 });\n * // '320123******0123'\n * ```\n *\n * @public\n */\nexport function maskNIK(nik: string, options: MaskOptions = {}): string {\n if (!/^\\d{16}$/.test(nik)) {\n return nik;\n }\n\n const { start = 4, end = 4, char = '*', separator } = options;\n\n if (start + end >= 16) {\n return nik;\n }\n\n if (separator) {\n // Format with separator first, then apply masking\n const formatted = formatNIK(nik, separator);\n const parts = formatted.split(separator);\n\n // Calculate which parts to mask\n // Format: PP-KK-DD-YY-MM-DD-XXXX (7 parts)\n // Mask parts based on character positions\n let charCount = 0;\n const maskedParts = parts.map((part) => {\n const partStart = charCount;\n const partEnd = charCount + part.length;\n charCount += part.length;\n\n // Check if this part should be fully/partially masked\n if (partEnd <= start) {\n // Fully visible (before start)\n return part;\n } else if (partStart >= 16 - end) {\n // Fully visible (after end)\n return part;\n } else if (partStart >= start && partEnd <= 16 - end) {\n // Fully masked\n return char.repeat(part.length);\n } else {\n // Partially masked\n return part\n .split('')\n .map((ch, idx) => {\n const pos = partStart + idx;\n if (pos < start || pos >= 16 - end) {\n return ch;\n }\n return char;\n })\n .join('');\n }\n });\n\n return maskedParts.join(separator);\n }\n\n // Without separator: simple masking\n const startPart = nik.substring(0, start);\n const endPart = nik.substring(16 - end);\n const maskLength = 16 - start - end;\n return startPart + char.repeat(maskLength) + endPart;\n}"]}
|
|
1
|
+
{"version":3,"sources":["../../src/nik/constants.ts","../../src/nik/validate.ts","../../src/nik/parse.ts","../../src/nik/format.ts","../../src/nik/utils.ts"],"names":[],"mappings":";AAIO,IAAM,SAAA,GAAoC;AAAA,EAC/C,IAAA,EAAM,MAAA;AAAA,EACN,IAAA,EAAM,gBAAA;AAAA,EACN,IAAA,EAAM,gBAAA;AAAA,EACN,IAAA,EAAM,MAAA;AAAA,EACN,IAAA,EAAM,OAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM,UAAA;AAAA,EACN,IAAA,EAAM,SAAA;AAAA,EACN,IAAA,EAAM,2BAAA;AAAA,EACN,IAAA,EAAM,gBAAA;AAAA,EACN,IAAA,EAAM,aAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA,EACN,IAAA,EAAM,aAAA;AAAA,EACN,IAAA,EAAM,eAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA,EACN,IAAA,EAAM,QAAA;AAAA,EACN,IAAA,EAAM,MAAA;AAAA,EACN,IAAA,EAAM,qBAAA;AAAA,EACN,IAAA,EAAM,qBAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM,mBAAA;AAAA,EACN,IAAA,EAAM,oBAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM,gBAAA;AAAA,EACN,IAAA,EAAM,iBAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM,mBAAA;AAAA,EACN,IAAA,EAAM,WAAA;AAAA,EACN,IAAA,EAAM,gBAAA;AAAA,EACN,IAAA,EAAM,QAAA;AAAA,EACN,IAAA,EAAM,cAAA;AAAA,EACN,IAAA,EAAM,OAAA;AAAA,EACN,IAAA,EAAM,aAAA;AAAA,EACN,IAAA,EAAM,eAAA;AAAA,EACN,IAAA,EAAM,cAAA;AAAA,EACN,IAAA,EAAM,kBAAA;AAAA,EACN,IAAA,EAAM;AACR,CAAA;AAMO,IAAM,SAAA,GAAoD;AAAA,EAC/D,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,IAAA,EAAM,eAAA;AAAA,IACN,IAAA,EAAM,cAAA;AAAA,IACN,IAAA,EAAM,cAAA;AAAA,IACN,IAAA,EAAM,aAAA;AAAA,IACN,IAAA,EAAM;AAAA,GACR;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,sBAAA;AAAA,IACN,IAAA,EAAM,oBAAA;AAAA,IACN,IAAA,EAAM,oBAAA;AAAA,IACN,IAAA,EAAM,oBAAA;AAAA,IACN,IAAA,EAAM;AAAA;AAEV,CAAA;;;ACtCO,SAAS,YAAY,GAAA,EAAsB;AAChD,EAAA,IAAI,CAAC,UAAA,CAAW,IAAA,CAAK,GAAG,CAAA,EAAG;AACzB,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAA,GAAe,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AACvC,EAAA,IAAI,CAAC,SAAA,CAAU,YAAY,CAAA,EAAG;AAC5B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AAClC,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,EAAE,CAAA;AACpC,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,SAAA,CAAU,EAAA,EAAI,EAAE,CAAA;AAEnC,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,OAAA,EAAS,EAAE,CAAA;AACjC,EAAA,MAAM,QAAA,GAAW,IAAA,GAAO,EAAA,GAAK,IAAA,GAAO,OAAO,GAAA,GAAO,IAAA;AAElD,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,EAAU,EAAE,CAAA;AACnC,EAAA,IAAI,GAAA,GAAM,QAAA,CAAS,MAAA,EAAQ,EAAE,CAAA;AAE7B,EAAA,IAAI,MAAM,EAAA,EAAI;AACZ,IAAA,GAAA,GAAM,GAAA,GAAM,EAAA;AAAA,EACd;AAEA,EAAA,IAAI,KAAA,GAAQ,CAAA,IAAK,KAAA,GAAQ,EAAA,EAAI;AAC3B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,GAAA,GAAM,CAAA,IAAK,GAAA,GAAM,EAAA,EAAI;AACvB,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,WAAW,IAAI,IAAA,CAAK,QAAA,EAAU,KAAA,GAAQ,GAAG,GAAG,CAAA;AAClD,EAAA,IACE,QAAA,CAAS,WAAA,EAAY,KAAM,QAAA,IAC3B,QAAA,CAAS,QAAA,EAAS,KAAM,KAAA,GAAQ,CAAA,IAChC,QAAA,CAAS,OAAA,EAAQ,KAAM,GAAA,EACvB;AACA,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AACrB,EAAA,IAAI,QAAA,GAAW,OAAO,QAAA,GAAW,IAAI,KAAK,IAAA,EAAM,CAAA,EAAG,CAAC,CAAA,EAAG;AACrD,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAA;AACT;;;AC7BO,SAAS,SAAS,GAAA,EAA6B;AACpD,EAAA,IAAI,CAAC,UAAA,CAAW,IAAA,CAAK,GAAG,CAAA,EAAG;AACzB,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAA,GAAe,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AACvC,EAAA,MAAM,WAAA,GAAc,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AACtC,EAAA,MAAM,YAAA,GAAe,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AACvC,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AAClC,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,EAAE,CAAA;AACpC,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,SAAA,CAAU,EAAA,EAAI,EAAE,CAAA;AACnC,EAAA,MAAM,YAAA,GAAe,GAAA,CAAI,SAAA,CAAU,EAAA,EAAI,EAAE,CAAA;AAEzC,EAAA,MAAM,QAAA,GAAW,UAAU,YAAY,CAAA;AACvC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,SAAA,GAAY,SAAA,CAAU,YAAY,CAAA,IAAK,EAAC;AAC9C,EAAA,MAAM,OAAA,GAAU,SAAA,CAAU,WAAW,CAAA,IAAK,SAAA;AAE1C,EAAA,IAAI,GAAA,GAAM,QAAA,CAAS,MAAA,EAAQ,EAAE,CAAA;AAC7B,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,QAAA,EAAU,EAAE,CAAA;AACnC,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,OAAA,EAAS,EAAE,CAAA;AAEjC,EAAA,IAAI,MAAA,GAAmC,IAAA;AACvC,EAAA,IAAI,MAAM,EAAA,EAAI;AACZ,IAAA,MAAA,GAAS,QAAA;AACT,IAAA,GAAA,IAAO,EAAA;AAAA,EACT,CAAA,MAAO;AACL,IAAA,MAAA,GAAS,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,QAAA,GAAW,IAAA,GAAO,EAAA,GAAK,IAAA,GAAO,OAAO,GAAA,GAAO,IAAA;AAElD,EAAA,MAAM,YAAY,IAAI,IAAA,CAAK,QAAA,EAAU,KAAA,GAAQ,GAAG,GAAG,CAAA;AACnD,EAAA,IACE,SAAA,CAAU,WAAA,EAAY,KAAM,QAAA,IAC5B,SAAA,CAAU,QAAA,EAAS,KAAM,KAAA,GAAQ,CAAA,IACjC,SAAA,CAAU,OAAA,EAAQ,KAAM,GAAA,EACxB;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO;AAAA,IACL,QAAA,EAAU;AAAA,MACR,IAAA,EAAM,YAAA;AAAA,MACN,IAAA,EAAM;AAAA,KACR;AAAA,IACA,OAAA,EAAS;AAAA,MACP,IAAA,EAAM,WAAA;AAAA,MACN,IAAA,EAAM;AAAA,KACR;AAAA,IACA,QAAA,EAAU;AAAA,MACR,IAAA,EAAM,YAAA;AAAA,MACN,IAAA,EAAM;AAAA,KACR;AAAA,IACA,SAAA;AAAA,IACA,MAAA;AAAA,IACA,YAAA;AAAA,IACA,OAAA,EAAS;AAAA,GACX;AACF;;;ACxEO,SAAS,SAAA,CAAU,GAAA,EAAa,SAAA,GAAoB,GAAA,EAAa;AACtE,EAAA,IAAI,CAAC,UAAA,CAAW,IAAA,CAAK,GAAG,CAAA,EAAG;AACzB,IAAA,OAAO,GAAA;AAAA,EACT;AAUA,EAAA,OAAO;AAAA,IACL,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AAAA;AAAA,IAClB,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AAAA;AAAA,IAClB,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AAAA;AAAA,IAClB,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AAAA;AAAA,IAClB,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,EAAE,CAAA;AAAA;AAAA,IACnB,GAAA,CAAI,SAAA,CAAU,EAAA,EAAI,EAAE,CAAA;AAAA;AAAA,IACpB,GAAA,CAAI,SAAA,CAAU,EAAA,EAAI,EAAE;AAAA;AAAA,GACtB,CAAE,KAAK,SAAS,CAAA;AAClB;AA0CO,SAAS,OAAA,CAAQ,GAAA,EAAa,OAAA,GAAuB,EAAC,EAAW;AACtE,EAAA,IAAI,CAAC,UAAA,CAAW,IAAA,CAAK,GAAG,CAAA,EAAG;AACzB,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,MAAM,EAAE,QAAQ,CAAA,EAAG,GAAA,GAAM,GAAG,IAAA,GAAO,GAAA,EAAK,WAAU,GAAI,OAAA;AAEtD,EAAA,IAAI,KAAA,GAAQ,OAAO,EAAA,EAAI;AACrB,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,IAAI,SAAA,EAAW;AAEb,IAAA,MAAM,SAAA,GAAY,SAAA,CAAU,GAAA,EAAK,SAAS,CAAA;AAC1C,IAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,KAAA,CAAM,SAAS,CAAA;AAKvC,IAAA,IAAI,SAAA,GAAY,CAAA;AAChB,IAAA,MAAM,WAAA,GAAc,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACtC,MAAA,MAAM,SAAA,GAAY,SAAA;AAClB,MAAA,MAAM,OAAA,GAAU,YAAY,IAAA,CAAK,MAAA;AACjC,MAAA,SAAA,IAAa,IAAA,CAAK,MAAA;AAGlB,MAAA,IAAI,WAAW,KAAA,EAAO;AAEpB,QAAA,OAAO,IAAA;AAAA,MACT,CAAA,MAAA,IAAW,SAAA,IAAa,EAAA,GAAK,GAAA,EAAK;AAEhC,QAAA,OAAO,IAAA;AAAA,MACT,CAAA,MAAA,IAAW,SAAA,IAAa,KAAA,IAAS,OAAA,IAAW,KAAK,GAAA,EAAK;AAEpD,QAAA,OAAO,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA;AAAA,MAChC,CAAA,MAAO;AAEL,QAAA,OAAO,KACJ,KAAA,CAAM,EAAE,EACR,GAAA,CAAI,CAAC,IAAI,GAAA,KAAQ;AAChB,UAAA,MAAM,MAAM,SAAA,GAAY,GAAA;AACxB,UAAA,IAAI,GAAA,GAAM,KAAA,IAAS,GAAA,IAAO,EAAA,GAAK,GAAA,EAAK;AAClC,YAAA,OAAO,EAAA;AAAA,UACT;AACA,UAAA,OAAO,IAAA;AAAA,QACT,CAAC,CAAA,CACA,IAAA,CAAK,EAAE,CAAA;AAAA,MACZ;AAAA,IACF,CAAC,CAAA;AAED,IAAA,OAAO,WAAA,CAAY,KAAK,SAAS,CAAA;AAAA,EACnC;AAGA,EAAA,MAAM,SAAA,GAAY,GAAA,CAAI,SAAA,CAAU,CAAA,EAAG,KAAK,CAAA;AACxC,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,SAAA,CAAU,EAAA,GAAK,GAAG,CAAA;AACtC,EAAA,MAAM,UAAA,GAAa,KAAK,KAAA,GAAQ,GAAA;AAChC,EAAA,OAAO,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,UAAU,CAAA,GAAI,OAAA;AAC/C;;;AC/IO,SAAS,MAAA,CACd,GAAA,EACA,aAAA,mBAAsB,IAAI,MAAK,EAChB;AACf,EAAA,MAAM,IAAA,GAAO,SAAS,GAAG,CAAA;AACzB,EAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,IAAA,CAAK,SAAA,EAAW;AAC5B,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAY,IAAA,CAAK,SAAA;AACvB,EAAA,IAAI,GAAA,GAAM,aAAA,CAAc,WAAA,EAAY,GAAI,UAAU,WAAA,EAAY;AAC9D,EAAA,MAAM,CAAA,GAAI,aAAA,CAAc,QAAA,EAAS,GAAI,UAAU,QAAA,EAAS;AAExD,EAAA,IAAI,CAAA,GAAI,KAAM,CAAA,KAAM,CAAA,IAAK,cAAc,OAAA,EAAQ,GAAI,SAAA,CAAU,OAAA,EAAQ,EAAI;AACvE,IAAA,GAAA,EAAA;AAAA,EACF;AAEA,EAAA,OAAO,GAAA;AACT;AAcO,SAAS,eAAA,CACd,KACA,OAAA,GAAsC;AAAA,EACpC,GAAA,EAAK,SAAA;AAAA,EACL,KAAA,EAAO,MAAA;AAAA,EACP,IAAA,EAAM;AACR,CAAA,EACA,SAAiB,OAAA,EACF;AACf,EAAA,MAAM,IAAA,GAAO,SAAS,GAAG,CAAA;AACzB,EAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,IAAA,CAAK,SAAA,EAAW;AAC5B,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAI,KAAK,cAAA,CAAe,MAAA,EAAQ,OAAO,CAAA,CAAE,MAAA,CAAO,KAAK,SAAS,CAAA;AACvE;AASO,SAAS,gBAAA,CACd,KACA,MAAA,EACS;AACT,EAAA,MAAM,IAAA,GAAO,SAAS,GAAG,CAAA;AACzB,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAK,MAAA,KAAW,MAAA;AACzB;AASO,SAAS,mBAAA,CAAoB,KAAa,SAAA,EAA0B;AACzE,EAAA,MAAM,IAAA,GAAO,SAAS,GAAG,CAAA;AACzB,EAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,IAAA,CAAK,SAAA,EAAW;AAC5B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,OACE,KAAK,SAAA,CAAU,WAAA,OAAkB,SAAA,CAAU,WAAA,MAC3C,IAAA,CAAK,SAAA,CAAU,UAAS,KAAM,SAAA,CAAU,UAAS,IACjD,IAAA,CAAK,UAAU,OAAA,EAAQ,KAAM,UAAU,OAAA,EAAQ;AAEnD","file":"index.js","sourcesContent":["/**\n * Indonesian province codes and names\n * Based on Dukcapil Kemendagri data\n */\nexport const PROVINCES: Record<string, string> = {\n '11': 'Aceh',\n '12': 'Sumatera Utara',\n '13': 'Sumatera Barat',\n '14': 'Riau',\n '15': 'Jambi',\n '16': 'Sumatera Selatan',\n '17': 'Bengkulu',\n '18': 'Lampung',\n '19': 'Kepulauan Bangka Belitung',\n '21': 'Kepulauan Riau',\n '31': 'DKI Jakarta',\n '32': 'Jawa Barat',\n '33': 'Jawa Tengah',\n '34': 'DI Yogyakarta',\n '35': 'Jawa Timur',\n '36': 'Banten',\n '51': 'Bali',\n '52': 'Nusa Tenggara Barat',\n '53': 'Nusa Tenggara Timur',\n '61': 'Kalimantan Barat',\n '62': 'Kalimantan Tengah',\n '63': 'Kalimantan Selatan',\n '64': 'Kalimantan Timur',\n '65': 'Kalimantan Utara',\n '71': 'Sulawesi Utara',\n '72': 'Sulawesi Tengah',\n '73': 'Sulawesi Selatan',\n '74': 'Sulawesi Tenggara',\n '75': 'Gorontalo',\n '76': 'Sulawesi Barat',\n '81': 'Maluku',\n '82': 'Maluku Utara',\n '91': 'Papua',\n '92': 'Papua Barat',\n '93': 'Papua Selatan',\n '94': 'Papua Tengah',\n '95': 'Papua Pegunungan',\n '96': 'Papua Barat Daya',\n};\n\n/**\n * Regency codes for each province (simplified - only major ones)\n * In a real implementation, you'd have complete data\n */\nexport const REGENCIES: Record<string, Record<string, string>> = {\n '32': {\n '01': 'Kab. Bogor',\n '02': 'Kab. Sukabumi',\n '03': 'Kab. Cianjur',\n '71': 'Kota Bandung',\n '72': 'Kota Bekasi',\n '73': 'Kota Depok',\n },\n '31': { \n '01': 'Kota Jakarta Selatan',\n '02': 'Kota Jakarta Timur',\n '03': 'Kota Jakarta Pusat',\n '04': 'Kota Jakarta Barat',\n '05': 'Kota Jakarta Utara',\n },\n};","import { PROVINCES } from './constants';\n\n/**\n * Validates a NIK (Nomor Induk Kependudukan) format.\n *\n * A valid NIK must:\n * - Be exactly 16 digits\n * - Have a valid province code (positions 1-2)\n * - Have a valid date (positions 7-12)\n * - Not be in the future\n * - Not be before 1900\n *\n * For female NIKs, the day is encoded as (actual day + 40).\n * For example, a female born on the 15th would have day = 55.\n *\n * @param nik - The 16-digit NIK string to validate\n * @returns `true` if the NIK is valid, `false` otherwise\n *\n * @example\n * ```typescript\n * validateNIK('3201234567890123'); // true - valid NIK\n * validateNIK('1234'); // false - wrong length\n * validateNIK('9912345678901234'); // false - invalid province\n * ```\n *\n * @public\n */\nexport function validateNIK(nik: string): boolean {\n if (!/^\\d{16}$/.test(nik)) {\n return false;\n }\n\n const provinceCode = nik.substring(0, 2);\n if (!PROVINCES[provinceCode]) {\n return false;\n }\n\n const yearStr = nik.substring(6, 8);\n const monthStr = nik.substring(8, 10);\n const dayStr = nik.substring(10, 12);\n\n const year = parseInt(yearStr, 10);\n const fullYear = year > 30 ? 1900 + year : 2000 + year;\n\n const month = parseInt(monthStr, 10);\n let day = parseInt(dayStr, 10);\n\n if (day > 40) {\n day = day - 40;\n }\n\n if (month < 1 || month > 12) {\n return false;\n }\n\n if (day < 1 || day > 31) {\n return false;\n }\n\n const testDate = new Date(fullYear, month - 1, day);\n if (\n testDate.getFullYear() !== fullYear ||\n testDate.getMonth() !== month - 1 ||\n testDate.getDate() !== day\n ) {\n return false;\n }\n\n const now = new Date();\n if (testDate > now || testDate < new Date(1900, 0, 1)) {\n return false;\n }\n\n return true;\n}\n","import { PROVINCES, REGENCIES } from './constants';\nimport { NIKInfo } from './types';\n\n/**\n * Parses a NIK and extracts all embedded information.\n *\n * Extracts province, regency, district codes, birth date, gender,\n * and serial number from a 16-digit NIK string.\n *\n * @param nik - The 16-digit NIK string to parse\n * @returns Parsed NIK information, or `null` if the NIK format is invalid\n *\n * @example\n * Parse a valid male NIK:\n * ```typescript\n * const info = parseNIK('3201018901310123');\n * console.log(info);\n * // {\n * // province: { code: '32', name: 'Jawa Barat' },\n * // regency: { code: '01', name: 'Kab. Bogor' },\n * // district: { code: '01', name: null },\n * // birthDate: Date(1989, 0, 31), // Jan 31, 1989\n * // gender: 'male',\n * // serialNumber: '0123',\n * // isValid: true\n * // }\n * ```\n *\n * @example\n * Parse a female NIK (day + 40):\n * ```typescript\n * const info = parseNIK('3201019508550123');\n * console.log(info.gender); // 'female'\n * console.log(info.birthDate); // Date(1995, 7, 15) - Aug 15, 1995\n * ```\n *\n * @example\n * Invalid NIK returns null:\n * ```typescript\n * const info = parseNIK('invalid');\n * console.log(info); // null\n * ```\n *\n * @public\n */\nexport function parseNIK(nik: string): NIKInfo | null {\n if (!/^\\d{16}$/.test(nik)) {\n return null;\n }\n\n const provinceCode = nik.substring(0, 2);\n const regencyCode = nik.substring(2, 4);\n const districtCode = nik.substring(4, 6);\n const yearStr = nik.substring(6, 8);\n const monthStr = nik.substring(8, 10);\n const dayStr = nik.substring(10, 12);\n const serialNumber = nik.substring(12, 16);\n\n const province = PROVINCES[provinceCode];\n if (!province) {\n return null;\n }\n\n const regencies = REGENCIES[provinceCode] || {};\n const regency = regencies[regencyCode] || 'Unknown';\n\n let day = parseInt(dayStr, 10);\n const month = parseInt(monthStr, 10);\n const year = parseInt(yearStr, 10);\n\n let gender: 'male' | 'female' | null = null;\n if (day > 40) {\n gender = 'female';\n day -= 40;\n } else {\n gender = 'male';\n }\n\n const fullYear = year > 30 ? 1900 + year : 2000 + year;\n\n const birthDate = new Date(fullYear, month - 1, day);\n if (\n birthDate.getFullYear() !== fullYear ||\n birthDate.getMonth() !== month - 1 ||\n birthDate.getDate() !== day\n ) {\n return null;\n }\n\n return {\n province: {\n code: provinceCode,\n name: province,\n },\n regency: {\n code: regencyCode,\n name: regency,\n },\n district: {\n code: districtCode,\n name: null,\n },\n birthDate,\n gender,\n serialNumber,\n isValid: true,\n };\n}","import { MaskOptions } from './types';\n\n/**\n * Formats a NIK with separators for better readability.\n *\n * Groups the NIK into logical segments: province, regency, district,\n * year, month, day, and serial number.\n *\n * @param nik - The 16-digit NIK string to format\n * @param separator - Character to use as separator\n * @returns Formatted NIK string, or original string if invalid format\n *\n * @example\n * Default separator (dash):\n * ```typescript\n * formatNIK('3201234567890123');\n * // '32-01-23-45-67-89-0123'\n * ```\n *\n * @example\n * Custom separator:\n * ```typescript\n * formatNIK('3201234567890123', ' ');\n * // '32 01 23 45 67 89 0123'\n * ```\n *\n * @example\n * Invalid NIK returns as-is:\n * ```typescript\n * formatNIK('1234');\n * // '1234'\n * ```\n *\n * @public\n */\nexport function formatNIK(nik: string, separator: string = '-'): string {\n if (!/^\\d{16}$/.test(nik)) {\n return nik;\n }\n\n // Format: PP-KK-DD-YY-MM-DD-XXXX\n // PP = Province (2 digits)\n // KK = Regency (2 digits)\n // DD = District (2 digits)\n // YY = Year (2 digits)\n // MM = Month (2 digits)\n // DD = Day (2 digits, +40 for female)\n // XXXX = Serial number (4 digits)\n return [\n nik.substring(0, 2), // Province\n nik.substring(2, 4), // Regency\n nik.substring(4, 6), // District\n nik.substring(6, 8), // Year\n nik.substring(8, 10), // Month\n nik.substring(10, 12), // Day\n nik.substring(12, 16), // Serial\n ].join(separator);\n}\n\n/**\n * Masks a NIK to protect privacy while keeping partial visibility.\n *\n * By default, shows the first 4 and last 4 digits, masking the middle 8.\n * Optionally formats the masked NIK with separators.\n *\n * @param nik - The 16-digit NIK string to mask\n * @param options - Masking configuration options\n * @returns Masked NIK string, or original string if invalid format\n *\n * @example\n * Default masking (first 4, last 4):\n * ```typescript\n * maskNIK('3201234567890123');\n * // '3201********0123'\n * ```\n *\n * @example\n * Custom mask character:\n * ```typescript\n * maskNIK('3201234567890123', { char: 'X' });\n * // '3201XXXXXXXX0123'\n * ```\n *\n * @example\n * With separator:\n * ```typescript\n * maskNIK('3201234567890123', { separator: '-' });\n * // '32-01-**-**-**-**-0123'\n * ```\n *\n * @example\n * Custom start and end:\n * ```typescript\n * maskNIK('3201234567890123', { start: 6, end: 4 });\n * // '320123******0123'\n * ```\n *\n * @public\n */\nexport function maskNIK(nik: string, options: MaskOptions = {}): string {\n if (!/^\\d{16}$/.test(nik)) {\n return nik;\n }\n\n const { start = 4, end = 4, char = '*', separator } = options;\n\n if (start + end >= 16) {\n return nik;\n }\n\n if (separator) {\n // Format with separator first, then apply masking\n const formatted = formatNIK(nik, separator);\n const parts = formatted.split(separator);\n\n // Calculate which parts to mask\n // Format: PP-KK-DD-YY-MM-DD-XXXX (7 parts)\n // Mask parts based on character positions\n let charCount = 0;\n const maskedParts = parts.map((part) => {\n const partStart = charCount;\n const partEnd = charCount + part.length;\n charCount += part.length;\n\n // Check if this part should be fully/partially masked\n if (partEnd <= start) {\n // Fully visible (before start)\n return part;\n } else if (partStart >= 16 - end) {\n // Fully visible (after end)\n return part;\n } else if (partStart >= start && partEnd <= 16 - end) {\n // Fully masked\n return char.repeat(part.length);\n } else {\n // Partially masked\n return part\n .split('')\n .map((ch, idx) => {\n const pos = partStart + idx;\n if (pos < start || pos >= 16 - end) {\n return ch;\n }\n return char;\n })\n .join('');\n }\n });\n\n return maskedParts.join(separator);\n }\n\n // Without separator: simple masking\n const startPart = nik.substring(0, start);\n const endPart = nik.substring(16 - end);\n const maskLength = 16 - start - end;\n return startPart + char.repeat(maskLength) + endPart;\n}","import { parseNIK } from './parse';\n\n/**\n * Calculates the age of a person based on their NIK.\n *\n * @param nik - The 16-digit NIK string\n * @param referenceDate - The date to calculate age from (default: current date)\n * @returns The age in years, or null if the NIK is invalid or birth date cannot be parsed\n *\n * @example\n * ```typescript\n * getAge('3201018901310123'); // 35 (as of 2024)\n * ```\n */\nexport function getAge(\n nik: string,\n referenceDate: Date = new Date()\n): number | null {\n const info = parseNIK(nik);\n if (!info || !info.birthDate) {\n return null;\n }\n\n const birthDate = info.birthDate;\n let age = referenceDate.getFullYear() - birthDate.getFullYear();\n const m = referenceDate.getMonth() - birthDate.getMonth();\n\n if (m < 0 || (m === 0 && referenceDate.getDate() < birthDate.getDate())) {\n age--;\n }\n\n return age;\n}\n\n/**\n * Formats the birth date from a NIK into a human-readable string.\n *\n * @param nik - The 16-digit NIK string\n * @param locale - The locale to use for formatting (default: 'id-ID')\n * @returns Formatted birth date string, or null if invalid\n *\n * @example\n * ```typescript\n * formatBirthDate('3201018901310123'); // '31 Januari 1989'\n * ```\n */\nexport function formatBirthDate(\n nik: string,\n options: Intl.DateTimeFormatOptions = {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n },\n locale: string = 'id-ID'\n): string | null {\n const info = parseNIK(nik);\n if (!info || !info.birthDate) {\n return null;\n }\n\n return new Intl.DateTimeFormat(locale, options).format(info.birthDate);\n}\n\n/**\n * Checks if a NIK matches a specific gender.\n *\n * @param nik - The 16-digit NIK string\n * @param gender - The gender to check ('male' | 'female')\n * @returns True if the NIK matches the gender, false otherwise\n */\nexport function isValidForGender(\n nik: string,\n gender: 'male' | 'female'\n): boolean {\n const info = parseNIK(nik);\n if (!info) {\n return false;\n }\n return info.gender === gender;\n}\n\n/**\n * Checks if a NIK matches a specific birth date.\n *\n * @param nik - The 16-digit NIK string\n * @param birthDate - The birth date to check\n * @returns True if the NIK matches the birth date, false otherwise\n */\nexport function isValidForBirthDate(nik: string, birthDate: Date): boolean {\n const info = parseNIK(nik);\n if (!info || !info.birthDate) {\n return false;\n }\n\n return (\n info.birthDate.getFullYear() === birthDate.getFullYear() &&\n info.birthDate.getMonth() === birthDate.getMonth() &&\n info.birthDate.getDate() === birthDate.getDate()\n );\n}\n"]}
|
package/dist/phone/index.cjs
CHANGED
|
@@ -642,6 +642,37 @@ function maskPhoneNumber(phone, options = {}) {
|
|
|
642
642
|
return masked;
|
|
643
643
|
}
|
|
644
644
|
|
|
645
|
+
// src/phone/links.ts
|
|
646
|
+
function generateWALink(phone, message) {
|
|
647
|
+
if (!validatePhoneNumber(phone)) {
|
|
648
|
+
return "";
|
|
649
|
+
}
|
|
650
|
+
const e164 = toE164(phone);
|
|
651
|
+
let link = `https://wa.me/${e164}`;
|
|
652
|
+
if (message) {
|
|
653
|
+
link += `?text=${encodeURIComponent(message)}`;
|
|
654
|
+
}
|
|
655
|
+
return link;
|
|
656
|
+
}
|
|
657
|
+
function generateSmsLink(phone, body) {
|
|
658
|
+
if (!validatePhoneNumber(phone)) {
|
|
659
|
+
return "";
|
|
660
|
+
}
|
|
661
|
+
const e164 = toE164(phone);
|
|
662
|
+
let link = `sms:+${e164}`;
|
|
663
|
+
if (body) {
|
|
664
|
+
link += `?body=${encodeURIComponent(body)}`;
|
|
665
|
+
}
|
|
666
|
+
return link;
|
|
667
|
+
}
|
|
668
|
+
function generateTelLink(phone) {
|
|
669
|
+
if (!validatePhoneNumber(phone)) {
|
|
670
|
+
return "";
|
|
671
|
+
}
|
|
672
|
+
const e164 = toE164(phone);
|
|
673
|
+
return `tel:+${e164}`;
|
|
674
|
+
}
|
|
675
|
+
|
|
645
676
|
// src/phone/parse.ts
|
|
646
677
|
function parsePhoneNumber(phone) {
|
|
647
678
|
if (!validatePhoneNumber(phone)) {
|
|
@@ -690,6 +721,13 @@ function getOperator(phone) {
|
|
|
690
721
|
const prefix = normalized.substring(0, 4);
|
|
691
722
|
return OPERATOR_PREFIXES[prefix] || null;
|
|
692
723
|
}
|
|
724
|
+
function isProvider(phone, providerName) {
|
|
725
|
+
const operator = getOperator(phone);
|
|
726
|
+
if (!operator) {
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
return operator.toLowerCase() === providerName.toLowerCase();
|
|
730
|
+
}
|
|
693
731
|
function getRegion(phone) {
|
|
694
732
|
if (!phone.startsWith("0")) {
|
|
695
733
|
return null;
|
|
@@ -717,9 +755,13 @@ function normalizeToNational2(phone) {
|
|
|
717
755
|
|
|
718
756
|
exports.cleanPhoneNumber = cleanPhoneNumber;
|
|
719
757
|
exports.formatPhoneNumber = formatPhoneNumber;
|
|
758
|
+
exports.generateSmsLink = generateSmsLink;
|
|
759
|
+
exports.generateTelLink = generateTelLink;
|
|
760
|
+
exports.generateWALink = generateWALink;
|
|
720
761
|
exports.getOperator = getOperator;
|
|
721
762
|
exports.isLandlineNumber = isLandlineNumber;
|
|
722
763
|
exports.isMobileNumber = isMobileNumber;
|
|
764
|
+
exports.isProvider = isProvider;
|
|
723
765
|
exports.maskPhoneNumber = maskPhoneNumber;
|
|
724
766
|
exports.parsePhoneNumber = parsePhoneNumber;
|
|
725
767
|
exports.toE164 = toE164;
|