const axios = require('axios'); async function nikParse(nik) { try { const provincesRes = await axios.get('https://emsifa.github.io/api-wilayah-indonesia/api/provinces.json'); const provinces = Object.fromEntries(provincesRes.data.map(p => [p.id, p.name.toUpperCase()])); nik = nik.toString(); if (nik.length !== 16 || !provinces[nik.slice(0, 2)]) throw new Error('NIK tidak valid: panjang atau kode provinsi salah'); const provinceId = nik.slice(0, 2); const regenciesRes = await axios.get(`https://emsifa.github.io/api-wilayah-indonesia/api/regencies/${provinceId}.json`); const regencies = Object.fromEntries(regenciesRes.data.map(r => [r.id, r.name.toUpperCase()])); if (!regencies[nik.slice(0, 4)]) throw new Error('NIK tidak valid: kode kabupaten/kota salah'); const regencyId = nik.slice(0, 4); const districtsRes = await axios.get(`https://emsifa.github.io/api-wilayah-indonesia/api/districts/${regencyId}.json`); const districts = Object.fromEntries(districtsRes.data.map(d => [d.id.slice(0, -1), `${d.name.toUpperCase()}`])); if (!districts[nik.slice(0, 6)]) throw new Error('NIK tidak valid: kode kecamatan salah'); const province = provinces[provinceId]; const city = regencies[regencyId]; const subdistrict = districts[nik.slice(0, 6)]; const day = parseInt(nik.slice(6, 8)); const month = parseInt(nik.slice(8, 10)); const yearCode = nik.slice(10, 12); const uniqCode = nik.slice(12, 16); const gender = day > 40 ? 'PEREMPUAN' : 'LAKI-LAKI'; const birthDay = day > 40 ? (day - 40).toString().padStart(2, '0') : day.toString().padStart(2, '0'); const birthYear = yearCode < new Date().getFullYear().toString().slice(-2) ? `20${yearCode}` : `19${yearCode}`; const birthDate = `${birthDay}/${month.toString().padStart(2, '0')}/${birthYear}`; const birth = new Date(birthYear, month - 1, birthDay); if (isNaN(birth.getTime())) throw new Error('Tanggal lahir tidak valid'); const today = new Date(); let years = today.getFullYear() - birth.getFullYear(); let months = today.getMonth() - birth.getMonth(); let remainingDays = today.getDate() - birth.getDate(); if (remainingDays < 0) { remainingDays += new Date(today.getFullYear(), today.getMonth(), 0).getDate(); months--; } if (months < 0) { months += 12; years--; } const age = `${years} Tahun ${months} Bulan ${remainingDays} Hari`; let ageCategory = ''; if (years < 12) ageCategory = 'Anak-anak'; else if (years < 18) ageCategory = 'Remaja'; else if (years < 60) ageCategory = 'Dewasa'; else ageCategory = 'Lansia'; const nextBirthday = new Date(today.getMonth() < month - 1 ? today.getFullYear() : today.getFullYear() + 1, month - 1, birthDay); const timeDiff = nextBirthday - today; const monthsLeft = Math.floor(timeDiff / (1000 * 60 * 60 * 24 * 30)); const daysLeft = Math.floor((timeDiff % (1000 * 60 * 60 * 24 * 30)) / (1000 * 60 * 60 * 24)); const birthdayCountdown = `${monthsLeft} Bulan ${daysLeft} Hari`; const baseDate = new Date(1970, 0, 2); const diffDays = Math.floor((birth - baseDate + 86400000) / (1000 * 60 * 60 * 24)); const pasaranIndex = Math.round((diffDays % 5) * 2) / 2; const pasaranNames = ['Wage', 'Kliwon', 'Legi', 'Pahing', 'Pon']; const dayNames = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu']; const monthNames = ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember']; const birthDateFull = `${birthDay} ${monthNames[month - 1]} ${birthYear}`; const pasaran = `${dayNames[birth.getDay()]} ${pasaranNames[pasaranIndex] || ''}, ${birthDay} ${monthNames[month - 1]} ${birthYear}`; let zodiac = ''; if ((month === 1 && day >= 20) || (month === 2 && day < 19)) zodiac = 'Aquarius'; else if ((month === 2 && day >= 19) || (month === 3 && day < 21)) zodiac = 'Pisces'; else if ((month === 3 && day >= 21) || (month === 4 && day < 20)) zodiac = 'Aries'; else if ((month === 4 && day >= 20) || (month === 5 && day < 21)) zodiac = 'Taurus'; else if ((month === 5 && day >= 21) || (month === 6 && day < 22)) zodiac = 'Gemini'; else if ((month === 6 && day >= 21) || (month === 7 && day < 23)) zodiac = 'Cancer'; else if ((month === 7 && day >= 23) || (month === 8 && day < 23)) zodiac = 'Leo'; else if ((month === 8 && day >= 23) || (month === 9 && day < 23)) zodiac = 'Virgo'; else if ((month === 9 && day >= 23) || (month === 10 && day < 24)) zodiac = 'Libra'; else if ((month === 10 && day >= 24) || (month === 11 && day < 23)) zodiac = 'Scorpio'; else if ((month === 11 && day >= 23) || (month === 12 && day < 22)) zodiac = 'Sagitarius'; else if ((month === 12 && day >= 22) || (month === 1 && day < 20)) zodiac = 'Capricorn'; const regencyType = city.includes('KOTA') ? 'Kota' : 'Kabupaten'; const areaCode = `${provinceId}.${regencyId.slice(2)}.${nik.slice(4, 6)}`; return { nik, kelamin: gender, lahir: birthDate, lahir_lengkap: birthDateFull, provinsi: { kode: provinceId, nama: province }, kotakab: { kode: regencyId, nama: city, jenis: regencyType }, kecamatan: { kode: nik.slice(0, 6), nama: subdistrict }, kode_wilayah: areaCode, nomor_urut: uniqCode, tambahan: { pasaran, usia: age, kategori_usia: ageCategory, ultah: `${birthdayCountdown} Lagi`, zodiak: zodiac } }; } catch (error) { throw new Error(error.message); } } // Usage: const resp = await nikParse('XXXXXXXXXXXXXXXX'); console.log(resp);