UNPKG

indokit

Version:

Utility TypeScript untuk format & validasi data Indonesia (Rupiah, NIK, phone, slug, dsb.)

227 lines (219 loc) 7.8 kB
'use strict'; function formatRupiah(angka) { if (Number.isNaN(Number(angka))) throw new TypeError('formatRupiah: angka must be a number'); return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR' }).format(angka); } function formatTanggalID(date) { const d = new Date(date); if (Number.isNaN(d.getTime())) throw new TypeError('formatTanggalID: invalid date'); return new Intl.DateTimeFormat('id-ID', { day: 'numeric', month: 'long', year: 'numeric' }).format(d); } function isValidNIK(nik) { if (typeof nik !== 'string') return false; return /^[0-9]{16}$/.test(nik); } function isValidPhone(phone) { if (typeof phone !== 'string') return false; return /^(?:\+62|62|0)8[1-9][0-9]{6,9}$/.test(phone); } function terbilang(input) { // Validasi dan normalisasi input const normalizedInput = normalizeInput(input); // Handle angka negatif if (normalizedInput < 0) { return 'minus ' + terbilangPositive(Math.abs(normalizedInput)); } return terbilangPositive(normalizedInput); } function normalizeInput(input) { // Handle null, undefined if (input === null || input === undefined) { throw new TypeError('terbilang: input tidak boleh null atau undefined'); } // Handle boolean if (typeof input === 'boolean') { throw new TypeError('terbilang: input tidak boleh berupa boolean'); } // Handle object, array, function if (typeof input === 'object' || typeof input === 'function') { throw new TypeError('terbilang: input harus berupa angka atau string yang valid'); } let processedInput; if (typeof input === 'string') { // Trim whitespace const trimmed = input.trim(); // Handle empty string if (trimmed === '') { throw new TypeError('terbilang: string tidak boleh kosong'); } // Handle special string cases first if (trimmed.toLowerCase() === 'infinity' || trimmed.toLowerCase() === 'inf') { throw new TypeError('terbilang: input tidak boleh infinity'); } if (trimmed.toLowerCase() === 'nan') { throw new TypeError('terbilang: input tidak boleh NaN'); } // Check for mixed alphanumeric (contains both letters and numbers) const hasLetters = /[a-zA-Z]/.test(trimmed); const hasNumbers = /[0-9]/.test(trimmed); if (hasLetters && hasNumbers) { throw new TypeError('terbilang: input tidak boleh mengandung campuran huruf dan angka'); } // If contains only letters, reject if (hasLetters && !hasNumbers) { throw new TypeError('terbilang: input tidak boleh berupa huruf'); } // Validate that the string is a proper number format // This regex allows: optional +/-, digits, optional single decimal point with digits const numberRegex = /^[+-]?(\d+\.?\d*|\.\d+)$/; if (!numberRegex.test(trimmed)) { throw new TypeError('terbilang: string tidak dapat dikonversi menjadi angka yang valid'); } // Parse string to number processedInput = parseFloat(trimmed); // Check if parsing resulted in NaN (shouldn't happen with regex validation, but just in case) if (isNaN(processedInput)) { throw new TypeError('terbilang: string tidak dapat dikonversi menjadi angka yang valid'); } } else if (typeof input === 'number') { processedInput = input; } else { // Try to convert other types to number processedInput = Number(input); if (isNaN(processedInput)) { throw new TypeError('terbilang: input tidak dapat dikonversi menjadi angka yang valid'); } } // Check for special number values if (isNaN(processedInput)) { throw new TypeError('terbilang: input tidak boleh NaN'); } if (!isFinite(processedInput)) { throw new TypeError('terbilang: input tidak boleh infinity atau -infinity'); } // Check if it's an integer (allow decimal strings that represent integers) if (!Number.isInteger(processedInput)) { throw new TypeError('terbilang: input harus berupa bilangan bulat'); } // Check maximum safe integer if (Math.abs(processedInput) > Number.MAX_SAFE_INTEGER) { throw new TypeError('terbilang: angka terlalu besar (melebihi batas maksimum JavaScript)'); } // Check reasonable limits for Indonesian number system if (Math.abs(processedInput) >= 1000000000000000) { // 1 kuadriliun throw new TypeError('terbilang: angka terlalu besar (maksimum 999 triliun)'); } return processedInput; } function terbilangPositive(angka) { if (angka === 0) return 'nol'; const satuan = ['', 'satu', 'dua', 'tiga', 'empat', 'lima', 'enam', 'tujuh', 'delapan', 'sembilan']; const belasan = ['sepuluh', 'sebelas', 'dua belas', 'tiga belas', 'empat belas', 'lima belas', 'enam belas', 'tujuh belas', 'delapan belas', 'sembilan belas']; function konversiRatusan(n) { let hasil = ''; // Ratusan if (n >= 100) { const ratus = Math.floor(n / 100); if (ratus === 1) { hasil += 'seratus'; } else { hasil += satuan[ratus] + ' ratus'; } n %= 100; if (n > 0) hasil += ' '; } // Puluhan dan satuan if (n >= 20) { const puluh = Math.floor(n / 10); hasil += satuan[puluh] + ' puluh'; n %= 10; if (n > 0) hasil += ' ' + satuan[n]; } else if (n >= 10) { hasil += belasan[n - 10]; } else if (n > 0) { hasil += satuan[n]; } return hasil; } if (angka < 1000) { return konversiRatusan(angka); } let hasil = ''; // Triliun if (angka >= 1000000000000) { const triliun = Math.floor(angka / 1000000000000); hasil += konversiRatusan(triliun) + ' triliun'; angka %= 1000000000000; if (angka > 0) hasil += ' '; } // Miliar if (angka >= 1000000000) { const miliar = Math.floor(angka / 1000000000); hasil += konversiRatusan(miliar) + ' miliar'; angka %= 1000000000; if (angka > 0) hasil += ' '; } // Juta if (angka >= 1000000) { const juta = Math.floor(angka / 1000000); hasil += konversiRatusan(juta) + ' juta'; angka %= 1000000; if (angka > 0) hasil += ' '; } // Ribu if (angka >= 1000) { const ribu = Math.floor(angka / 1000); if (ribu === 1) { hasil += 'seribu'; } else { hasil += konversiRatusan(ribu) + ' ribu'; } angka %= 1000; if (angka > 0) hasil += ' '; } // Sisa (ratusan, puluhan, satuan) if (angka > 0) { hasil += konversiRatusan(angka); } return hasil; } function randomString(length) { if (!Number.isInteger(length) || length < 0) throw new TypeError('randomString: length must be a non-negative integer'); const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } exports.formatRupiah = formatRupiah; exports.formatTanggalID = formatTanggalID; exports.isValidNIK = isValidNIK; exports.isValidPhone = isValidPhone; exports.randomString = randomString; exports.terbilang = terbilang;