nik-id 1.0.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.
@@ -0,0 +1,83 @@
1
+ import { GenerateOptions } from './types.js';
2
+
3
+ /**
4
+ * Modul generator NIK (Nomor Induk Kependudukan).
5
+ *
6
+ * Menyediakan fungsi {@link generateNIK} untuk membuat NIK yang valid
7
+ * secara format, baik full random maupun dengan parameter tertentu
8
+ * (wilayah, gender, tanggal lahir).
9
+ *
10
+ * NIK yang dihasilkan valid secara **format** — semua aturan digit terpenuhi —
11
+ * tapi **bukan** NIK asli milik orang sungguhan. Cocok untuk testing,
12
+ * seeding database, atau demo.
13
+ *
14
+ * @module generate
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { generateNIK } from 'nik-id/generate';
19
+ *
20
+ * // Full random
21
+ * const nik1 = generateNIK();
22
+ *
23
+ * // Perempuan lahir 25 Agustus 1985
24
+ * const nik2 = generateNIK({ gender: "F", birthDate: new Date("1985-08-25") });
25
+ *
26
+ * // Wilayah spesifik
27
+ * const nik3 = generateNIK({
28
+ * provinceCode: "32",
29
+ * regencyCode: "3204",
30
+ * districtCode: "320407",
31
+ * });
32
+ * ```
33
+ */
34
+
35
+ /**
36
+ * Generate NIK (Nomor Induk Kependudukan) yang valid secara format.
37
+ *
38
+ * Semua parameter opsional — yang tidak diisi akan di-random.
39
+ * NIK yang dihasilkan memenuhi semua aturan format:
40
+ * - Kode provinsi dalam range 11-97
41
+ * - Tanggal lahir valid secara kalender
42
+ * - Gender tercermin di encoding DD (perempuan: DD + 40)
43
+ * - Sequence number dalam range 0001-9999
44
+ *
45
+ * **Catatan:** NIK ini bukan data asli — hanya valid secara format,
46
+ * cocok untuk testing dan demo.
47
+ *
48
+ * @param options - Opsi untuk men-generate NIK (semua opsional)
49
+ * @returns String NIK 16 digit yang valid secara format
50
+ * @throws {Error} Jika `provinceCode` di luar range 11-97
51
+ * @throws {Error} Jika `regencyCode` tidak diawali `provinceCode`
52
+ * @throws {Error} Jika `districtCode` tidak diawali `regencyCode`
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * // Full random
57
+ * generateNIK();
58
+ * // "3204071508900123"
59
+ *
60
+ * // Gender spesifik
61
+ * generateNIK({ gender: "F" });
62
+ * // "xxxxxx5508900456" (DD + 40 untuk perempuan)
63
+ *
64
+ * // Tanggal lahir spesifik
65
+ * generateNIK({ birthDate: new Date("1985-08-25") });
66
+ * // "xxxxxx2508850789"
67
+ *
68
+ * // Gender dan tanggal lahir
69
+ * generateNIK({ gender: "F", birthDate: new Date("1985-08-25") });
70
+ * // "xxxxxx6508850234" (25 + 40 = 65 untuk perempuan)
71
+ *
72
+ * // Wilayah lengkap
73
+ * generateNIK({
74
+ * provinceCode: "32",
75
+ * regencyCode: "3204",
76
+ * districtCode: "320407",
77
+ * });
78
+ * // "3204071508900001"
79
+ * ```
80
+ */
81
+ declare function generateNIK(options?: GenerateOptions): string;
82
+
83
+ export { generateNIK };
@@ -0,0 +1,70 @@
1
+ // src/generate.ts
2
+ var MIN_PROVINCE_CODE = 11;
3
+ var MAX_PROVINCE_CODE = 97;
4
+ var MIN_BIRTH_YEAR = 1950;
5
+ var MAX_BIRTH_YEAR = 2005;
6
+ function randomInt(min, max) {
7
+ return Math.floor(Math.random() * (max - min + 1)) + min;
8
+ }
9
+ function padZero(num, length) {
10
+ return String(num).padStart(length, "0");
11
+ }
12
+ function getDaysInMonth(year, month) {
13
+ return new Date(year, month, 0).getDate();
14
+ }
15
+ function randomBirthDate() {
16
+ const year = randomInt(MIN_BIRTH_YEAR, MAX_BIRTH_YEAR);
17
+ const month = randomInt(1, 12);
18
+ const maxDay = getDaysInMonth(year, month);
19
+ const day = randomInt(1, maxDay);
20
+ return new Date(year, month - 1, day);
21
+ }
22
+ function generateNIK(options = {}) {
23
+ const { gender, birthDate } = options;
24
+ let provinceCode;
25
+ if (options.provinceCode !== void 0) {
26
+ const pc = Number.parseInt(options.provinceCode, 10);
27
+ if (Number.isNaN(pc) || pc < MIN_PROVINCE_CODE || pc > MAX_PROVINCE_CODE) {
28
+ throw new Error("Kode provinsi tidak valid (harus 11-97)");
29
+ }
30
+ provinceCode = padZero(pc, 2);
31
+ } else {
32
+ provinceCode = padZero(randomInt(MIN_PROVINCE_CODE, MAX_PROVINCE_CODE), 2);
33
+ }
34
+ let regencyCode;
35
+ if (options.regencyCode !== void 0) {
36
+ if (!options.regencyCode.startsWith(provinceCode)) {
37
+ throw new Error(
38
+ `Kode kabupaten/kota "${options.regencyCode}" tidak sesuai dengan kode provinsi "${provinceCode}"`
39
+ );
40
+ }
41
+ regencyCode = options.regencyCode;
42
+ } else {
43
+ regencyCode = provinceCode + padZero(randomInt(1, 99), 2);
44
+ }
45
+ let districtCode;
46
+ if (options.districtCode !== void 0) {
47
+ if (!options.districtCode.startsWith(regencyCode)) {
48
+ throw new Error(
49
+ `Kode kecamatan "${options.districtCode}" tidak sesuai dengan kode kabupaten/kota "${regencyCode}"`
50
+ );
51
+ }
52
+ districtCode = options.districtCode;
53
+ } else {
54
+ districtCode = regencyCode + padZero(randomInt(1, 99), 2);
55
+ }
56
+ const birth = birthDate ?? randomBirthDate();
57
+ const day = birth.getDate();
58
+ const month = birth.getMonth() + 1;
59
+ const year = birth.getFullYear();
60
+ const selectedGender = gender ?? (Math.random() < 0.5 ? "M" : "F");
61
+ const encodedDay = selectedGender === "F" ? day + 40 : day;
62
+ const ddStr = padZero(encodedDay, 2);
63
+ const mmStr = padZero(month, 2);
64
+ const yyStr = padZero(year % 100, 2);
65
+ const seq = padZero(randomInt(1, 9999), 4);
66
+ return districtCode + ddStr + mmStr + yyStr + seq;
67
+ }
68
+ export {
69
+ generateNIK
70
+ };
package/dist/index.cjs ADDED
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ generateNIK: () => generateNIK,
24
+ parseNIK: () => parseNIK,
25
+ validateNIK: () => validateNIK
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/generate.ts
30
+ var MIN_PROVINCE_CODE = 11;
31
+ var MAX_PROVINCE_CODE = 97;
32
+ var MIN_BIRTH_YEAR = 1950;
33
+ var MAX_BIRTH_YEAR = 2005;
34
+ function randomInt(min, max) {
35
+ return Math.floor(Math.random() * (max - min + 1)) + min;
36
+ }
37
+ function padZero(num, length) {
38
+ return String(num).padStart(length, "0");
39
+ }
40
+ function getDaysInMonth(year, month) {
41
+ return new Date(year, month, 0).getDate();
42
+ }
43
+ function randomBirthDate() {
44
+ const year = randomInt(MIN_BIRTH_YEAR, MAX_BIRTH_YEAR);
45
+ const month = randomInt(1, 12);
46
+ const maxDay = getDaysInMonth(year, month);
47
+ const day = randomInt(1, maxDay);
48
+ return new Date(year, month - 1, day);
49
+ }
50
+ function generateNIK(options = {}) {
51
+ const { gender, birthDate } = options;
52
+ let provinceCode;
53
+ if (options.provinceCode !== void 0) {
54
+ const pc = Number.parseInt(options.provinceCode, 10);
55
+ if (Number.isNaN(pc) || pc < MIN_PROVINCE_CODE || pc > MAX_PROVINCE_CODE) {
56
+ throw new Error("Kode provinsi tidak valid (harus 11-97)");
57
+ }
58
+ provinceCode = padZero(pc, 2);
59
+ } else {
60
+ provinceCode = padZero(randomInt(MIN_PROVINCE_CODE, MAX_PROVINCE_CODE), 2);
61
+ }
62
+ let regencyCode;
63
+ if (options.regencyCode !== void 0) {
64
+ if (!options.regencyCode.startsWith(provinceCode)) {
65
+ throw new Error(
66
+ `Kode kabupaten/kota "${options.regencyCode}" tidak sesuai dengan kode provinsi "${provinceCode}"`
67
+ );
68
+ }
69
+ regencyCode = options.regencyCode;
70
+ } else {
71
+ regencyCode = provinceCode + padZero(randomInt(1, 99), 2);
72
+ }
73
+ let districtCode;
74
+ if (options.districtCode !== void 0) {
75
+ if (!options.districtCode.startsWith(regencyCode)) {
76
+ throw new Error(
77
+ `Kode kecamatan "${options.districtCode}" tidak sesuai dengan kode kabupaten/kota "${regencyCode}"`
78
+ );
79
+ }
80
+ districtCode = options.districtCode;
81
+ } else {
82
+ districtCode = regencyCode + padZero(randomInt(1, 99), 2);
83
+ }
84
+ const birth = birthDate ?? randomBirthDate();
85
+ const day = birth.getDate();
86
+ const month = birth.getMonth() + 1;
87
+ const year = birth.getFullYear();
88
+ const selectedGender = gender ?? (Math.random() < 0.5 ? "M" : "F");
89
+ const encodedDay = selectedGender === "F" ? day + 40 : day;
90
+ const ddStr = padZero(encodedDay, 2);
91
+ const mmStr = padZero(month, 2);
92
+ const yyStr = padZero(year % 100, 2);
93
+ const seq = padZero(randomInt(1, 9999), 4);
94
+ return districtCode + ddStr + mmStr + yyStr + seq;
95
+ }
96
+
97
+ // src/utils.ts
98
+ function disambiguateYear(yy) {
99
+ const currentYY = (/* @__PURE__ */ new Date()).getFullYear() % 100;
100
+ return yy > currentYY ? 1900 + yy : 2e3 + yy;
101
+ }
102
+
103
+ // src/validate.ts
104
+ var MIN_PROVINCE_CODE2 = 11;
105
+ var MAX_PROVINCE_CODE2 = 97;
106
+ var NIK_LENGTH = 16;
107
+ var DIGITS_ONLY_PATTERN = /^\d{16}$/;
108
+ function isValidCalendarDate(year, month, day) {
109
+ const date = new Date(year, month - 1, day);
110
+ return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day;
111
+ }
112
+ function validateNIK(nik) {
113
+ if (typeof nik !== "string") {
114
+ return { valid: false, error: "NIK harus berupa string" };
115
+ }
116
+ if (nik.length !== NIK_LENGTH) {
117
+ return { valid: false, error: "NIK harus 16 digit" };
118
+ }
119
+ if (!DIGITS_ONLY_PATTERN.test(nik)) {
120
+ return { valid: false, error: "NIK hanya boleh berisi angka" };
121
+ }
122
+ const provinceCode = Number.parseInt(nik.substring(0, 2), 10);
123
+ if (provinceCode < MIN_PROVINCE_CODE2 || provinceCode > MAX_PROVINCE_CODE2) {
124
+ return { valid: false, error: "Kode provinsi tidak valid" };
125
+ }
126
+ const dd = Number.parseInt(nik.substring(6, 8), 10);
127
+ const mm = Number.parseInt(nik.substring(8, 10), 10);
128
+ const yy = Number.parseInt(nik.substring(10, 12), 10);
129
+ const isValidMaleDD = dd >= 1 && dd <= 31;
130
+ const isValidFemaleDD = dd >= 41 && dd <= 71;
131
+ if (!isValidMaleDD && !isValidFemaleDD) {
132
+ return { valid: false, error: "Tanggal lahir tidak valid" };
133
+ }
134
+ if (mm < 1 || mm > 12) {
135
+ return { valid: false, error: "Tanggal lahir tidak valid" };
136
+ }
137
+ const actualDay = isValidFemaleDD ? dd - 40 : dd;
138
+ const fullYear = disambiguateYear(yy);
139
+ if (!isValidCalendarDate(fullYear, mm, actualDay)) {
140
+ return { valid: false, error: "Tanggal lahir tidak valid" };
141
+ }
142
+ const seq = nik.substring(12, 16);
143
+ if (seq === "0000") {
144
+ return { valid: false, error: "Nomor urut tidak valid" };
145
+ }
146
+ return { valid: true };
147
+ }
148
+
149
+ // src/parse.ts
150
+ function parseNIK(nik) {
151
+ const validation = validateNIK(nik);
152
+ if (!validation.valid) {
153
+ return { valid: false, error: validation.error };
154
+ }
155
+ const provinceCode = nik.substring(0, 2);
156
+ const regencyCode = nik.substring(0, 4);
157
+ const districtCode = nik.substring(0, 6);
158
+ const dd = Number.parseInt(nik.substring(6, 8), 10);
159
+ const mm = Number.parseInt(nik.substring(8, 10), 10);
160
+ const yy = Number.parseInt(nik.substring(10, 12), 10);
161
+ const isFemale = dd > 40;
162
+ const actualDay = isFemale ? dd - 40 : dd;
163
+ const gender = isFemale ? "F" : "M";
164
+ const fullYear = disambiguateYear(yy);
165
+ const birthDate = new Date(fullYear, mm - 1, actualDay);
166
+ const sequenceNumber = nik.substring(12, 16);
167
+ return {
168
+ valid: true,
169
+ nik,
170
+ provinceCode,
171
+ regencyCode,
172
+ districtCode,
173
+ birthDate,
174
+ gender,
175
+ sequenceNumber
176
+ };
177
+ }
178
+ // Annotate the CommonJS export names for ESM import in node:
179
+ 0 && (module.exports = {
180
+ generateNIK,
181
+ parseNIK,
182
+ validateNIK
183
+ });
@@ -0,0 +1,4 @@
1
+ export { generateNIK } from './generate.cjs';
2
+ export { parseNIK } from './parse.cjs';
3
+ export { GenerateOptions, NIKInvalid, NIKResult, NIKValid, ValidationInvalid, ValidationResult, ValidationValid } from './types.cjs';
4
+ export { validateNIK } from './validate.cjs';
@@ -0,0 +1,4 @@
1
+ export { generateNIK } from './generate.js';
2
+ export { parseNIK } from './parse.js';
3
+ export { GenerateOptions, NIKInvalid, NIKResult, NIKValid, ValidationInvalid, ValidationResult, ValidationValid } from './types.js';
4
+ export { validateNIK } from './validate.js';
package/dist/index.js ADDED
@@ -0,0 +1,154 @@
1
+ // src/generate.ts
2
+ var MIN_PROVINCE_CODE = 11;
3
+ var MAX_PROVINCE_CODE = 97;
4
+ var MIN_BIRTH_YEAR = 1950;
5
+ var MAX_BIRTH_YEAR = 2005;
6
+ function randomInt(min, max) {
7
+ return Math.floor(Math.random() * (max - min + 1)) + min;
8
+ }
9
+ function padZero(num, length) {
10
+ return String(num).padStart(length, "0");
11
+ }
12
+ function getDaysInMonth(year, month) {
13
+ return new Date(year, month, 0).getDate();
14
+ }
15
+ function randomBirthDate() {
16
+ const year = randomInt(MIN_BIRTH_YEAR, MAX_BIRTH_YEAR);
17
+ const month = randomInt(1, 12);
18
+ const maxDay = getDaysInMonth(year, month);
19
+ const day = randomInt(1, maxDay);
20
+ return new Date(year, month - 1, day);
21
+ }
22
+ function generateNIK(options = {}) {
23
+ const { gender, birthDate } = options;
24
+ let provinceCode;
25
+ if (options.provinceCode !== void 0) {
26
+ const pc = Number.parseInt(options.provinceCode, 10);
27
+ if (Number.isNaN(pc) || pc < MIN_PROVINCE_CODE || pc > MAX_PROVINCE_CODE) {
28
+ throw new Error("Kode provinsi tidak valid (harus 11-97)");
29
+ }
30
+ provinceCode = padZero(pc, 2);
31
+ } else {
32
+ provinceCode = padZero(randomInt(MIN_PROVINCE_CODE, MAX_PROVINCE_CODE), 2);
33
+ }
34
+ let regencyCode;
35
+ if (options.regencyCode !== void 0) {
36
+ if (!options.regencyCode.startsWith(provinceCode)) {
37
+ throw new Error(
38
+ `Kode kabupaten/kota "${options.regencyCode}" tidak sesuai dengan kode provinsi "${provinceCode}"`
39
+ );
40
+ }
41
+ regencyCode = options.regencyCode;
42
+ } else {
43
+ regencyCode = provinceCode + padZero(randomInt(1, 99), 2);
44
+ }
45
+ let districtCode;
46
+ if (options.districtCode !== void 0) {
47
+ if (!options.districtCode.startsWith(regencyCode)) {
48
+ throw new Error(
49
+ `Kode kecamatan "${options.districtCode}" tidak sesuai dengan kode kabupaten/kota "${regencyCode}"`
50
+ );
51
+ }
52
+ districtCode = options.districtCode;
53
+ } else {
54
+ districtCode = regencyCode + padZero(randomInt(1, 99), 2);
55
+ }
56
+ const birth = birthDate ?? randomBirthDate();
57
+ const day = birth.getDate();
58
+ const month = birth.getMonth() + 1;
59
+ const year = birth.getFullYear();
60
+ const selectedGender = gender ?? (Math.random() < 0.5 ? "M" : "F");
61
+ const encodedDay = selectedGender === "F" ? day + 40 : day;
62
+ const ddStr = padZero(encodedDay, 2);
63
+ const mmStr = padZero(month, 2);
64
+ const yyStr = padZero(year % 100, 2);
65
+ const seq = padZero(randomInt(1, 9999), 4);
66
+ return districtCode + ddStr + mmStr + yyStr + seq;
67
+ }
68
+
69
+ // src/utils.ts
70
+ function disambiguateYear(yy) {
71
+ const currentYY = (/* @__PURE__ */ new Date()).getFullYear() % 100;
72
+ return yy > currentYY ? 1900 + yy : 2e3 + yy;
73
+ }
74
+
75
+ // src/validate.ts
76
+ var MIN_PROVINCE_CODE2 = 11;
77
+ var MAX_PROVINCE_CODE2 = 97;
78
+ var NIK_LENGTH = 16;
79
+ var DIGITS_ONLY_PATTERN = /^\d{16}$/;
80
+ function isValidCalendarDate(year, month, day) {
81
+ const date = new Date(year, month - 1, day);
82
+ return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day;
83
+ }
84
+ function validateNIK(nik) {
85
+ if (typeof nik !== "string") {
86
+ return { valid: false, error: "NIK harus berupa string" };
87
+ }
88
+ if (nik.length !== NIK_LENGTH) {
89
+ return { valid: false, error: "NIK harus 16 digit" };
90
+ }
91
+ if (!DIGITS_ONLY_PATTERN.test(nik)) {
92
+ return { valid: false, error: "NIK hanya boleh berisi angka" };
93
+ }
94
+ const provinceCode = Number.parseInt(nik.substring(0, 2), 10);
95
+ if (provinceCode < MIN_PROVINCE_CODE2 || provinceCode > MAX_PROVINCE_CODE2) {
96
+ return { valid: false, error: "Kode provinsi tidak valid" };
97
+ }
98
+ const dd = Number.parseInt(nik.substring(6, 8), 10);
99
+ const mm = Number.parseInt(nik.substring(8, 10), 10);
100
+ const yy = Number.parseInt(nik.substring(10, 12), 10);
101
+ const isValidMaleDD = dd >= 1 && dd <= 31;
102
+ const isValidFemaleDD = dd >= 41 && dd <= 71;
103
+ if (!isValidMaleDD && !isValidFemaleDD) {
104
+ return { valid: false, error: "Tanggal lahir tidak valid" };
105
+ }
106
+ if (mm < 1 || mm > 12) {
107
+ return { valid: false, error: "Tanggal lahir tidak valid" };
108
+ }
109
+ const actualDay = isValidFemaleDD ? dd - 40 : dd;
110
+ const fullYear = disambiguateYear(yy);
111
+ if (!isValidCalendarDate(fullYear, mm, actualDay)) {
112
+ return { valid: false, error: "Tanggal lahir tidak valid" };
113
+ }
114
+ const seq = nik.substring(12, 16);
115
+ if (seq === "0000") {
116
+ return { valid: false, error: "Nomor urut tidak valid" };
117
+ }
118
+ return { valid: true };
119
+ }
120
+
121
+ // src/parse.ts
122
+ function parseNIK(nik) {
123
+ const validation = validateNIK(nik);
124
+ if (!validation.valid) {
125
+ return { valid: false, error: validation.error };
126
+ }
127
+ const provinceCode = nik.substring(0, 2);
128
+ const regencyCode = nik.substring(0, 4);
129
+ const districtCode = nik.substring(0, 6);
130
+ const dd = Number.parseInt(nik.substring(6, 8), 10);
131
+ const mm = Number.parseInt(nik.substring(8, 10), 10);
132
+ const yy = Number.parseInt(nik.substring(10, 12), 10);
133
+ const isFemale = dd > 40;
134
+ const actualDay = isFemale ? dd - 40 : dd;
135
+ const gender = isFemale ? "F" : "M";
136
+ const fullYear = disambiguateYear(yy);
137
+ const birthDate = new Date(fullYear, mm - 1, actualDay);
138
+ const sequenceNumber = nik.substring(12, 16);
139
+ return {
140
+ valid: true,
141
+ nik,
142
+ provinceCode,
143
+ regencyCode,
144
+ districtCode,
145
+ birthDate,
146
+ gender,
147
+ sequenceNumber
148
+ };
149
+ }
150
+ export {
151
+ generateNIK,
152
+ parseNIK,
153
+ validateNIK
154
+ };
package/dist/parse.cjs ADDED
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/parse.ts
21
+ var parse_exports = {};
22
+ __export(parse_exports, {
23
+ parseNIK: () => parseNIK
24
+ });
25
+ module.exports = __toCommonJS(parse_exports);
26
+
27
+ // src/utils.ts
28
+ function disambiguateYear(yy) {
29
+ const currentYY = (/* @__PURE__ */ new Date()).getFullYear() % 100;
30
+ return yy > currentYY ? 1900 + yy : 2e3 + yy;
31
+ }
32
+
33
+ // src/validate.ts
34
+ var MIN_PROVINCE_CODE = 11;
35
+ var MAX_PROVINCE_CODE = 97;
36
+ var NIK_LENGTH = 16;
37
+ var DIGITS_ONLY_PATTERN = /^\d{16}$/;
38
+ function isValidCalendarDate(year, month, day) {
39
+ const date = new Date(year, month - 1, day);
40
+ return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day;
41
+ }
42
+ function validateNIK(nik) {
43
+ if (typeof nik !== "string") {
44
+ return { valid: false, error: "NIK harus berupa string" };
45
+ }
46
+ if (nik.length !== NIK_LENGTH) {
47
+ return { valid: false, error: "NIK harus 16 digit" };
48
+ }
49
+ if (!DIGITS_ONLY_PATTERN.test(nik)) {
50
+ return { valid: false, error: "NIK hanya boleh berisi angka" };
51
+ }
52
+ const provinceCode = Number.parseInt(nik.substring(0, 2), 10);
53
+ if (provinceCode < MIN_PROVINCE_CODE || provinceCode > MAX_PROVINCE_CODE) {
54
+ return { valid: false, error: "Kode provinsi tidak valid" };
55
+ }
56
+ const dd = Number.parseInt(nik.substring(6, 8), 10);
57
+ const mm = Number.parseInt(nik.substring(8, 10), 10);
58
+ const yy = Number.parseInt(nik.substring(10, 12), 10);
59
+ const isValidMaleDD = dd >= 1 && dd <= 31;
60
+ const isValidFemaleDD = dd >= 41 && dd <= 71;
61
+ if (!isValidMaleDD && !isValidFemaleDD) {
62
+ return { valid: false, error: "Tanggal lahir tidak valid" };
63
+ }
64
+ if (mm < 1 || mm > 12) {
65
+ return { valid: false, error: "Tanggal lahir tidak valid" };
66
+ }
67
+ const actualDay = isValidFemaleDD ? dd - 40 : dd;
68
+ const fullYear = disambiguateYear(yy);
69
+ if (!isValidCalendarDate(fullYear, mm, actualDay)) {
70
+ return { valid: false, error: "Tanggal lahir tidak valid" };
71
+ }
72
+ const seq = nik.substring(12, 16);
73
+ if (seq === "0000") {
74
+ return { valid: false, error: "Nomor urut tidak valid" };
75
+ }
76
+ return { valid: true };
77
+ }
78
+
79
+ // src/parse.ts
80
+ function parseNIK(nik) {
81
+ const validation = validateNIK(nik);
82
+ if (!validation.valid) {
83
+ return { valid: false, error: validation.error };
84
+ }
85
+ const provinceCode = nik.substring(0, 2);
86
+ const regencyCode = nik.substring(0, 4);
87
+ const districtCode = nik.substring(0, 6);
88
+ const dd = Number.parseInt(nik.substring(6, 8), 10);
89
+ const mm = Number.parseInt(nik.substring(8, 10), 10);
90
+ const yy = Number.parseInt(nik.substring(10, 12), 10);
91
+ const isFemale = dd > 40;
92
+ const actualDay = isFemale ? dd - 40 : dd;
93
+ const gender = isFemale ? "F" : "M";
94
+ const fullYear = disambiguateYear(yy);
95
+ const birthDate = new Date(fullYear, mm - 1, actualDay);
96
+ const sequenceNumber = nik.substring(12, 16);
97
+ return {
98
+ valid: true,
99
+ nik,
100
+ provinceCode,
101
+ regencyCode,
102
+ districtCode,
103
+ birthDate,
104
+ gender,
105
+ sequenceNumber
106
+ };
107
+ }
108
+ // Annotate the CommonJS export names for ESM import in node:
109
+ 0 && (module.exports = {
110
+ parseNIK
111
+ });
@@ -0,0 +1,81 @@
1
+ import { NIKResult } from './types.cjs';
2
+
3
+ /**
4
+ * Modul parsing NIK (Nomor Induk Kependudukan).
5
+ *
6
+ * Menyediakan fungsi {@link parseNIK} yang memvalidasi dan mengekstrak
7
+ * semua komponen dari NIK: kode wilayah (provinsi, kabupaten/kota, kecamatan),
8
+ * tanggal lahir, jenis kelamin, dan nomor urut registrasi.
9
+ *
10
+ * Kode wilayah yang dikembalikan menggunakan format **Kemendagri** (bukan BPS).
11
+ * Untuk resolve ke nama wilayah, gunakan package `kode-wilayah-id` dengan
12
+ * fungsi `getProvinceByKemendagriCode()`, `getRegencyByKemendagriCode()`, dll.
13
+ *
14
+ * @module parse
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { parseNIK } from 'nik-id/parse';
19
+ *
20
+ * const result = parseNIK("3204076508850001");
21
+ * if (result.valid) {
22
+ * console.log(result.provinceCode); // "32"
23
+ * console.log(result.regencyCode); // "3204"
24
+ * console.log(result.districtCode); // "320407"
25
+ * console.log(result.gender); // "F"
26
+ * console.log(result.birthDate); // Date: 1985-08-25
27
+ * console.log(result.sequenceNumber); // "0001"
28
+ * }
29
+ * ```
30
+ */
31
+
32
+ /**
33
+ * Parse NIK menjadi komponen-komponennya.
34
+ *
35
+ * Fungsi ini pertama memvalidasi NIK menggunakan {@link validateNIK},
36
+ * kemudian mengekstrak setiap komponen ke dalam object {@link NIKResult}.
37
+ *
38
+ * Komponen yang diekstrak:
39
+ * - **provinceCode** — kode provinsi Kemendagri (2 digit)
40
+ * - **regencyCode** — kode kabupaten/kota Kemendagri (4 digit)
41
+ * - **districtCode** — kode kecamatan Kemendagri (6 digit)
42
+ * - **birthDate** — tanggal lahir sebagai `Date` object
43
+ * - **gender** — jenis kelamin (`"M"` atau `"F"`)
44
+ * - **sequenceNumber** — nomor urut registrasi (4 digit)
45
+ *
46
+ * @param nik - String NIK 16 digit yang akan di-parse
47
+ * @returns {@link NIKResult} — discriminated union yang bisa di-narrow
48
+ * menggunakan `result.valid`
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * const result = parseNIK("3204076508850001");
53
+ * if (result.valid) {
54
+ * // result bertipe NIKValid — semua field tersedia
55
+ * console.log(result.provinceCode); // "32"
56
+ * console.log(result.regencyCode); // "3204"
57
+ * console.log(result.districtCode); // "320407"
58
+ * console.log(result.birthDate); // Date: 1985-08-25
59
+ * console.log(result.gender); // "F"
60
+ * console.log(result.sequenceNumber); // "0001"
61
+ * } else {
62
+ * // result bertipe NIKInvalid — ada error
63
+ * console.log(result.error); // "NIK harus 16 digit"
64
+ * }
65
+ * ```
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * // Integrasi dengan kode-wilayah-id
70
+ * import { getProvinceByKemendagriCode } from 'kode-wilayah-id';
71
+ *
72
+ * const result = parseNIK("3204076508850001");
73
+ * if (result.valid) {
74
+ * const prov = getProvinceByKemendagriCode(result.provinceCode);
75
+ * console.log(prov?.name); // "JAWA BARAT"
76
+ * }
77
+ * ```
78
+ */
79
+ declare function parseNIK(nik: string): NIKResult;
80
+
81
+ export { parseNIK };