tw-id-validator 1.0.5

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/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # tw-id-validator
2
+
3
+ > 台灣證件驗證函式庫。支援身分證、統一證號、統一編號、自然人憑證、電子發票條碼。
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install tw-id-validator
9
+ ```
10
+
11
+ Zero runtime dependencies. Pure TypeScript.
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import {
17
+ isValidNationalId,
18
+ parseNationalId,
19
+ generateNationalId,
20
+ isValidUnifiedCert,
21
+ isValidBAN,
22
+ isValidCDC,
23
+ isValidMobileBarcode,
24
+ isValidDonateCode,
25
+ } from 'tw-id-validator'
26
+ ```
27
+
28
+ ## 身分證字號 (National ID)
29
+
30
+ ```typescript
31
+ // 驗證
32
+ isValidNationalId('A123456789') // → true
33
+ isValidNationalId('A123456788') // → false
34
+
35
+ // 解析
36
+ parseNationalId('A123456789')
37
+ // → {
38
+ // city: '台北市',
39
+ // cityCode: 'A',
40
+ // gender: 'male',
41
+ // sequence: '2345678',
42
+ // checkDigit: '9'
43
+ // }
44
+
45
+ // 產生測試用身分證
46
+ generateNationalId('A', 'male') // → 'A1xxxxxxx' (valid)
47
+ generateNationalId('B', 'female') // → 'B2xxxxxxx' (valid)
48
+ ```
49
+
50
+ ## 統一證號 (Unified Certificate)
51
+
52
+ ```typescript
53
+ // 驗證(支援新舊版)
54
+ isValidUnifiedCert('AB23456789') // → true (舊版)
55
+ isValidUnifiedCert('A800000014') // → true (新版,外籍)
56
+ isValidUnifiedCert('A900000007') // → true (新版,大陸/港澳)
57
+
58
+ // 解析
59
+ parseUnifiedCert('AB23456789')
60
+ // → { version: 'old', cityCode: 'A', category: 'B', sequence: '2345678', checkDigit: '9' }
61
+
62
+ parseUnifiedCert('A800000014')
63
+ // → { version: 'new', cityCode: 'A', category: '外籍/無國籍', sequence: '0000001', checkDigit: '4' }
64
+ ```
65
+
66
+ ## 統一編號 (Business Administration Number)
67
+
68
+ ```typescript
69
+ isValidBAN('12345675') // → true
70
+ isValidBAN('12345678') // → false
71
+
72
+ parseBAN('12345675')
73
+ // → { number: '12345675', hasSpecialRule: true }
74
+ ```
75
+
76
+ ## 自然人憑證 (Citizen Digital Certificate)
77
+
78
+ ```typescript
79
+ isValidCDC('AB12345678901234') // → true (2 letters + 14 digits)
80
+ isValidCDC('A12345678901234') // → false
81
+
82
+ parseCDC('AB12345678901234')
83
+ // → { prefix: 'AB', serial: '12345678901234' }
84
+ ```
85
+
86
+ ## 電子發票 (E-Invoice)
87
+
88
+ ### 手機條碼
89
+
90
+ ```typescript
91
+ isValidMobileBarcode('/+.-++..') // → true
92
+ isValidMobileBarcode('/1234567') // → true
93
+ isValidMobileBarcode('/ABCDEFG') // → true
94
+
95
+ parseMobileBarcode('/+.-++..')
96
+ // → { barcode: '/+.-++..' }
97
+ ```
98
+
99
+ ### 捐贈碼
100
+
101
+ ```typescript
102
+ isValidDonateCode('123') // → true (3-7 digits)
103
+ isValidDonateCode('1234567') // → true
104
+ isValidDonateCode(123) // → true (number input)
105
+
106
+ parseDonateCode('123')
107
+ // → { code: '123' }
108
+ ```
109
+
110
+ ## API Reference
111
+
112
+ | Function | Input | Output | Description |
113
+ |----------|-------|--------|-------------|
114
+ | `isValidNationalId(id)` | `string` | `boolean` | 驗證身分證字號 |
115
+ | `parseNationalId(id)` | `string` | `IdCardInfo \| null` | 解析身分證 |
116
+ | `generateNationalId(city, gender)` | `string, string` | `string \| null` | 產生測試用身分證 |
117
+ | `isValidUnifiedCert(id)` | `string` | `boolean` | 驗證統一證號(新舊版) |
118
+ | `parseUnifiedCert(id)` | `string` | `UnifiedCertInfo \| null` | 解析統一證號 |
119
+ | `isValidBAN(id)` | `string \| number` | `boolean` | 驗證統一編號 |
120
+ | `parseBAN(id)` | `string \| number` | `BANInfo \| null` | 解析統一編號 |
121
+ | `isValidCDC(id)` | `string` | `boolean` | 驗證自然人憑證 |
122
+ | `parseCDC(id)` | `string` | `CDCInfo \| null` | 解析自然人憑證 |
123
+ | `isValidMobileBarcode(code)` | `string` | `boolean` | 驗證發票手機條碼 |
124
+ | `parseMobileBarcode(code)` | `string` | `MobileBarcodeInfo \| null` | 解析手機條碼 |
125
+ | `isValidDonateCode(code)` | `string \| number` | `boolean` | 驗證發票捐贈碼 |
126
+ | `parseDonateCode(code)` | `string \| number` | `DonateCodeInfo \| null` | 解析捐贈碼 |
127
+
128
+ ## Design
129
+
130
+ | Feature | Why |
131
+ |---------|-----|
132
+ | Zero runtime deps | Pure TypeScript, no external packages |
133
+ | Flat API | Each file handles one document type |
134
+ | Pure functions | No side effects, independently testable |
135
+ | TypeScript strict | Full type safety, no `any` |
136
+ | 105 tests | Comprehensive coverage with ✅/❌ output |
137
+
138
+ ## License
139
+
140
+ MIT
package/dist/ban.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Taiwan Business Administration Number (營利事業統一編號) validation.
3
+ *
4
+ * Format: 8 digits
5
+ * Checksum: weighted [1,2,1,2,1,2,4,1] → sum of digit products → mod 5 === 0
6
+ * Special rule: if 7th digit is 7 and (sum+1) % 5 === 0, also valid
7
+ */
8
+ /**
9
+ * Validate a Taiwan Business Administration Number (統一編號).
10
+ * Supports both old (mod 10) and new (mod 5) rules.
11
+ */
12
+ export declare function isValidBAN(id: string | number): boolean;
13
+ export interface BANInfo {
14
+ number: string;
15
+ hasSpecialRule: boolean;
16
+ }
17
+ /**
18
+ * Parse a BAN number.
19
+ * Returns null if invalid.
20
+ */
21
+ export declare function parseBAN(id: string | number): BANInfo | null;
package/dist/ban.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Taiwan Business Administration Number (營利事業統一編號) validation.
3
+ *
4
+ * Format: 8 digits
5
+ * Checksum: weighted [1,2,1,2,1,2,4,1] → sum of digit products → mod 5 === 0
6
+ * Special rule: if 7th digit is 7 and (sum+1) % 5 === 0, also valid
7
+ */
8
+ const BAN_COEFFICIENTS = [1, 2, 1, 2, 1, 2, 4, 1];
9
+ const BAN_RE = /^\d{8}$/;
10
+ /**
11
+ * Validate a Taiwan Business Administration Number (統一編號).
12
+ * Supports both old (mod 10) and new (mod 5) rules.
13
+ */
14
+ export function isValidBAN(id) {
15
+ const str = String(id);
16
+ if (!BAN_RE.test(str))
17
+ return false;
18
+ const digits = str.split('').map(Number);
19
+ const sum = digits.reduce((acc, d, i) => {
20
+ const product = d * BAN_COEFFICIENTS[i];
21
+ return acc + Math.floor(product / 10) + (product % 10);
22
+ }, 0);
23
+ // New rule: mod 5 === 0
24
+ if (sum % 5 === 0)
25
+ return true;
26
+ // Special case: 7th digit is 7 and (sum+1) % 5 === 0
27
+ if (digits[6] === 7 && (sum + 1) % 5 === 0)
28
+ return true;
29
+ return false;
30
+ }
31
+ /**
32
+ * Parse a BAN number.
33
+ * Returns null if invalid.
34
+ */
35
+ export function parseBAN(id) {
36
+ const str = String(id);
37
+ if (!isValidBAN(str))
38
+ return null;
39
+ const digits = str.split('').map(Number);
40
+ const sum = digits.reduce((acc, d, i) => {
41
+ const product = d * BAN_COEFFICIENTS[i];
42
+ return acc + Math.floor(product / 10) + (product % 10);
43
+ }, 0);
44
+ return {
45
+ number: str,
46
+ hasSpecialRule: digits[6] === 7 && (sum + 1) % 5 === 0,
47
+ };
48
+ }
package/dist/cdc.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Taiwan Citizen Digital Certificate Number (自然人憑證) validation.
3
+ *
4
+ * Format: 2 letters + 14 digits (16 chars total)
5
+ */
6
+ /**
7
+ * Validate a Taiwan Citizen Digital Certificate Number (自然人憑證字號).
8
+ */
9
+ export declare function isValidCDC(id: string): boolean;
10
+ export interface CDCInfo {
11
+ prefix: string;
12
+ serial: string;
13
+ }
14
+ /**
15
+ * Parse a CDC number.
16
+ * Returns null if invalid.
17
+ */
18
+ export declare function parseCDC(id: string): CDCInfo | null;
package/dist/cdc.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Taiwan Citizen Digital Certificate Number (自然人憑證) validation.
3
+ *
4
+ * Format: 2 letters + 14 digits (16 chars total)
5
+ */
6
+ const CDC_RE = /^[A-Z]{2}\d{14}$/;
7
+ /**
8
+ * Validate a Taiwan Citizen Digital Certificate Number (自然人憑證字號).
9
+ */
10
+ export function isValidCDC(id) {
11
+ return CDC_RE.test(id);
12
+ }
13
+ /**
14
+ * Parse a CDC number.
15
+ * Returns null if invalid.
16
+ */
17
+ export function parseCDC(id) {
18
+ if (!isValidCDC(id))
19
+ return null;
20
+ return {
21
+ prefix: id.slice(0, 2),
22
+ serial: id.slice(2),
23
+ };
24
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Taiwan National ID (身分證字號) and Unified Certificate (統一證號) validation.
3
+ *
4
+ * National ID format: 1 letter + 1 digit (1/2) + 8 digits
5
+ * Unified Certificate (old): 2 letters (2nd A-D) + 8 digits
6
+ * Unified Certificate (new): 1 letter + 8/9 + 7 digits + 1 check digit
7
+ */
8
+ /**
9
+ * Validate a Taiwan national ID number (身分證字號).
10
+ * Format: 1 letter + 1 digit (1 or 2) + 8 digits
11
+ */
12
+ export declare function isValidNationalId(id: string): boolean;
13
+ export interface IdCardInfo {
14
+ city: string;
15
+ cityCode: string;
16
+ gender: 'male' | 'female';
17
+ sequence: string;
18
+ checkDigit: string;
19
+ }
20
+ /**
21
+ * Parse a national ID number into its components.
22
+ * Returns null if invalid.
23
+ */
24
+ export declare function parseNationalId(id: string): IdCardInfo | null;
25
+ /**
26
+ * Generate a test national ID number.
27
+ * @param city - City code (A-Z), defaults to 'A' (Taipei)
28
+ * @param gender - 'male' (1) or 'female' (2), defaults to 'male'
29
+ */
30
+ export declare function generateNationalId(city?: string, gender?: 'male' | 'female'): string | null;
31
+ /**
32
+ * Validate a Taiwan unified certificate number (統一證號).
33
+ * Supports both old and new formats.
34
+ */
35
+ export declare function isValidUnifiedCert(id: string): boolean;
36
+ export interface UnifiedCertInfo {
37
+ version: 'old' | 'new';
38
+ cityCode: string;
39
+ category: string;
40
+ sequence: string;
41
+ checkDigit: string;
42
+ }
43
+ /**
44
+ * Parse a unified certificate number.
45
+ * Returns null if invalid.
46
+ */
47
+ export declare function parseUnifiedCert(id: string): UnifiedCertInfo | null;
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Taiwan National ID (身分證字號) and Unified Certificate (統一證號) validation.
3
+ *
4
+ * National ID format: 1 letter + 1 digit (1/2) + 8 digits
5
+ * Unified Certificate (old): 2 letters (2nd A-D) + 8 digits
6
+ * Unified Certificate (new): 1 letter + 8/9 + 7 digits + 1 check digit
7
+ */
8
+ // ============================================================
9
+ // Constants
10
+ // ============================================================
11
+ /**
12
+ * City/locale code mapping for national ID first letter.
13
+ * Each value = tens * 1 + ones * 9 (pre-computed for checksum).
14
+ */
15
+ const CITY_CODES = {
16
+ A: 1, B: 10, C: 19, D: 28, E: 37, F: 46, G: 55, H: 64,
17
+ I: 39, J: 73, K: 82, L: 2, M: 11, N: 22, O: 48, P: 29,
18
+ Q: 38, R: 47, S: 56, T: 65, U: 74, V: 83, W: 21, X: 3,
19
+ Y: 12, Z: 30,
20
+ };
21
+ /** City names for parsing. */
22
+ const CITY_NAMES = {
23
+ A: '台北市', B: '台中市', C: '基隆市', D: '台南市', E: '高雄市',
24
+ F: '台北縣', G: '宜蘭縣', H: '桃園縣', I: '嘉義市', J: '新竹縣',
25
+ K: '苗栗縣', L: '台中縣', M: '南投縣', N: '彰化縣', O: '新竹市',
26
+ P: '雲林縣', Q: '嘉義縣', R: '台南縣', S: '高雄縣', T: '屏東縣',
27
+ U: '花蓮縣', V: '台東縣', W: '金門縣', X: '澎湖縣', Y: '陽明山',
28
+ Z: '連江縣',
29
+ };
30
+ /**
31
+ * Second letter mapping for old-format unified certificate.
32
+ * Pre-computed same way as CITY_CODES.
33
+ */
34
+ const CERT_SECOND_LETTER = {
35
+ A: 0, B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7,
36
+ I: 4, J: 8, K: 9, L: 0, M: 1, N: 2, O: 5, P: 3,
37
+ Q: 4, R: 5, S: 6, T: 7, U: 8, V: 9, W: 2, X: 0,
38
+ Y: 1, Z: 3,
39
+ };
40
+ /** Checksum weights: [first, 8 digits, checkDigit] */
41
+ const CHECKSUM_WEIGHTS = [1, 8, 7, 6, 5, 4, 3, 2, 1, 1];
42
+ // ============================================================
43
+ // Core checksum algorithm (shared by national ID and unified cert)
44
+ // ============================================================
45
+ /**
46
+ * Compute the checksum for a Taiwan ID string.
47
+ * Works for both national ID and unified certificate.
48
+ */
49
+ function computeChecksum(id) {
50
+ const firstCode = CITY_CODES[id[0]];
51
+ const secondChar = id[1];
52
+ // Second char: if letter (old unified cert), use CERT_SECOND_LETTER; if digit, parse it
53
+ const secondCode = isNaN(Number(secondChar))
54
+ ? CERT_SECOND_LETTER[secondChar]
55
+ : Number(secondChar);
56
+ const digits = [firstCode, secondCode, ...id.slice(2).split('').map(Number)];
57
+ return digits.reduce((sum, d, i) => sum + d * CHECKSUM_WEIGHTS[i], 0);
58
+ }
59
+ // ============================================================
60
+ // National ID (身分證字號)
61
+ // ============================================================
62
+ /** National ID regex: 1 letter + 1/2 + 8 digits */
63
+ const NATIONAL_ID_RE = /^[A-Z][12]\d{8}$/;
64
+ /**
65
+ * Validate a Taiwan national ID number (身分證字號).
66
+ * Format: 1 letter + 1 digit (1 or 2) + 8 digits
67
+ */
68
+ export function isValidNationalId(id) {
69
+ if (!NATIONAL_ID_RE.test(id))
70
+ return false;
71
+ return computeChecksum(id) % 10 === 0;
72
+ }
73
+ /**
74
+ * Parse a national ID number into its components.
75
+ * Returns null if invalid.
76
+ */
77
+ export function parseNationalId(id) {
78
+ if (!isValidNationalId(id))
79
+ return null;
80
+ return {
81
+ city: CITY_NAMES[id[0]] ?? 'Unknown',
82
+ cityCode: id[0],
83
+ gender: id[1] === '1' ? 'male' : 'female',
84
+ sequence: id.slice(2, 9),
85
+ checkDigit: id[9],
86
+ };
87
+ }
88
+ /**
89
+ * Generate a test national ID number.
90
+ * @param city - City code (A-Z), defaults to 'A' (Taipei)
91
+ * @param gender - 'male' (1) or 'female' (2), defaults to 'male'
92
+ */
93
+ export function generateNationalId(city = 'A', gender = 'male') {
94
+ if (!CITY_CODES[city])
95
+ return null;
96
+ const genderDigit = gender === 'male' ? '1' : '2';
97
+ const seq = String(Math.floor(Math.random() * 10000000)).padStart(7, '0');
98
+ const partial = city + genderDigit + seq;
99
+ // Find check digit (0-9) that makes checksum % 10 === 0
100
+ const firstCode = CITY_CODES[city];
101
+ const secondCode = Number(genderDigit);
102
+ const seqDigits = seq.split('').map(Number);
103
+ const baseSum = firstCode * 1 + secondCode * 8 +
104
+ seqDigits.reduce((sum, d, i) => sum + d * CHECKSUM_WEIGHTS[i + 2], 0);
105
+ const checkDigit = (10 - (baseSum % 10)) % 10;
106
+ return partial + checkDigit;
107
+ }
108
+ // ============================================================
109
+ // Unified Certificate (統一證號)
110
+ // ============================================================
111
+ /** Old format: 2 letters (2nd A-D) + 8 digits */
112
+ const UNIFIED_CERT_OLD_RE = /^[A-Z][A-D]\d{8}$/;
113
+ /** New format: 1 letter + 8/9 + 8 digits (total 10 chars) */
114
+ const UNIFIED_CERT_NEW_RE = /^[A-Z][89]\d{8}$/;
115
+ /**
116
+ * Validate a Taiwan unified certificate number (統一證號).
117
+ * Supports both old and new formats.
118
+ */
119
+ export function isValidUnifiedCert(id) {
120
+ if (UNIFIED_CERT_OLD_RE.test(id)) {
121
+ return computeChecksum(id) % 10 === 0;
122
+ }
123
+ if (UNIFIED_CERT_NEW_RE.test(id)) {
124
+ return computeChecksum(id) % 10 === 0;
125
+ }
126
+ return false;
127
+ }
128
+ /**
129
+ * Parse a unified certificate number.
130
+ * Returns null if invalid.
131
+ */
132
+ export function parseUnifiedCert(id) {
133
+ if (!isValidUnifiedCert(id))
134
+ return null;
135
+ if (UNIFIED_CERT_OLD_RE.test(id)) {
136
+ return {
137
+ version: 'old',
138
+ cityCode: id[0],
139
+ category: id[1],
140
+ sequence: id.slice(2, 9),
141
+ checkDigit: id[9],
142
+ };
143
+ }
144
+ const categoryMap = {
145
+ '8': '外籍/無國籍',
146
+ '9': '大陸/港澳/無戶籍',
147
+ };
148
+ return {
149
+ version: 'new',
150
+ cityCode: id[0],
151
+ category: categoryMap[id[1]] ?? 'Unknown',
152
+ sequence: id.slice(2, 9),
153
+ checkDigit: id[9],
154
+ };
155
+ }
@@ -0,0 +1,4 @@
1
+ export { isValidNationalId, parseNationalId, generateNationalId, isValidUnifiedCert, parseUnifiedCert, type IdCardInfo, type UnifiedCertInfo, } from './id-card.js';
2
+ export { isValidBAN, parseBAN, type BANInfo, } from './ban.js';
3
+ export { isValidCDC, parseCDC, type CDCInfo, } from './cdc.js';
4
+ export { isValidMobileBarcode, parseMobileBarcode, isValidDonateCode, parseDonateCode, type MobileBarcodeInfo, type DonateCodeInfo, } from './invoice.js';
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ // National ID + Unified Certificate
2
+ export { isValidNationalId, parseNationalId, generateNationalId, isValidUnifiedCert, parseUnifiedCert, } from './id-card.js';
3
+ // Business Administration Number
4
+ export { isValidBAN, parseBAN, } from './ban.js';
5
+ // Citizen Digital Certificate
6
+ export { isValidCDC, parseCDC, } from './cdc.js';
7
+ // E-Invoice
8
+ export { isValidMobileBarcode, parseMobileBarcode, isValidDonateCode, parseDonateCode, } from './invoice.js';
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Taiwan E-Invoice codes validation.
3
+ *
4
+ * Mobile Barcode (手機條碼): / + 7 chars [0-9A-Z.\-+]
5
+ * Donate Code (捐贈碼): 3-7 digits
6
+ */
7
+ /**
8
+ * Validate a Taiwan E-Invoice Mobile Barcode (電子發票手機條碼).
9
+ */
10
+ export declare function isValidMobileBarcode(code: string): boolean;
11
+ export interface MobileBarcodeInfo {
12
+ barcode: string;
13
+ }
14
+ /**
15
+ * Parse a mobile barcode.
16
+ * Returns null if invalid.
17
+ */
18
+ export declare function parseMobileBarcode(code: string): MobileBarcodeInfo | null;
19
+ /**
20
+ * Validate a Taiwan E-Invoice Donate Code (電子發票捐贈碼).
21
+ * 3-7 digits.
22
+ */
23
+ export declare function isValidDonateCode(code: string | number): boolean;
24
+ export interface DonateCodeInfo {
25
+ code: string;
26
+ }
27
+ /**
28
+ * Parse a donate code.
29
+ * Returns null if invalid.
30
+ */
31
+ export declare function parseDonateCode(code: string | number): DonateCodeInfo | null;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Taiwan E-Invoice codes validation.
3
+ *
4
+ * Mobile Barcode (手機條碼): / + 7 chars [0-9A-Z.\-+]
5
+ * Donate Code (捐贈碼): 3-7 digits
6
+ */
7
+ const MOBILE_BARCODE_RE = /^\/[\dA-Z.\-+]{7}$/;
8
+ const DONATE_CODE_RE = /^\d{3,7}$/;
9
+ /**
10
+ * Validate a Taiwan E-Invoice Mobile Barcode (電子發票手機條碼).
11
+ */
12
+ export function isValidMobileBarcode(code) {
13
+ return MOBILE_BARCODE_RE.test(code);
14
+ }
15
+ /**
16
+ * Parse a mobile barcode.
17
+ * Returns null if invalid.
18
+ */
19
+ export function parseMobileBarcode(code) {
20
+ if (!isValidMobileBarcode(code))
21
+ return null;
22
+ return { barcode: code };
23
+ }
24
+ /**
25
+ * Validate a Taiwan E-Invoice Donate Code (電子發票捐贈碼).
26
+ * 3-7 digits.
27
+ */
28
+ export function isValidDonateCode(code) {
29
+ return DONATE_CODE_RE.test(String(code));
30
+ }
31
+ /**
32
+ * Parse a donate code.
33
+ * Returns null if invalid.
34
+ */
35
+ export function parseDonateCode(code) {
36
+ const str = String(code);
37
+ if (!isValidDonateCode(str))
38
+ return null;
39
+ return { code: str };
40
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "tw-id-validator",
3
+ "version": "1.0.5",
4
+ "description": "Taiwan ID validator. Supports national ID, unified certificate, BAN, citizen digital certificate, and e-invoice codes.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist/*.js",
16
+ "dist/*.d.ts",
17
+ "!dist/test*"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "typecheck": "tsc --noEmit",
22
+ "test": "npm run build && node dist/test-runner.js",
23
+ "clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
24
+ "security:audit": "node scripts/security-audit.mjs",
25
+ "release": "node scripts/release.mjs",
26
+ "release:dry": "node scripts/release.mjs --dry",
27
+ "release:patch": "node scripts/release.mjs patch",
28
+ "release:minor": "node scripts/release.mjs minor",
29
+ "prepublishOnly": "npm run typecheck && npm run build"
30
+ },
31
+ "keywords": [
32
+ "taiwan",
33
+ "id-validator",
34
+ "national-id",
35
+ "unified-certificate",
36
+ "ban",
37
+ "citizen-digital-certificate",
38
+ "e-invoice",
39
+ "身分證",
40
+ "統一證號",
41
+ "統一編號",
42
+ "自然人憑證",
43
+ "發票"
44
+ ],
45
+ "author": "",
46
+ "license": "MIT",
47
+ "repository": {
48
+ "type": "git",
49
+ "url": ""
50
+ },
51
+ "engines": {
52
+ "node": ">=18.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^22.0.0",
56
+ "typescript": "^5.7.2"
57
+ }
58
+ }