@indodev/toolkit 0.0.1
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/LICENCE +21 -0
- package/README.md +293 -0
- package/dist/currency/index.cjs +245 -0
- package/dist/currency/index.cjs.map +1 -0
- package/dist/currency/index.d.cts +43 -0
- package/dist/currency/index.d.ts +43 -0
- package/dist/currency/index.js +239 -0
- package/dist/currency/index.js.map +1 -0
- package/dist/index.cjs +1173 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1153 -0
- package/dist/index.js.map +1 -0
- package/dist/nik/index.cjs +218 -0
- package/dist/nik/index.cjs.map +1 -0
- package/dist/nik/index.d.cts +288 -0
- package/dist/nik/index.d.ts +288 -0
- package/dist/nik/index.js +213 -0
- package/dist/nik/index.js.map +1 -0
- package/dist/phone/index.cjs +730 -0
- package/dist/phone/index.cjs.map +1 -0
- package/dist/phone/index.d.cts +472 -0
- package/dist/phone/index.d.ts +472 -0
- package/dist/phone/index.js +718 -0
- package/dist/phone/index.js.map +1 -0
- package/dist/words-Dy8iYkbv.d.cts +333 -0
- package/dist/words-Dy8iYkbv.d.ts +333 -0
- package/package.json +120 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// src/nik/constants.ts
|
|
2
|
+
var PROVINCES = {
|
|
3
|
+
"11": "Aceh",
|
|
4
|
+
"12": "Sumatera Utara",
|
|
5
|
+
"13": "Sumatera Barat",
|
|
6
|
+
"14": "Riau",
|
|
7
|
+
"15": "Jambi",
|
|
8
|
+
"16": "Sumatera Selatan",
|
|
9
|
+
"17": "Bengkulu",
|
|
10
|
+
"18": "Lampung",
|
|
11
|
+
"19": "Kepulauan Bangka Belitung",
|
|
12
|
+
"21": "Kepulauan Riau",
|
|
13
|
+
"31": "DKI Jakarta",
|
|
14
|
+
"32": "Jawa Barat",
|
|
15
|
+
"33": "Jawa Tengah",
|
|
16
|
+
"34": "DI Yogyakarta",
|
|
17
|
+
"35": "Jawa Timur",
|
|
18
|
+
"36": "Banten",
|
|
19
|
+
"51": "Bali",
|
|
20
|
+
"52": "Nusa Tenggara Barat",
|
|
21
|
+
"53": "Nusa Tenggara Timur",
|
|
22
|
+
"61": "Kalimantan Barat",
|
|
23
|
+
"62": "Kalimantan Tengah",
|
|
24
|
+
"63": "Kalimantan Selatan",
|
|
25
|
+
"64": "Kalimantan Timur",
|
|
26
|
+
"65": "Kalimantan Utara",
|
|
27
|
+
"71": "Sulawesi Utara",
|
|
28
|
+
"72": "Sulawesi Tengah",
|
|
29
|
+
"73": "Sulawesi Selatan",
|
|
30
|
+
"74": "Sulawesi Tenggara",
|
|
31
|
+
"75": "Gorontalo",
|
|
32
|
+
"76": "Sulawesi Barat",
|
|
33
|
+
"81": "Maluku",
|
|
34
|
+
"82": "Maluku Utara",
|
|
35
|
+
"91": "Papua",
|
|
36
|
+
"92": "Papua Barat",
|
|
37
|
+
"93": "Papua Selatan",
|
|
38
|
+
"94": "Papua Tengah",
|
|
39
|
+
"95": "Papua Pegunungan",
|
|
40
|
+
"96": "Papua Barat Daya"
|
|
41
|
+
};
|
|
42
|
+
var REGENCIES = {
|
|
43
|
+
"32": {
|
|
44
|
+
"01": "Kab. Bogor",
|
|
45
|
+
"02": "Kab. Sukabumi",
|
|
46
|
+
"03": "Kab. Cianjur",
|
|
47
|
+
"71": "Kota Bandung",
|
|
48
|
+
"72": "Kota Bekasi",
|
|
49
|
+
"73": "Kota Depok"
|
|
50
|
+
},
|
|
51
|
+
"31": {
|
|
52
|
+
"01": "Kota Jakarta Selatan",
|
|
53
|
+
"02": "Kota Jakarta Timur",
|
|
54
|
+
"03": "Kota Jakarta Pusat",
|
|
55
|
+
"04": "Kota Jakarta Barat",
|
|
56
|
+
"05": "Kota Jakarta Utara"
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// src/nik/validate.ts
|
|
61
|
+
function validateNIK(nik) {
|
|
62
|
+
if (!/^\d{16}$/.test(nik)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const provinceCode = nik.substring(0, 2);
|
|
66
|
+
if (!PROVINCES[provinceCode]) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const yearStr = nik.substring(6, 8);
|
|
70
|
+
const monthStr = nik.substring(8, 10);
|
|
71
|
+
const dayStr = nik.substring(10, 12);
|
|
72
|
+
const year = parseInt(yearStr, 10);
|
|
73
|
+
const fullYear = year > 30 ? 1900 + year : 2e3 + year;
|
|
74
|
+
const month = parseInt(monthStr, 10);
|
|
75
|
+
let day = parseInt(dayStr, 10);
|
|
76
|
+
if (day > 40) {
|
|
77
|
+
day = day - 40;
|
|
78
|
+
}
|
|
79
|
+
if (month < 1 || month > 12) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
if (day < 1 || day > 31) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const testDate = new Date(fullYear, month - 1, day);
|
|
86
|
+
if (testDate.getFullYear() !== fullYear || testDate.getMonth() !== month - 1 || testDate.getDate() !== day) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const now = /* @__PURE__ */ new Date();
|
|
90
|
+
if (testDate > now || testDate < new Date(1900, 0, 1)) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/nik/parse.ts
|
|
97
|
+
function parseNIK(nik) {
|
|
98
|
+
if (!/^\d{16}$/.test(nik)) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const provinceCode = nik.substring(0, 2);
|
|
102
|
+
const regencyCode = nik.substring(2, 4);
|
|
103
|
+
const districtCode = nik.substring(4, 6);
|
|
104
|
+
const yearStr = nik.substring(6, 8);
|
|
105
|
+
const monthStr = nik.substring(8, 10);
|
|
106
|
+
const dayStr = nik.substring(10, 12);
|
|
107
|
+
const serialNumber = nik.substring(12, 16);
|
|
108
|
+
const province = PROVINCES[provinceCode];
|
|
109
|
+
if (!province) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const regencies = REGENCIES[provinceCode] || {};
|
|
113
|
+
const regency = regencies[regencyCode] || "Unknown";
|
|
114
|
+
let day = parseInt(dayStr, 10);
|
|
115
|
+
const month = parseInt(monthStr, 10);
|
|
116
|
+
const year = parseInt(yearStr, 10);
|
|
117
|
+
let gender = null;
|
|
118
|
+
if (day > 40) {
|
|
119
|
+
gender = "female";
|
|
120
|
+
day -= 40;
|
|
121
|
+
} else {
|
|
122
|
+
gender = "male";
|
|
123
|
+
}
|
|
124
|
+
const fullYear = year > 30 ? 1900 + year : 2e3 + year;
|
|
125
|
+
const birthDate = new Date(fullYear, month - 1, day);
|
|
126
|
+
if (birthDate.getFullYear() !== fullYear || birthDate.getMonth() !== month - 1 || birthDate.getDate() !== day) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
province: {
|
|
131
|
+
code: provinceCode,
|
|
132
|
+
name: province
|
|
133
|
+
},
|
|
134
|
+
regency: {
|
|
135
|
+
code: regencyCode,
|
|
136
|
+
name: regency
|
|
137
|
+
},
|
|
138
|
+
district: {
|
|
139
|
+
code: districtCode,
|
|
140
|
+
name: null
|
|
141
|
+
},
|
|
142
|
+
birthDate,
|
|
143
|
+
gender,
|
|
144
|
+
serialNumber,
|
|
145
|
+
isValid: true
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/nik/format.ts
|
|
150
|
+
function formatNIK(nik, separator = "-") {
|
|
151
|
+
if (!/^\d{16}$/.test(nik)) {
|
|
152
|
+
return nik;
|
|
153
|
+
}
|
|
154
|
+
return [
|
|
155
|
+
nik.substring(0, 2),
|
|
156
|
+
// Province
|
|
157
|
+
nik.substring(2, 4),
|
|
158
|
+
// Regency
|
|
159
|
+
nik.substring(4, 6),
|
|
160
|
+
// District
|
|
161
|
+
nik.substring(6, 8),
|
|
162
|
+
// Year
|
|
163
|
+
nik.substring(8, 10),
|
|
164
|
+
// Month
|
|
165
|
+
nik.substring(10, 12),
|
|
166
|
+
// Day
|
|
167
|
+
nik.substring(12, 16)
|
|
168
|
+
// Serial
|
|
169
|
+
].join(separator);
|
|
170
|
+
}
|
|
171
|
+
function maskNIK(nik, options = {}) {
|
|
172
|
+
if (!/^\d{16}$/.test(nik)) {
|
|
173
|
+
return nik;
|
|
174
|
+
}
|
|
175
|
+
const { start = 4, end = 4, char = "*", separator } = options;
|
|
176
|
+
if (start + end >= 16) {
|
|
177
|
+
return nik;
|
|
178
|
+
}
|
|
179
|
+
if (separator) {
|
|
180
|
+
const formatted = formatNIK(nik, separator);
|
|
181
|
+
const parts = formatted.split(separator);
|
|
182
|
+
let charCount = 0;
|
|
183
|
+
const maskedParts = parts.map((part) => {
|
|
184
|
+
const partStart = charCount;
|
|
185
|
+
const partEnd = charCount + part.length;
|
|
186
|
+
charCount += part.length;
|
|
187
|
+
if (partEnd <= start) {
|
|
188
|
+
return part;
|
|
189
|
+
} else if (partStart >= 16 - end) {
|
|
190
|
+
return part;
|
|
191
|
+
} else if (partStart >= start && partEnd <= 16 - end) {
|
|
192
|
+
return char.repeat(part.length);
|
|
193
|
+
} else {
|
|
194
|
+
return part.split("").map((ch, idx) => {
|
|
195
|
+
const pos = partStart + idx;
|
|
196
|
+
if (pos < start || pos >= 16 - end) {
|
|
197
|
+
return ch;
|
|
198
|
+
}
|
|
199
|
+
return char;
|
|
200
|
+
}).join("");
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
return maskedParts.join(separator);
|
|
204
|
+
}
|
|
205
|
+
const startPart = nik.substring(0, start);
|
|
206
|
+
const endPart = nik.substring(16 - end);
|
|
207
|
+
const maskLength = 16 - start - end;
|
|
208
|
+
return startPart + char.repeat(maskLength) + endPart;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export { formatNIK, maskNIK, parseNIK, validateNIK };
|
|
212
|
+
//# sourceMappingURL=index.js.map
|
|
213
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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}"]}
|