taiwan-validator 1.1.0 → 1.2.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.
- package/README.en.md +2 -1
- package/README.md +2 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -2
- package/dist/index.d.ts +28 -2
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -8
- package/src/index.ts +21 -6
- package/src/types.ts +22 -0
- package/src/validators/landline-phone.test.ts +34 -0
- package/src/validators/landline-phone.ts +64 -0
- package/src/validators/national-id.test.ts +34 -61
- package/src/validators/national-id.ts +42 -115
- package/src/validators/nhi-card.test.ts +21 -0
- package/src/validators/nhi-card.ts +39 -0
- package/src/validators/passport.test.ts +20 -0
- package/src/validators/passport.ts +38 -0
- package/src/validators/postal-code.test.ts +31 -0
- package/src/validators/postal-code.ts +41 -0
- package/src/validators/resident-certificate.test.ts +60 -65
- package/src/validators/resident-certificate.ts +98 -68
- package/src/validators/shared.ts +63 -0
- package/src/validators/uniform-invoice.test.ts +24 -0
- package/src/validators/uniform-invoice.ts +39 -0
package/README.en.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Taiwan Validator
|
|
2
2
|
|
|
3
|
-
A comprehensive TypeScript validator for Taiwan identification numbers and
|
|
3
|
+
A comprehensive TypeScript validator for Taiwan, supporting identification numbers, business codes, license plates, and more.
|
|
4
4
|
|
|
5
5
|
[](https://github.com/imgarylai/taiwan-validator/actions/workflows/test.yml)
|
|
6
|
+
[](https://codecov.io/gh/imgarylai/taiwan-validator)
|
|
6
7
|
[](https://opensource.org/licenses/MIT)
|
|
7
8
|
|
|
8
9
|
[繁體中文](README.md)
|
package/README.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Taiwan Validator
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
一個完整的台灣身分驗證 TypeScript 套件,支援身分證件、營業代碼、車牌號碼等多種格式驗證。
|
|
4
4
|
|
|
5
5
|
[](https://github.com/imgarylai/taiwan-validator/actions/workflows/test.yml)
|
|
6
|
+
[](https://codecov.io/gh/imgarylai/taiwan-validator)
|
|
6
7
|
[](https://opensource.org/licenses/MIT)
|
|
7
8
|
|
|
8
9
|
[English](README.en.md)
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var d={A:10,B:11,C:12,D:13,E:14,F:15,G:16,H:17,I:34,J:18,K:19,L:20,M:21,N:22,O:35,P:23,Q:24,R:25,S:26,T:27,U:28,V:29,W:32,X:30,Y:31,Z:33},p={A:"\u81FA\u5317\u5E02",B:"\u81FA\u4E2D\u5E02",C:"\u57FA\u9686\u5E02",D:"\u81FA\u5357\u5E02",E:"\u9AD8\u96C4\u5E02",F:"\u65B0\u5317\u5E02",G:"\u5B9C\u862D\u7E23",H:"\u6843\u5712\u5E02",I:"\u5609\u7FA9\u5E02",J:"\u65B0\u7AF9\u7E23",K:"\u82D7\u6817\u7E23",L:"\u81FA\u4E2D\u7E23",M:"\u5357\u6295\u7E23",N:"\u5F70\u5316\u7E23",O:"\u65B0\u7AF9\u5E02",P:"\u96F2\u6797\u7E23",Q:"\u5609\u7FA9\u7E23",R:"\u81FA\u5357\u7E23",S:"\u9AD8\u96C4\u7E23",T:"\u5C4F\u6771\u7E23",U:"\u82B1\u84EE\u7E23",V:"\u81FA\u6771\u7E23",W:"\u91D1\u9580\u7E23",X:"\u6F8E\u6E56\u7E23",Y:"\u967D\u660E\u5C71\u7BA1\u7406\u5C40",Z:"\u9023\u6C5F\u7E23"};function y(t){if(!/^[A-Z][12]\d{8}$/.test(t))return false;let i=t[0],s=t.slice(1),r=d[i],n=Math.floor(r/10),a=r%10,o=[1,9,8,7,6,5,4,3,2,1,1];return [n,a,...s.split("").map(Number)].reduce((c,f,u)=>c+f*o[u],0)%10===0}function A(t,e){if(!t||typeof t!="string")return {isValid:false,message:"\u8EAB\u5206\u8B49\u5B57\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let i=t.trim().toUpperCase(),s=y(i);return {isValid:s,message:s?void 0:"\u7121\u6548\u7684\u8EAB\u5206\u8B49\u5B57\u865F"}}function b(t){if(!t||typeof t!="string")return {isValid:false,message:"\u8EAB\u5206\u8B49\u5B57\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim().toUpperCase();if(!y(e))return {isValid:false,message:"\u7121\u6548\u7684\u8EAB\u5206\u8B49\u5B57\u865F"};let s=e[0],n=e[1]==="1"?"male":"female",a=p[s];return {isValid:true,gender:n,region:a}}function h(t){if(!t||typeof t!="string")return {isValid:false,message:"\u7D71\u4E00\u7DE8\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim();if(!/^\d{8}$/.test(e))return {isValid:false,message:"\u7D71\u4E00\u7DE8\u865F\u5FC5\u9808\u70BA8\u4F4D\u6578\u5B57"};let s=e.split("").map(Number),r=[1,2,1,2,1,2,4,1],n=0;for(let o=0;o<8;o++){let l=s[o]*r[o];l>=10&&(l=Math.floor(l/10)+l%10),n+=l;}let a=n%10===0||s[6]===7&&n%10===1;return {isValid:a,message:a?void 0:"\u7D71\u4E00\u7DE8\u865F\u6AA2\u67E5\u78BC\u932F\u8AA4"}}function $(t){if(!t||typeof t!="string")return {isValid:false,message:"\u767C\u7968\u865F\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.replace(/[-\s]/g,"").toUpperCase();return /^[A-Z]{2}\d{8}$/.test(e)?{isValid:true}:{isValid:false,message:"\u767C\u7968\u865F\u78BC\u683C\u5F0F\u5FC5\u9808\u70BA2\u78BC\u82F1\u6587\u82078\u78BC\u6578\u5B57"}}function g(t){if(!/^[A-Z][A-D]\d{8}$/.test(t))return false;let i=t[0],s=t[1],r=t.slice(2),n=d[i],a=d[s],o=Math.floor(n/10),l=n%10,m=a%10,c=[1,9,8,7,6,5,4,3,2,1,1];return [o,l,m,...r.split("").map(Number)].reduce((x,N,T)=>x+N*c[T],0)%10===0}function V(t){if(!/^[A-Z][89]\d{8}$/.test(t))return false;let i=t[0],s=t.slice(1),r=d[i],n=Math.floor(r/10),a=r%10,o=[1,9,8,7,6,5,4,3,2,1,1];return [n,a,...s.split("").map(Number)].reduce((c,f,u)=>c+f*o[u],0)%10===0}function R(t){return /^[A-Z][A-D]\d{8}$/.test(t)?"old":/^[A-Z][89]\d{8}$/.test(t)?"new":null}function E(t,e){if(!t||typeof t!="string")return {isValid:false,message:"\u5C45\u7559\u8B49\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let i=t.trim().toUpperCase();if(e==="old"){let n=g(i);return {isValid:n,message:n?void 0:"\u7121\u6548\u7684\u820A\u5F0F\u5C45\u7559\u8B49\u865F"}}if(e==="new"){let n=V(i);return {isValid:n,message:n?void 0:"\u7121\u6548\u7684\u65B0\u5F0F\u5C45\u7559\u8B49\u865F"}}let s=R(i);if(!s)return {isValid:false,message:"\u7121\u6548\u7684\u5C45\u7559\u8B49\u865F\u683C\u5F0F"};let r=s==="old"?g(i):V(i);return {isValid:r,message:r?void 0:`\u7121\u6548\u7684${s==="old"?"\u820A\u5F0F":"\u65B0\u5F0F"}\u5C45\u7559\u8B49\u865F`}}function z(t){if(!t||typeof t!="string")return {isValid:false,message:"\u5C45\u7559\u8B49\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim().toUpperCase(),i=R(e);if(!i)return {isValid:false,message:"\u7121\u6548\u7684\u5C45\u7559\u8B49\u865F\u683C\u5F0F"};if(!(i==="old"?g(e):V(e)))return {isValid:false,message:`\u7121\u6548\u7684${i==="old"?"\u820A\u5F0F":"\u65B0\u5F0F"}\u5C45\u7559\u8B49\u865F`};let r=e[0],n=e[1],a=p[r],o,l;return i==="new"?(o=n==="8"?"male":"female",l="foreigner"):(o=n==="A"||n==="C"?"male":"female",l=n==="A"||n==="B"?"non-citizen":"foreigner"),{isValid:true,format:i,gender:o,region:a,identityType:l}}function M(t){if(!t||typeof t!="string")return {isValid:false,message:"\u624B\u6A5F\u865F\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim().replace(/[\s\-()]/g,"");return /^09\d{8}$/.test(e)?{isValid:true}:{isValid:false,message:"\u624B\u6A5F\u865F\u78BC\u5FC5\u9808\u4EE509\u958B\u982D\u4E14\u70BA10\u4F4D\u6578\u5B57"}}var Z=[{code:"0826",totalLength:9},{code:"0836",totalLength:9},{code:"037",totalLength:9},{code:"049",totalLength:9},{code:"082",totalLength:9},{code:"089",totalLength:9},{code:"02",totalLength:10},{code:"03",totalLength:9},{code:"04",totalLength:10},{code:"05",totalLength:9},{code:"06",totalLength:9},{code:"07",totalLength:9},{code:"08",totalLength:9}];function G(t){if(!t||typeof t!="string")return {isValid:false,message:"\u96FB\u8A71\u865F\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.replace(/[^\d]/g,""),i=Z.find(s=>e.startsWith(s.code));return i?e.length!==i.totalLength?{isValid:false,message:`\u8A72\u5340\u78BC\u7684\u96FB\u8A71\u865F\u78BC\u9577\u5EA6\u61C9\u70BA ${i.totalLength} \u78BC`}:{isValid:true}:{isValid:false,message:"\u7121\u6548\u7684\u53F0\u7063\u5E02\u8A71\u5340\u78BC"}}function U(t){if(t==null)return {isValid:false,message:"\u90F5\u905E\u5340\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32\u6216\u6578\u5B57"};let e=String(t).trim().replace(/[-\s]/g,"");return /^[1-9]\d{2}$|^[1-9]\d{4}$|^[1-9]\d{5}$/.test(e)?{isValid:true}:{isValid:false,message:"\u90F5\u905E\u5340\u865F\u683C\u5F0F\u5FC5\u9808\u70BA\u9996\u78BC\u975E0\u76843\u78BC\u30015\u78BC\u62166\u78BC\u6578\u5B57"}}function w(t){if(!t||typeof t!="string")return {isValid:false,message:"\u81EA\u7136\u4EBA\u6191\u8B49\u7DE8\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim().toUpperCase();return /^[A-Z]{2}\d{14}$/.test(e)?{isValid:true}:{isValid:false,message:"\u81EA\u7136\u4EBA\u6191\u8B49\u7DE8\u865F\u5FC5\u9808\u70BA2\u500B\u82F1\u6587\u5B57\u6BCD\u52A0\u4E0A14\u4F4D\u6578\u5B57"}}function D(t){if(!t||typeof t!="string")return {isValid:false,message:"\u5065\u4FDD\u5361\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.replace(/[-\s]/g,"");return /^\d{12}$/.test(e)?{isValid:true}:{isValid:false,message:"\u5065\u4FDD\u5361\u865F\u683C\u5F0F\u5FC5\u9808\u70BA12\u78BC\u6578\u5B57"}}function O(t){if(!t||typeof t!="string")return {isValid:false,message:"\u8B77\u7167\u865F\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim();return /^\d{9}$/.test(e)?{isValid:true}:{isValid:false,message:"\u8B77\u7167\u865F\u78BC\u683C\u5F0F\u5FC5\u9808\u70BA9\u4F4D\u6578\u5B57"}}function _(t){if(!t||typeof t!="string")return {isValid:false,message:"\u624B\u6A5F\u689D\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim().toUpperCase();return /^\/[A-Z0-9+.-]{7}$/.test(e)?{isValid:true}:{isValid:false,message:"\u624B\u6A5F\u689D\u78BC\u5FC5\u9808\u4EE5 / \u958B\u982D\uFF0C\u5F8C\u63A57\u500B\u6709\u6548\u5B57\u5143\uFF08A-Z\u30010-9\u3001+\u3001-\u3001.\uFF09"}}function B(t){if(!t||typeof t!="string")return {isValid:false,message:"\u6350\u8D08\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim();return /^\d{3,7}$/.test(e)?{isValid:true}:{isValid:false,message:"\u6350\u8D08\u78BC\u5FC5\u9808\u70BA3\u81F37\u4F4D\u6578\u5B57"}}function P(t){return /^[A-HJ-NP-Z]{3}-\d{4}$/.test(t)?!t.split("-")[1].includes("4"):false}function L(t){return /^\d[A-Z]-\d{4}$/.test(t)}function C(t){return /^E[A-HJ-NP-Z]{2}-\d{4}$/.test(t)?!t.split("-")[1].includes("4"):false}function I(t){let e=/^\d{3}-[A-Z]{3}$/,i=/^[A-Z]{3}-\d{3}$/;return e.test(t)||i.test(t)}function v(t){return /^[A-Z]{2}\d-\d{3}$/.test(t)}function F(t){return C(t)?"electric-car":P(t)?"car":L(t)?"car-old":I(t)?"motorcycle-small":v(t)?"motorcycle":null}function k(t,e={}){let{type:i,detectType:s=true}=e;if(!t||typeof t!="string")return {isValid:false,message:"\u8ECA\u724C\u865F\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let r=t.trim().toUpperCase();if(i){let a=false;switch(i){case "car":a=P(r);break;case "car-old":a=L(r);break;case "electric-car":a=C(r);break;case "motorcycle-small":a=I(r);break;case "motorcycle":a=v(r);break}return {isValid:a,message:a?void 0:`\u7121\u6548\u7684${H(i)}\u8ECA\u724C\u865F\u78BC`,plateType:a&&s?i:void 0}}let n=F(r);return n?{isValid:true,plateType:s?n:void 0}:{isValid:false,message:"\u7121\u6548\u7684\u8ECA\u724C\u865F\u78BC\u683C\u5F0F"}}function H(t){return {car:"\u6C7D\u8ECA","car-old":"\u6C7D\u8ECA\uFF08\u820A\u5236\uFF09","electric-car":"\u96FB\u52D5\u6C7D\u8ECA","motorcycle-small":"\u5C0F\u578B\u6A5F\u8ECA",motorcycle:"\u6A5F\u8ECA"}[t]}exports.parseNationalId=b;exports.parseResidentCertificate=z;exports.validateBusinessNumber=h;exports.validateCitizenCertificate=w;exports.validateEInvoiceDonationCode=B;exports.validateEInvoiceMobileBarcode=_;exports.validateLandlinePhone=G;exports.validateLicensePlate=k;exports.validateMobilePhone=M;exports.validateNHICard=D;exports.validateNationalId=A;exports.validatePassport=O;exports.validatePostalCode=U;exports.validateResidentCertificate=E;exports.validateUniformInvoice=$;//# sourceMappingURL=index.cjs.map
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/validators/national-id.ts","../src/validators/business-number.ts","../src/validators/resident-certificate.ts","../src/validators/mobile-phone.ts","../src/validators/citizen-certificate.ts","../src/validators/einvoice-mobile-barcode.ts","../src/validators/einvoice-donation-code.ts","../src/validators/license-plate.ts"],"names":["LETTER_MAPPING","validateOldFormat","id","letter","numbers","letterValue","d1","d2","weights","acc","digit","index","validateNewFormat","firstLetter","secondLetter","firstLetterValue","secondLetterValue","d3","d4","detectFormat","validateNationalId","format","normalizedId","isValid","detectedFormat","validateBusinessNumber","number","normalized","digits","sum","i","product","validateResidentCertificate","validateMobilePhone","phone","validateCitizenCertificate","certNumber","validateEInvoiceMobileBarcode","barcode","validateEInvoiceDonationCode","code","validateNewCarPlate","plate","validateOldCarPlate","validateElectricCarPlate","validateSmallMotorcyclePlate","pattern1","pattern2","validateMotorcyclePlate","detectPlateType","validateLicensePlate","options","type","detectType","getPlateTypeName","detectedType"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ValidationResult, NationalIdType } from \"../types\";\n\n/**\n * 字母對應數字表(用於身分證字號驗證)\n */\nconst LETTER_MAPPING: Record<string, number> = {\n A: 10,\n B: 11,\n C: 12,\n D: 13,\n E: 14,\n F: 15,\n G: 16,\n H: 17,\n I: 34,\n J: 18,\n K: 19,\n L: 20,\n M: 21,\n N: 22,\n O: 35,\n P: 23,\n Q: 24,\n R: 25,\n S: 26,\n T: 27,\n U: 28,\n V: 29,\n W: 32,\n X: 30,\n Y: 31,\n Z: 33,\n};\n\n/**\n * 驗證舊式身分證字號格式(1個字母 + 9個數字)\n * 格式:A123456789\n * - 第一個字元:地區代碼(字母)\n * - 第二個字元:性別(1 = 男性,2 = 女性)\n * - 最後一個字元:檢查碼\n */\nfunction validateOldFormat(id: string): boolean {\n const pattern = /^[A-Z][12]\\d{8}$/;\n if (!pattern.test(id)) {\n return false;\n }\n\n const letter = id[0] as string;\n const numbers = id.slice(1);\n\n const letterValue = LETTER_MAPPING[letter] as number;\n\n // 計算檢查碼\n const d1 = Math.floor(letterValue / 10);\n const d2 = letterValue % 10;\n\n const weights = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1];\n const digits = [d1, d2, ...numbers.split(\"\").map(Number)];\n\n const sum = digits.reduce(\n (acc, digit, index) => acc + digit * weights[index]!,\n 0,\n );\n\n return sum % 10 === 0;\n}\n\n/**\n * 驗證新式身分證字號格式(2個字母 + 8個數字)\n * 格式:AA12345678\n * - 第一個字元:地區代碼(字母)\n * - 第二個字元:性別(8 = 男性,9 = 女性)以字母表示\n * - 最後一個字元:檢查碼\n */\nfunction validateNewFormat(id: string): boolean {\n const pattern = /^[A-Z]{2}\\d{8}$/;\n if (!pattern.test(id)) {\n return false;\n }\n\n const firstLetter = id[0] as string;\n const secondLetter = id[1] as string;\n const numbers = id.slice(2);\n\n const firstLetterValue = LETTER_MAPPING[firstLetter] as number;\n const secondLetterValue = LETTER_MAPPING[secondLetter] as number;\n\n // 計算新式格式的檢查碼\n const d1 = Math.floor(firstLetterValue / 10);\n const d2 = firstLetterValue % 10;\n const d3 = Math.floor(secondLetterValue / 10);\n const d4 = secondLetterValue % 10;\n\n const weights = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 1];\n const digits = [d1, d2, d3, d4, ...numbers.split(\"\").map(Number)];\n\n const sum = digits.reduce(\n (acc, digit, index) => acc + digit * weights[index]!,\n 0,\n );\n\n return sum % 10 === 0;\n}\n\n/**\n * 偵測身分證字號格式類型\n */\nfunction detectFormat(id: string): NationalIdType | null {\n if (/^[A-Z][12]\\d{8}$/.test(id)) {\n return \"old\";\n }\n if (/^[A-Z]{2}\\d{8}$/.test(id)) {\n return \"new\";\n }\n return null;\n}\n\n/**\n * 驗證台灣身分證字號(支援新舊格式)\n * @param id - 要驗證的身分證字號\n * @param format - 可選:指定格式類型('old' 或 'new')\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateNationalId('A123456789'); // 舊式格式\n * validateNationalId('AA12345678'); // 新式格式\n * ```\n */\nexport function validateNationalId(\n id: string,\n format?: NationalIdType,\n): ValidationResult {\n if (!id || typeof id !== \"string\") {\n return {\n isValid: false,\n message: \"身分證字號必須為非空字串\",\n };\n }\n\n const normalizedId = id.trim().toUpperCase();\n\n // 如果指定了格式,只驗證該格式\n if (format === \"old\") {\n const isValid = validateOldFormat(normalizedId);\n return {\n isValid,\n message: isValid ? undefined : \"無效的舊式身分證字號\",\n };\n }\n\n if (format === \"new\") {\n const isValid = validateNewFormat(normalizedId);\n return {\n isValid,\n message: isValid ? undefined : \"無效的新式身分證字號\",\n };\n }\n\n // 自動偵測格式\n const detectedFormat = detectFormat(normalizedId);\n\n if (!detectedFormat) {\n return {\n isValid: false,\n message: \"無效的身分證字號格式\",\n };\n }\n\n const isValid =\n detectedFormat === \"old\"\n ? validateOldFormat(normalizedId)\n : validateNewFormat(normalizedId);\n\n return {\n isValid,\n message: isValid\n ? undefined\n : `無效的${detectedFormat === \"old\" ? \"舊式\" : \"新式\"}身分證字號`,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣營利事業統一編號\n * 格式:8位數字\n * 使用加權檢查碼演算法\n *\n * @param number - 要驗證的統一編號\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateBusinessNumber('12345678');\n * ```\n */\nexport function validateBusinessNumber(number: string): ValidationResult {\n if (!number || typeof number !== \"string\") {\n return {\n isValid: false,\n message: \"統一編號必須為非空字串\",\n };\n }\n\n const normalized = number.trim();\n\n // 檢查是否為8位數字\n const pattern = /^\\d{8}$/;\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"統一編號必須為8位數字\",\n };\n }\n\n const digits = normalized.split(\"\").map(Number);\n const weights = [1, 2, 1, 2, 1, 2, 4, 1];\n\n // 計算加權總和\n let sum = 0;\n for (let i = 0; i < 8; i++) {\n let product = digits[i]! * weights[i]!;\n\n // 如果乘積為兩位數,將十位數和個位數相加\n if (product >= 10) {\n product = Math.floor(product / 10) + (product % 10);\n }\n\n sum += product;\n }\n\n // 特殊情況:第7位數字為7時\n // 如果第7位數字為7且總和除以10的餘數為1,也視為有效\n const isValid = sum % 10 === 0 || (digits[6]! === 7 && sum % 10 === 1);\n\n return {\n isValid,\n message: isValid ? undefined : \"統一編號檢查碼錯誤\",\n };\n}\n","import type { ValidationResult, ResidentCertificateType } from \"../types\";\n\n/**\n * 字母對應數字表(用於居留證號驗證)\n */\nconst LETTER_MAPPING: Record<string, number> = {\n A: 10,\n B: 11,\n C: 12,\n D: 13,\n E: 14,\n F: 15,\n G: 16,\n H: 17,\n I: 34,\n J: 18,\n K: 19,\n L: 20,\n M: 21,\n N: 22,\n O: 35,\n P: 23,\n Q: 24,\n R: 25,\n S: 26,\n T: 27,\n U: 28,\n V: 29,\n W: 32,\n X: 30,\n Y: 31,\n Z: 33,\n};\n\n/**\n * 驗證舊式居留證號格式(1個字母 + 9個數字)\n * 格式:A800000000\n * - 第一個字元:地區代碼(A、B、C 或 D 表示外國人士)\n * - 第二個字元:性別/類型(8 = 男性,9 = 女性)\n * - 最後一個字元:檢查碼\n */\nfunction validateOldFormat(id: string): boolean {\n const pattern = /^[A-D][89]\\d{8}$/;\n if (!pattern.test(id)) {\n return false;\n }\n\n const letter = id[0] as string;\n const numbers = id.slice(1);\n\n const letterValue = LETTER_MAPPING[letter] as number;\n\n // 計算檢查碼(與身分證字號相同的演算法)\n const d1 = Math.floor(letterValue / 10);\n const d2 = letterValue % 10;\n\n const weights = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1];\n const digits = [d1, d2, ...numbers.split(\"\").map(Number)];\n\n const sum = digits.reduce(\n (acc, digit, index) => acc + digit * weights[index]!,\n 0,\n );\n\n return sum % 10 === 0;\n}\n\n/**\n * 驗證新式居留證號格式(2個字母 + 8個數字)\n * 格式:AA12345678\n * - 第一個字元:地區代碼(字母)\n * - 第二個字元:性別(8 = 男性,9 = 女性)以字母表示\n * - 最後一個字元:檢查碼\n */\nfunction validateNewFormat(id: string): boolean {\n const pattern = /^[A-Z]{2}\\d{8}$/;\n if (!pattern.test(id)) {\n return false;\n }\n\n const firstLetter = id[0] as string;\n const secondLetter = id[1] as string;\n const numbers = id.slice(2);\n\n const firstLetterValue = LETTER_MAPPING[firstLetter] as number;\n const secondLetterValue = LETTER_MAPPING[secondLetter] as number;\n\n // 計算新式格式的檢查碼\n const d1 = Math.floor(firstLetterValue / 10);\n const d2 = firstLetterValue % 10;\n const d3 = Math.floor(secondLetterValue / 10);\n const d4 = secondLetterValue % 10;\n\n const weights = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 1];\n const digits = [d1, d2, d3, d4, ...numbers.split(\"\").map(Number)];\n\n const sum = digits.reduce(\n (acc, digit, index) => acc + digit * weights[index]!,\n 0,\n );\n\n return sum % 10 === 0;\n}\n\n/**\n * 偵測居留證號格式類型\n */\nfunction detectFormat(id: string): ResidentCertificateType | null {\n if (/^[A-D][89]\\d{8}$/.test(id)) {\n return \"old\";\n }\n if (/^[A-Z]{2}\\d{8}$/.test(id)) {\n return \"new\";\n }\n return null;\n}\n\n/**\n * 驗證台灣居留證號(支援新舊格式)\n * @param id - 要驗證的居留證號\n * @param format - 可選:指定格式類型('old' 或 'new')\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateResidentCertificate('A800000000'); // 舊式格式\n * validateResidentCertificate('AA12345678'); // 新式格式\n * ```\n */\nexport function validateResidentCertificate(\n id: string,\n format?: ResidentCertificateType,\n): ValidationResult {\n if (!id || typeof id !== \"string\") {\n return {\n isValid: false,\n message: \"居留證號必須為非空字串\",\n };\n }\n\n const normalizedId = id.trim().toUpperCase();\n\n // 如果指定了格式,只驗證該格式\n if (format === \"old\") {\n const isValid = validateOldFormat(normalizedId);\n return {\n isValid,\n message: isValid ? undefined : \"無效的舊式居留證號\",\n };\n }\n\n if (format === \"new\") {\n const isValid = validateNewFormat(normalizedId);\n return {\n isValid,\n message: isValid ? undefined : \"無效的新式居留證號\",\n };\n }\n\n // 自動偵測格式\n const detectedFormat = detectFormat(normalizedId);\n\n if (!detectedFormat) {\n return {\n isValid: false,\n message: \"無效的居留證號格式\",\n };\n }\n\n const isValid =\n detectedFormat === \"old\"\n ? validateOldFormat(normalizedId)\n : validateNewFormat(normalizedId);\n\n return {\n isValid,\n message: isValid\n ? undefined\n : `無效的${detectedFormat === \"old\" ? \"舊式\" : \"新式\"}居留證號`,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣手機號碼\n * 格式:09XXXXXXXX(10位數字,以09開頭)\n *\n * @param phone - 要驗證的手機號碼\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateMobilePhone('0912345678');\n * validateMobilePhone('0912-345-678'); // 含分隔符號\n * ```\n */\nexport function validateMobilePhone(phone: string): ValidationResult {\n if (!phone || typeof phone !== \"string\") {\n return {\n isValid: false,\n message: \"手機號碼必須為非空字串\",\n };\n }\n\n // 移除常見的分隔符號(空格、破折號、括號)\n const normalized = phone.trim().replace(/[\\s\\-()]/g, \"\");\n\n // 檢查是否符合台灣手機號碼格式\n // 以09開頭且總共10位數字\n const pattern = /^09\\d{8}$/;\n\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"手機號碼必須以09開頭且為10位數字\",\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣自然人憑證編號\n * 格式:2個大寫英文字母 + 14位數字\n * 範例:AB12345678901234\n *\n * @param certNumber - 要驗證的自然人憑證編號\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateCitizenCertificate('AB12345678901234');\n * ```\n */\nexport function validateCitizenCertificate(\n certNumber: string,\n): ValidationResult {\n if (!certNumber || typeof certNumber !== \"string\") {\n return {\n isValid: false,\n message: \"自然人憑證編號必須為非空字串\",\n };\n }\n\n const normalized = certNumber.trim().toUpperCase();\n\n // 檢查格式:2個字母 + 14位數字\n const pattern = /^[A-Z]{2}\\d{14}$/;\n\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"自然人憑證編號必須為2個英文字母加上14位數字\",\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣電子發票手機條碼\n * 格式:/ + 7個字元(大寫英文字母、數字、+、-、.)\n * 範例:/ABCD123\n *\n * 手機條碼用於將電子發票儲存在手機載具中\n *\n * @param barcode - 要驗證的手機條碼\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateEInvoiceMobileBarcode('/ABCD123');\n * validateEInvoiceMobileBarcode('/1234567');\n * ```\n */\nexport function validateEInvoiceMobileBarcode(\n barcode: string,\n): ValidationResult {\n if (!barcode || typeof barcode !== \"string\") {\n return {\n isValid: false,\n message: \"手機條碼必須為非空字串\",\n };\n }\n\n const normalized = barcode.trim().toUpperCase();\n\n // 檢查格式:以 / 開頭,後接7個字元\n // 有效字元:A-Z、0-9、+、-、.\n const pattern = /^\\/[A-Z0-9+.-]{7}$/;\n\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"手機條碼必須以 / 開頭,後接7個有效字元(A-Z、0-9、+、-、.)\",\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣電子發票捐贈碼\n * 格式:3-7位數字\n * 範例:123、12345、1234567\n *\n * 捐贈碼用於將電子發票捐贈給已註冊的慈善機構\n *\n * @param code - 要驗證的捐贈碼\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateEInvoiceDonationCode('123');\n * validateEInvoiceDonationCode('12345');\n * ```\n */\nexport function validateEInvoiceDonationCode(code: string): ValidationResult {\n if (!code || typeof code !== \"string\") {\n return {\n isValid: false,\n message: \"捐贈碼必須為非空字串\",\n };\n }\n\n const normalized = code.trim();\n\n // 檢查格式:3-7位數字\n const pattern = /^\\d{3,7}$/;\n\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"捐贈碼必須為3至7位數字\",\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 車牌類型\n */\nexport type LicensePlateType =\n | \"car\" // 汽車(新制)\n | \"car-old\" // 汽車(舊制)\n | \"electric-car\" // 電動汽車\n | \"motorcycle-small\" // 小型機車(50cc以下)\n | \"motorcycle\"; // 一般機車\n\n/**\n * 車牌驗證結果(含車牌類型資訊)\n */\nexport interface LicensePlateValidationResult extends ValidationResult {\n plateType?: LicensePlateType | undefined;\n}\n\n/**\n * 驗證新制汽車車牌(2012年12月後)\n * 格式:3個英文字母 - 4個數字\n * 不使用字母 I、O\n * 不使用數字 4\n */\nfunction validateNewCarPlate(plate: string): boolean {\n // 格式:3個英文字母-4個數字(例如:ABC-1234)\n const pattern = /^[A-HJ-NP-Z]{3}-\\d{4}$/;\n if (!pattern.test(plate)) {\n return false;\n }\n\n // 檢查數字部分不包含 4\n const numbers = plate.split(\"-\")[1] as string;\n return !numbers.includes(\"4\");\n}\n\n/**\n * 驗證舊制汽車車牌(1992-2012)\n * 格式:1個數字 + 1個英文字母 - 4個數字\n */\nfunction validateOldCarPlate(plate: string): boolean {\n // 格式:1個數字+1個英文字母-4個數字(例如:1A-2345)\n const pattern = /^\\d[A-Z]-\\d{4}$/;\n return pattern.test(plate);\n}\n\n/**\n * 驗證電動汽車車牌\n * 格式:E + 2個英文字母 - 4個數字\n * 不使用字母 I、O\n * 不使用數字 4\n */\nfunction validateElectricCarPlate(plate: string): boolean {\n // 格式:E + 2個英文字母-4個數字(例如:EAB-1234)\n const pattern = /^E[A-HJ-NP-Z]{2}-\\d{4}$/;\n if (!pattern.test(plate)) {\n return false;\n }\n\n // 檢查數字部分不包含 4\n const numbers = plate.split(\"-\")[1] as string;\n return !numbers.includes(\"4\");\n}\n\n/**\n * 驗證小型機車車牌(50cc以下)\n * 格式:3個數字-3個英文字母 或 3個英文字母-3個數字\n */\nfunction validateSmallMotorcyclePlate(plate: string): boolean {\n // 格式1:3個數字-3個英文字母(例如:123-ABC)\n // 格式2:3個英文字母-3個數字(例如:ABC-123)\n const pattern1 = /^\\d{3}-[A-Z]{3}$/;\n const pattern2 = /^[A-Z]{3}-\\d{3}$/;\n return pattern1.test(plate) || pattern2.test(plate);\n}\n\n/**\n * 驗證一般機車車牌(舊制,50-250cc)\n * 格式:2個英文字母 + 1個數字 - 3個數字\n */\nfunction validateMotorcyclePlate(plate: string): boolean {\n // 格式:2個英文字母+1個數字-3個數字(例如:AB1-234)\n const pattern = /^[A-Z]{2}\\d-\\d{3}$/;\n return pattern.test(plate);\n}\n\n/**\n * 偵測車牌類型\n */\nfunction detectPlateType(plate: string): LicensePlateType | null {\n if (validateElectricCarPlate(plate)) {\n return \"electric-car\";\n }\n if (validateNewCarPlate(plate)) {\n return \"car\";\n }\n if (validateOldCarPlate(plate)) {\n return \"car-old\";\n }\n if (validateSmallMotorcyclePlate(plate)) {\n return \"motorcycle-small\";\n }\n if (validateMotorcyclePlate(plate)) {\n return \"motorcycle\";\n }\n return null;\n}\n\n/**\n * 驗證台灣車牌號碼\n * @param plate - 要驗證的車牌號碼\n * @param options - 驗證選項\n * @param options.type - 可選:指定車牌類型\n * @param options.detectType - 是否偵測車牌類型(預設:true)\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * // 新制汽車\n * validateLicensePlate('ABC-1235');\n *\n * // 電動汽車\n * validateLicensePlate('EAB-1235');\n *\n * // 舊制汽車\n * validateLicensePlate('1A-2345');\n *\n * // 小型機車\n * validateLicensePlate('123-ABC');\n * validateLicensePlate('ABC-123');\n *\n * // 一般機車\n * validateLicensePlate('AB1-234');\n *\n * // 指定類型驗證\n * validateLicensePlate('ABC-1235', { type: 'car' });\n *\n * // 不偵測類型\n * validateLicensePlate('ABC-1235', { detectType: false });\n * ```\n */\nexport function validateLicensePlate(\n plate: string,\n options: { type?: LicensePlateType; detectType?: boolean } = {},\n): LicensePlateValidationResult {\n const { type, detectType = true } = options;\n\n if (!plate || typeof plate !== \"string\") {\n return {\n isValid: false,\n message: \"車牌號碼必須為非空字串\",\n };\n }\n\n // 移除空格並轉換為大寫\n const normalized = plate.trim().toUpperCase();\n\n // 如果指定了類型,只驗證該類型\n if (type) {\n let isValid = false;\n switch (type) {\n case \"car\":\n isValid = validateNewCarPlate(normalized);\n break;\n case \"car-old\":\n isValid = validateOldCarPlate(normalized);\n break;\n case \"electric-car\":\n isValid = validateElectricCarPlate(normalized);\n break;\n case \"motorcycle-small\":\n isValid = validateSmallMotorcyclePlate(normalized);\n break;\n case \"motorcycle\":\n isValid = validateMotorcyclePlate(normalized);\n break;\n }\n\n return {\n isValid,\n message: isValid ? undefined : `無效的${getPlateTypeName(type)}車牌號碼`,\n plateType: isValid && detectType ? type : undefined,\n };\n }\n\n // 自動偵測類型\n const detectedType = detectPlateType(normalized);\n\n if (!detectedType) {\n return {\n isValid: false,\n message: \"無效的車牌號碼格式\",\n };\n }\n\n return {\n isValid: true,\n plateType: detectType ? detectedType : undefined,\n };\n}\n\n/**\n * 取得車牌類型的中文名稱\n */\nfunction getPlateTypeName(type: LicensePlateType): string {\n const typeNames: Record<LicensePlateType, string> = {\n car: \"汽車\",\n \"car-old\": \"汽車(舊制)\",\n \"electric-car\": \"電動汽車\",\n \"motorcycle-small\": \"小型機車\",\n motorcycle: \"機車\",\n };\n return typeNames[type];\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/validators/shared.ts","../src/validators/national-id.ts","../src/validators/business-number.ts","../src/validators/uniform-invoice.ts","../src/validators/resident-certificate.ts","../src/validators/mobile-phone.ts","../src/validators/landline-phone.ts","../src/validators/postal-code.ts","../src/validators/citizen-certificate.ts","../src/validators/nhi-card.ts","../src/validators/passport.ts","../src/validators/einvoice-mobile-barcode.ts","../src/validators/einvoice-donation-code.ts","../src/validators/license-plate.ts"],"names":["LETTER_MAPPING","REGION_MAPPING","validateFormatAndChecksum","id","letter","numbers","letterValue","d1","d2","weights","acc","digit","index","validateNationalId","_format","normalizedId","isValid","parseNationalId","firstLetter","gender","region","validateBusinessNumber","number","normalized","digits","sum","i","product","validateUniformInvoice","invoice","validateOldFormat","secondLetter","firstLetterValue","secondLetterValue","d3","validateNewFormat","detectFormat","validateResidentCertificate","format","detectedFormat","parseResidentCertificate","secondChar","identityType","validateMobilePhone","phone","AREA_CODES","validateLandlinePhone","match","entry","validatePostalCode","code","strCode","validateCitizenCertificate","certNumber","validateNHICard","cardNumber","validatePassport","passport","validateEInvoiceMobileBarcode","barcode","validateEInvoiceDonationCode","validateNewCarPlate","plate","validateOldCarPlate","validateElectricCarPlate","validateSmallMotorcyclePlate","pattern1","pattern2","validateMotorcyclePlate","detectPlateType","validateLicensePlate","options","type","detectType","getPlateTypeName","detectedType"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * 字母對應數字表(用於身分證字號與居留證號驗證)\n */\nexport const LETTER_MAPPING: Record<string, number> = {\n A: 10,\n B: 11,\n C: 12,\n D: 13,\n E: 14,\n F: 15,\n G: 16,\n H: 17,\n I: 34,\n J: 18,\n K: 19,\n L: 20,\n M: 21,\n N: 22,\n O: 35,\n P: 23,\n Q: 24,\n R: 25,\n S: 26,\n T: 27,\n U: 28,\n V: 29,\n W: 32,\n X: 30,\n Y: 31,\n Z: 33,\n};\n\n/**\n * 字母對應地區名稱表\n */\nexport const REGION_MAPPING: Record<string, string> = {\n A: \"臺北市\",\n B: \"臺中市\",\n C: \"基隆市\",\n D: \"臺南市\",\n E: \"高雄市\",\n F: \"新北市\",\n G: \"宜蘭縣\",\n H: \"桃園市\",\n I: \"嘉義市\",\n J: \"新竹縣\",\n K: \"苗栗縣\",\n L: \"臺中縣\",\n M: \"南投縣\",\n N: \"彰化縣\",\n O: \"新竹市\",\n P: \"雲林縣\",\n Q: \"嘉義縣\",\n R: \"臺南縣\",\n S: \"高雄縣\",\n T: \"屏東縣\",\n U: \"花蓮縣\",\n V: \"臺東縣\",\n W: \"金門縣\",\n X: \"澎湖縣\",\n Y: \"陽明山管理局\",\n Z: \"連江縣\",\n};\n","import type { ValidationResult, NationalIdType, NationalIdInfo } from \"../types\";\nimport { LETTER_MAPPING, REGION_MAPPING } from \"./shared\";\n\n/**\n * 驗證台灣身分證字號格式與檢查碼(1個字母 + 9個數字)\n * 格式:A123456789\n * - 第一個字元:地區代碼(字母)\n * - 第二個字元:性別(1 = 男性,2 = 女性)\n * - 最後一個字元:檢查碼\n */\nfunction validateFormatAndChecksum(id: string): boolean {\n const pattern = /^[A-Z][12]\\d{8}$/;\n if (!pattern.test(id)) {\n return false;\n }\n\n const letter = id[0] as string;\n const numbers = id.slice(1);\n\n const letterValue = LETTER_MAPPING[letter] as number;\n\n const d1 = Math.floor(letterValue / 10);\n const d2 = letterValue % 10;\n\n const weights = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1];\n const digits = [d1, d2, ...numbers.split(\"\").map(Number)];\n\n const sum = digits.reduce(\n (acc, digit, index) => acc + digit * weights[index]!,\n 0,\n );\n\n return sum % 10 === 0;\n}\n\n/**\n * 驗證台灣身分證字號(身分證字號皆為「1個字母 + 9個數字」格式)\n * @param id - 要驗證的身分證字號\n * @param format - 可選(為維持相容性保留):指定格式類型\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateNationalId('A123456789');\n * ```\n */\nexport function validateNationalId(\n id: string,\n _format?: NationalIdType,\n): ValidationResult {\n if (!id || typeof id !== \"string\") {\n return {\n isValid: false,\n message: \"身分證字號必須為非空字串\",\n };\n }\n\n const normalizedId = id.trim().toUpperCase();\n\n const isValid = validateFormatAndChecksum(normalizedId);\n\n return {\n isValid,\n message: isValid ? undefined : \"無效的身分證字號\",\n };\n}\n\n/**\n * 解析台灣身分證字號資訊(性別、發證地區)\n * @param id - 要解析的身分證字號\n * @returns 解析結果,包含驗證狀態及相關欄位\n *\n * @example\n * ```typescript\n * parseNationalId('A123456789');\n * // 輸出: { isValid: true, gender: 'male', region: '臺北市' }\n * ```\n */\nexport function parseNationalId(id: string): NationalIdInfo {\n if (!id || typeof id !== \"string\") {\n return {\n isValid: false,\n message: \"身分證字號必須為非空字串\",\n };\n }\n\n const normalizedId = id.trim().toUpperCase();\n\n const isValid = validateFormatAndChecksum(normalizedId);\n if (!isValid) {\n return {\n isValid: false,\n message: \"無效的身分證字號\",\n };\n }\n\n const firstLetter = normalizedId[0] as string;\n const genderDigit = normalizedId[1] as string;\n\n const gender = genderDigit === \"1\" ? \"male\" : \"female\";\n const region = REGION_MAPPING[firstLetter] as string;\n\n return {\n isValid: true,\n gender,\n region,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣營利事業統一編號\n * 格式:8位數字\n * 使用加權檢查碼演算法\n *\n * @param number - 要驗證的統一編號\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateBusinessNumber('12345678');\n * ```\n */\nexport function validateBusinessNumber(number: string): ValidationResult {\n if (!number || typeof number !== \"string\") {\n return {\n isValid: false,\n message: \"統一編號必須為非空字串\",\n };\n }\n\n const normalized = number.trim();\n\n // 檢查是否為8位數字\n const pattern = /^\\d{8}$/;\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"統一編號必須為8位數字\",\n };\n }\n\n const digits = normalized.split(\"\").map(Number);\n const weights = [1, 2, 1, 2, 1, 2, 4, 1];\n\n // 計算加權總和\n let sum = 0;\n for (let i = 0; i < 8; i++) {\n let product = digits[i]! * weights[i]!;\n\n // 如果乘積為兩位數,將十位數和個位數相加\n if (product >= 10) {\n product = Math.floor(product / 10) + (product % 10);\n }\n\n sum += product;\n }\n\n // 特殊情況:第7位數字為7時\n // 如果第7位數字為7且總和除以10的餘數為1,也視為有效\n const isValid = sum % 10 === 0 || (digits[6]! === 7 && sum % 10 === 1);\n\n return {\n isValid,\n message: isValid ? undefined : \"統一編號檢查碼錯誤\",\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣統一發票號碼格式\n * 格式為 2 碼英文開頭 + 8 碼數字(可包含減號或空格,如 AB-12345678 或 AB 12345678)\n *\n * @param invoice - 要驗證的發票號碼\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateUniformInvoice('AB-12345678'); // { isValid: true }\n * validateUniformInvoice('AB12345678'); // { isValid: true }\n * ```\n */\nexport function validateUniformInvoice(invoice: string): ValidationResult {\n if (!invoice || typeof invoice !== \"string\") {\n return {\n isValid: false,\n message: \"發票號碼必須為非空字串\",\n };\n }\n\n // 去除空格與減號並轉大寫\n const normalized = invoice.replace(/[-\\s]/g, \"\").toUpperCase();\n\n const pattern = /^[A-Z]{2}\\d{8}$/;\n\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"發票號碼格式必須為2碼英文與8碼數字\",\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult, ResidentCertificateType, ResidentCertificateInfo } from \"../types\";\nimport { LETTER_MAPPING, REGION_MAPPING } from \"./shared\";\n\n/**\n * 驗證舊式居留證號格式與檢查碼(2個字母 + 8個數字)\n * 格式:AA12345678\n * - 第一個字元:地區代碼(字母)\n * - 第二個字元:身分與性別碼(A = 男性無戶籍國民/港澳陸居民,B = 女性無戶籍國民/港澳陸居民,C = 男性外國人,D = 女性外國人)\n * - 最後一個字元:檢查碼\n */\nfunction validateOldFormat(id: string): boolean {\n const pattern = /^[A-Z][A-D]\\d{8}$/;\n if (!pattern.test(id)) {\n return false;\n }\n\n const firstLetter = id[0] as string;\n const secondLetter = id[1] as string;\n const numbers = id.slice(2);\n\n const firstLetterValue = LETTER_MAPPING[firstLetter] as number;\n const secondLetterValue = LETTER_MAPPING[secondLetter] as number;\n\n const d1 = Math.floor(firstLetterValue / 10);\n const d2 = firstLetterValue % 10;\n const d3 = secondLetterValue % 10; // 取第二個字母對應數字的個位數\n\n const weights = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1];\n const digits = [d1, d2, d3, ...numbers.split(\"\").map(Number)];\n\n const sum = digits.reduce(\n (acc, digit, index) => acc + digit * weights[index]!,\n 0,\n );\n\n return sum % 10 === 0;\n}\n\n/**\n * 驗證新式居留證號格式與檢查碼(1個字母 + 9個數字,其中第二碼為8或9)\n * 格式:A800000001\n * - 第一個字元:地區代碼(字母)\n * - 第二個字元:性別(8 = 男性,9 = 女性)\n * - 最後一個字元:檢查碼\n */\nfunction validateNewFormat(id: string): boolean {\n const pattern = /^[A-Z][89]\\d{8}$/;\n if (!pattern.test(id)) {\n return false;\n }\n\n const letter = id[0] as string;\n const numbers = id.slice(1);\n\n const letterValue = LETTER_MAPPING[letter] as number;\n\n const d1 = Math.floor(letterValue / 10);\n const d2 = letterValue % 10;\n\n const weights = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1];\n const digits = [d1, d2, ...numbers.split(\"\").map(Number)];\n\n const sum = digits.reduce(\n (acc, digit, index) => acc + digit * weights[index]!,\n 0,\n );\n\n return sum % 10 === 0;\n}\n\n/**\n * 偵測居留證號格式類型\n */\nfunction detectFormat(id: string): ResidentCertificateType | null {\n if (/^[A-Z][A-D]\\d{8}$/.test(id)) {\n return \"old\";\n }\n if (/^[A-Z][89]\\d{8}$/.test(id)) {\n return \"new\";\n }\n return null;\n}\n\n/**\n * 驗證台灣居留證號(支援新舊格式,舊版為2字母+8數字,新版為1字母+9數字)\n * @param id - 要驗證的居留證號\n * @param format - 可選:指定格式類型('old' 或 'new')\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateResidentCertificate('AD00000001', 'old'); // 舊式格式\n * validateResidentCertificate('A800000001', 'new'); // 新式格式\n * ```\n */\nexport function validateResidentCertificate(\n id: string,\n format?: ResidentCertificateType,\n): ValidationResult {\n if (!id || typeof id !== \"string\") {\n return {\n isValid: false,\n message: \"居留證號必須為非空字串\",\n };\n }\n\n const normalizedId = id.trim().toUpperCase();\n\n if (format === \"old\") {\n const isValid = validateOldFormat(normalizedId);\n return {\n isValid,\n message: isValid ? undefined : \"無效的舊式居留證號\",\n };\n }\n\n if (format === \"new\") {\n const isValid = validateNewFormat(normalizedId);\n return {\n isValid,\n message: isValid ? undefined : \"無效的新式居留證號\",\n };\n }\n\n const detectedFormat = detectFormat(normalizedId);\n\n if (!detectedFormat) {\n return {\n isValid: false,\n message: \"無效的居留證號格式\",\n };\n }\n\n const isValid =\n detectedFormat === \"old\"\n ? validateOldFormat(normalizedId)\n : validateNewFormat(normalizedId);\n\n return {\n isValid,\n message: isValid\n ? undefined\n : `無效的${detectedFormat === \"old\" ? \"舊式\" : \"新式\"}居留證號`,\n };\n}\n\n/**\n * 解析台灣居留證號資訊(格式版本、性別、發證地區、身分類型)\n * @param id - 要解析的居留證號\n * @returns 解析結果,包含驗證狀態及相關欄位\n *\n * @example\n * ```typescript\n * parseResidentCertificate('A800000001');\n * // 輸出: { isValid: true, format: 'new', gender: 'male', region: '臺北市' }\n * ```\n */\nexport function parseResidentCertificate(id: string): ResidentCertificateInfo {\n if (!id || typeof id !== \"string\") {\n return {\n isValid: false,\n message: \"居留證號必須為非空字串\",\n };\n }\n\n const normalizedId = id.trim().toUpperCase();\n\n const format = detectFormat(normalizedId);\n if (!format) {\n return {\n isValid: false,\n message: \"無效的居留證號格式\",\n };\n }\n\n const isValid =\n format === \"old\"\n ? validateOldFormat(normalizedId)\n : validateNewFormat(normalizedId);\n\n if (!isValid) {\n return {\n isValid: false,\n message: `無效的${format === \"old\" ? \"舊式\" : \"新式\"}居留證號`,\n };\n }\n\n const firstLetter = normalizedId[0] as string;\n const secondChar = normalizedId[1] as string;\n\n const region = REGION_MAPPING[firstLetter] as string;\n let gender: \"male\" | \"female\";\n let identityType: \"non-citizen\" | \"foreigner\" | undefined;\n\n if (format === \"new\") {\n gender = secondChar === \"8\" ? \"male\" : \"female\";\n identityType = \"foreigner\"; // 新式格式下通常統稱為外來人口\n } else {\n // 舊式格式: A, B, C, D\n gender = (secondChar === \"A\" || secondChar === \"C\") ? \"male\" : \"female\";\n identityType = (secondChar === \"A\" || secondChar === \"B\") ? \"non-citizen\" : \"foreigner\";\n }\n\n return {\n isValid: true,\n format,\n gender,\n region,\n identityType,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣手機號碼\n * 格式:09XXXXXXXX(10位數字,以09開頭)\n *\n * @param phone - 要驗證的手機號碼\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateMobilePhone('0912345678');\n * validateMobilePhone('0912-345-678'); // 含分隔符號\n * ```\n */\nexport function validateMobilePhone(phone: string): ValidationResult {\n if (!phone || typeof phone !== \"string\") {\n return {\n isValid: false,\n message: \"手機號碼必須為非空字串\",\n };\n }\n\n // 移除常見的分隔符號(空格、破折號、括號)\n const normalized = phone.trim().replace(/[\\s\\-()]/g, \"\");\n\n // 檢查是否符合台灣手機號碼格式\n // 以09開頭且總共10位數字\n const pattern = /^09\\d{8}$/;\n\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"手機號碼必須以09開頭且為10位數字\",\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n// 台灣市話區碼與對應的總長度(含區碼)\nconst AREA_CODES = [\n { code: \"0826\", totalLength: 9 }, // 烏坵\n { code: \"0836\", totalLength: 9 }, // 馬祖\n { code: \"037\", totalLength: 9 }, // 苗栗\n { code: \"049\", totalLength: 9 }, // 南投\n { code: \"082\", totalLength: 9 }, // 金門\n { code: \"089\", totalLength: 9 }, // 台東\n { code: \"02\", totalLength: 10 }, // 雙北、基隆\n { code: \"03\", totalLength: 9 }, // 桃園、新竹、宜蘭、花蓮\n { code: \"04\", totalLength: 10 }, // 台中、彰化\n { code: \"05\", totalLength: 9 }, // 雲林、嘉義\n { code: \"06\", totalLength: 9 }, // 台南、澎湖\n { code: \"07\", totalLength: 9 }, // 高雄\n { code: \"08\", totalLength: 9 }, // 屏東\n];\n\n/**\n * 驗證台灣市內電話號碼格式\n * 支援帶有括號、減號或空格的格式(例如 (02) 1234-5678, 02-12345678, 03-1234567)\n *\n * @param phone - 要驗證的市話號碼\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateLandlinePhone('(02) 1234-5678'); // { isValid: true }\n * validateLandlinePhone('03-1234567'); // { isValid: true }\n * ```\n */\nexport function validateLandlinePhone(phone: string): ValidationResult {\n if (!phone || typeof phone !== \"string\") {\n return {\n isValid: false,\n message: \"電話號碼必須為非空字串\",\n };\n }\n\n // 移除非數字的字元\n const normalized = phone.replace(/[^\\d]/g, \"\");\n\n // 尋找匹配的區碼\n const match = AREA_CODES.find((entry) => normalized.startsWith(entry.code));\n\n if (!match) {\n return {\n isValid: false,\n message: \"無效的台灣市話區碼\",\n };\n }\n\n if (normalized.length !== match.totalLength) {\n return {\n isValid: false,\n message: `該區碼的電話號碼長度應為 ${match.totalLength} 碼`,\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣郵遞區號格式\n * 支援 3 碼、5 碼 (3+2) 及新式 6 碼 (3+3) 格式(可包含減號,如 100-001 或 100001)\n * 第一碼必須為 1-9\n *\n * @param code - 要驗證的郵遞區號\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validatePostalCode('100'); // { isValid: true }\n * validatePostalCode('100-01'); // { isValid: true }\n * validatePostalCode('100001'); // { isValid: true }\n * ```\n */\nexport function validatePostalCode(code: string | number): ValidationResult {\n if (code === null || code === undefined) {\n return {\n isValid: false,\n message: \"郵遞區號必須為非空字串或數字\",\n };\n }\n\n const strCode = String(code).trim().replace(/[-\\s]/g, \"\");\n\n // 驗證格式:3 碼、5 碼或 6 碼,且首碼為 1-9\n const pattern = /^[1-9]\\d{2}$|^[1-9]\\d{4}$|^[1-9]\\d{5}$/;\n\n if (!pattern.test(strCode)) {\n return {\n isValid: false,\n message: \"郵遞區號格式必須為首碼非0的3碼、5碼或6碼數字\",\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣自然人憑證編號\n * 格式:2個大寫英文字母 + 14位數字\n * 範例:AB12345678901234\n *\n * @param certNumber - 要驗證的自然人憑證編號\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateCitizenCertificate('AB12345678901234');\n * ```\n */\nexport function validateCitizenCertificate(\n certNumber: string,\n): ValidationResult {\n if (!certNumber || typeof certNumber !== \"string\") {\n return {\n isValid: false,\n message: \"自然人憑證編號必須為非空字串\",\n };\n }\n\n const normalized = certNumber.trim().toUpperCase();\n\n // 檢查格式:2個字母 + 14位數字\n const pattern = /^[A-Z]{2}\\d{14}$/;\n\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"自然人憑證編號必須為2個英文字母加上14位數字\",\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣國民健康保險卡(健保卡)卡號格式\n * 格式為 12 碼數字(可包含減號或空格,如 0000 1234 5678)\n *\n * @param cardNumber - 要驗證的健保卡卡號\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateNHICard('0000 1234 5678'); // { isValid: true }\n * validateNHICard('000012345678'); // { isValid: true }\n * ```\n */\nexport function validateNHICard(cardNumber: string): ValidationResult {\n if (!cardNumber || typeof cardNumber !== \"string\") {\n return {\n isValid: false,\n message: \"健保卡號必須為非空字串\",\n };\n }\n\n // 去除空格與減號\n const normalized = cardNumber.replace(/[-\\s]/g, \"\");\n\n const pattern = /^\\d{12}$/;\n\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"健保卡號格式必須為12碼數字\",\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證中華民國(台灣)護照號碼格式\n * 晶片護照與一般護照為 9 碼數字格式(晶片護照通常以 3 開頭)\n *\n * @param passport - 要驗證的護照號碼\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validatePassport('312345678'); // { isValid: true }\n * ```\n */\nexport function validatePassport(passport: string): ValidationResult {\n if (!passport || typeof passport !== \"string\") {\n return {\n isValid: false,\n message: \"護照號碼必須為非空字串\",\n };\n }\n\n const normalized = passport.trim();\n\n // 驗證 9 碼數字\n const pattern = /^\\d{9}$/;\n\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"護照號碼格式必須為9位數字\",\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣電子發票手機條碼\n * 格式:/ + 7個字元(大寫英文字母、數字、+、-、.)\n * 範例:/ABCD123\n *\n * 手機條碼用於將電子發票儲存在手機載具中\n *\n * @param barcode - 要驗證的手機條碼\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateEInvoiceMobileBarcode('/ABCD123');\n * validateEInvoiceMobileBarcode('/1234567');\n * ```\n */\nexport function validateEInvoiceMobileBarcode(\n barcode: string,\n): ValidationResult {\n if (!barcode || typeof barcode !== \"string\") {\n return {\n isValid: false,\n message: \"手機條碼必須為非空字串\",\n };\n }\n\n const normalized = barcode.trim().toUpperCase();\n\n // 檢查格式:以 / 開頭,後接7個字元\n // 有效字元:A-Z、0-9、+、-、.\n const pattern = /^\\/[A-Z0-9+.-]{7}$/;\n\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"手機條碼必須以 / 開頭,後接7個有效字元(A-Z、0-9、+、-、.)\",\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 驗證台灣電子發票捐贈碼\n * 格式:3-7位數字\n * 範例:123、12345、1234567\n *\n * 捐贈碼用於將電子發票捐贈給已註冊的慈善機構\n *\n * @param code - 要驗證的捐贈碼\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * validateEInvoiceDonationCode('123');\n * validateEInvoiceDonationCode('12345');\n * ```\n */\nexport function validateEInvoiceDonationCode(code: string): ValidationResult {\n if (!code || typeof code !== \"string\") {\n return {\n isValid: false,\n message: \"捐贈碼必須為非空字串\",\n };\n }\n\n const normalized = code.trim();\n\n // 檢查格式:3-7位數字\n const pattern = /^\\d{3,7}$/;\n\n if (!pattern.test(normalized)) {\n return {\n isValid: false,\n message: \"捐贈碼必須為3至7位數字\",\n };\n }\n\n return {\n isValid: true,\n };\n}\n","import type { ValidationResult } from \"../types\";\n\n/**\n * 車牌類型\n */\nexport type LicensePlateType =\n | \"car\" // 汽車(新制)\n | \"car-old\" // 汽車(舊制)\n | \"electric-car\" // 電動汽車\n | \"motorcycle-small\" // 小型機車(50cc以下)\n | \"motorcycle\"; // 一般機車\n\n/**\n * 車牌驗證結果(含車牌類型資訊)\n */\nexport interface LicensePlateValidationResult extends ValidationResult {\n plateType?: LicensePlateType | undefined;\n}\n\n/**\n * 驗證新制汽車車牌(2012年12月後)\n * 格式:3個英文字母 - 4個數字\n * 不使用字母 I、O\n * 不使用數字 4\n */\nfunction validateNewCarPlate(plate: string): boolean {\n // 格式:3個英文字母-4個數字(例如:ABC-1234)\n const pattern = /^[A-HJ-NP-Z]{3}-\\d{4}$/;\n if (!pattern.test(plate)) {\n return false;\n }\n\n // 檢查數字部分不包含 4\n const numbers = plate.split(\"-\")[1] as string;\n return !numbers.includes(\"4\");\n}\n\n/**\n * 驗證舊制汽車車牌(1992-2012)\n * 格式:1個數字 + 1個英文字母 - 4個數字\n */\nfunction validateOldCarPlate(plate: string): boolean {\n // 格式:1個數字+1個英文字母-4個數字(例如:1A-2345)\n const pattern = /^\\d[A-Z]-\\d{4}$/;\n return pattern.test(plate);\n}\n\n/**\n * 驗證電動汽車車牌\n * 格式:E + 2個英文字母 - 4個數字\n * 不使用字母 I、O\n * 不使用數字 4\n */\nfunction validateElectricCarPlate(plate: string): boolean {\n // 格式:E + 2個英文字母-4個數字(例如:EAB-1234)\n const pattern = /^E[A-HJ-NP-Z]{2}-\\d{4}$/;\n if (!pattern.test(plate)) {\n return false;\n }\n\n // 檢查數字部分不包含 4\n const numbers = plate.split(\"-\")[1] as string;\n return !numbers.includes(\"4\");\n}\n\n/**\n * 驗證小型機車車牌(50cc以下)\n * 格式:3個數字-3個英文字母 或 3個英文字母-3個數字\n */\nfunction validateSmallMotorcyclePlate(plate: string): boolean {\n // 格式1:3個數字-3個英文字母(例如:123-ABC)\n // 格式2:3個英文字母-3個數字(例如:ABC-123)\n const pattern1 = /^\\d{3}-[A-Z]{3}$/;\n const pattern2 = /^[A-Z]{3}-\\d{3}$/;\n return pattern1.test(plate) || pattern2.test(plate);\n}\n\n/**\n * 驗證一般機車車牌(舊制,50-250cc)\n * 格式:2個英文字母 + 1個數字 - 3個數字\n */\nfunction validateMotorcyclePlate(plate: string): boolean {\n // 格式:2個英文字母+1個數字-3個數字(例如:AB1-234)\n const pattern = /^[A-Z]{2}\\d-\\d{3}$/;\n return pattern.test(plate);\n}\n\n/**\n * 偵測車牌類型\n */\nfunction detectPlateType(plate: string): LicensePlateType | null {\n if (validateElectricCarPlate(plate)) {\n return \"electric-car\";\n }\n if (validateNewCarPlate(plate)) {\n return \"car\";\n }\n if (validateOldCarPlate(plate)) {\n return \"car-old\";\n }\n if (validateSmallMotorcyclePlate(plate)) {\n return \"motorcycle-small\";\n }\n if (validateMotorcyclePlate(plate)) {\n return \"motorcycle\";\n }\n return null;\n}\n\n/**\n * 驗證台灣車牌號碼\n * @param plate - 要驗證的車牌號碼\n * @param options - 驗證選項\n * @param options.type - 可選:指定車牌類型\n * @param options.detectType - 是否偵測車牌類型(預設:true)\n * @returns 驗證結果\n *\n * @example\n * ```typescript\n * // 新制汽車\n * validateLicensePlate('ABC-1235');\n *\n * // 電動汽車\n * validateLicensePlate('EAB-1235');\n *\n * // 舊制汽車\n * validateLicensePlate('1A-2345');\n *\n * // 小型機車\n * validateLicensePlate('123-ABC');\n * validateLicensePlate('ABC-123');\n *\n * // 一般機車\n * validateLicensePlate('AB1-234');\n *\n * // 指定類型驗證\n * validateLicensePlate('ABC-1235', { type: 'car' });\n *\n * // 不偵測類型\n * validateLicensePlate('ABC-1235', { detectType: false });\n * ```\n */\nexport function validateLicensePlate(\n plate: string,\n options: { type?: LicensePlateType; detectType?: boolean } = {},\n): LicensePlateValidationResult {\n const { type, detectType = true } = options;\n\n if (!plate || typeof plate !== \"string\") {\n return {\n isValid: false,\n message: \"車牌號碼必須為非空字串\",\n };\n }\n\n // 移除空格並轉換為大寫\n const normalized = plate.trim().toUpperCase();\n\n // 如果指定了類型,只驗證該類型\n if (type) {\n let isValid = false;\n switch (type) {\n case \"car\":\n isValid = validateNewCarPlate(normalized);\n break;\n case \"car-old\":\n isValid = validateOldCarPlate(normalized);\n break;\n case \"electric-car\":\n isValid = validateElectricCarPlate(normalized);\n break;\n case \"motorcycle-small\":\n isValid = validateSmallMotorcyclePlate(normalized);\n break;\n case \"motorcycle\":\n isValid = validateMotorcyclePlate(normalized);\n break;\n }\n\n return {\n isValid,\n message: isValid ? undefined : `無效的${getPlateTypeName(type)}車牌號碼`,\n plateType: isValid && detectType ? type : undefined,\n };\n }\n\n // 自動偵測類型\n const detectedType = detectPlateType(normalized);\n\n if (!detectedType) {\n return {\n isValid: false,\n message: \"無效的車牌號碼格式\",\n };\n }\n\n return {\n isValid: true,\n plateType: detectType ? detectedType : undefined,\n };\n}\n\n/**\n * 取得車牌類型的中文名稱\n */\nfunction getPlateTypeName(type: LicensePlateType): string {\n const typeNames: Record<LicensePlateType, string> = {\n car: \"汽車\",\n \"car-old\": \"汽車(舊制)\",\n \"electric-car\": \"電動汽車\",\n \"motorcycle-small\": \"小型機車\",\n motorcycle: \"機車\",\n };\n return typeNames[type];\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -5,17 +5,43 @@ interface ValidationResult {
|
|
|
5
5
|
type Gender = "male" | "female";
|
|
6
6
|
type ResidentCertificateType = "old" | "new";
|
|
7
7
|
type NationalIdType = "old" | "new";
|
|
8
|
+
interface NationalIdInfo {
|
|
9
|
+
isValid: boolean;
|
|
10
|
+
gender?: Gender;
|
|
11
|
+
region?: string;
|
|
12
|
+
message?: string;
|
|
13
|
+
}
|
|
14
|
+
interface ResidentCertificateInfo {
|
|
15
|
+
isValid: boolean;
|
|
16
|
+
format?: ResidentCertificateType;
|
|
17
|
+
gender?: Gender;
|
|
18
|
+
region?: string;
|
|
19
|
+
identityType?: "non-citizen" | "foreigner";
|
|
20
|
+
message?: string;
|
|
21
|
+
}
|
|
8
22
|
|
|
9
|
-
declare function validateNationalId(id: string,
|
|
23
|
+
declare function validateNationalId(id: string, _format?: NationalIdType): ValidationResult;
|
|
24
|
+
declare function parseNationalId(id: string): NationalIdInfo;
|
|
10
25
|
|
|
11
26
|
declare function validateBusinessNumber(number: string): ValidationResult;
|
|
12
27
|
|
|
28
|
+
declare function validateUniformInvoice(invoice: string): ValidationResult;
|
|
29
|
+
|
|
13
30
|
declare function validateResidentCertificate(id: string, format?: ResidentCertificateType): ValidationResult;
|
|
31
|
+
declare function parseResidentCertificate(id: string): ResidentCertificateInfo;
|
|
14
32
|
|
|
15
33
|
declare function validateMobilePhone(phone: string): ValidationResult;
|
|
16
34
|
|
|
35
|
+
declare function validateLandlinePhone(phone: string): ValidationResult;
|
|
36
|
+
|
|
37
|
+
declare function validatePostalCode(code: string | number): ValidationResult;
|
|
38
|
+
|
|
17
39
|
declare function validateCitizenCertificate(certNumber: string): ValidationResult;
|
|
18
40
|
|
|
41
|
+
declare function validateNHICard(cardNumber: string): ValidationResult;
|
|
42
|
+
|
|
43
|
+
declare function validatePassport(passport: string): ValidationResult;
|
|
44
|
+
|
|
19
45
|
declare function validateEInvoiceMobileBarcode(barcode: string): ValidationResult;
|
|
20
46
|
|
|
21
47
|
declare function validateEInvoiceDonationCode(code: string): ValidationResult;
|
|
@@ -29,4 +55,4 @@ declare function validateLicensePlate(plate: string, options?: {
|
|
|
29
55
|
detectType?: boolean;
|
|
30
56
|
}): LicensePlateValidationResult;
|
|
31
57
|
|
|
32
|
-
export { type Gender, type LicensePlateType, type LicensePlateValidationResult, type NationalIdType, type ResidentCertificateType, type ValidationResult, validateBusinessNumber, validateCitizenCertificate, validateEInvoiceDonationCode, validateEInvoiceMobileBarcode, validateLicensePlate, validateMobilePhone, validateNationalId, validateResidentCertificate };
|
|
58
|
+
export { type Gender, type LicensePlateType, type LicensePlateValidationResult, type NationalIdInfo, type NationalIdType, type ResidentCertificateInfo, type ResidentCertificateType, type ValidationResult, parseNationalId, parseResidentCertificate, validateBusinessNumber, validateCitizenCertificate, validateEInvoiceDonationCode, validateEInvoiceMobileBarcode, validateLandlinePhone, validateLicensePlate, validateMobilePhone, validateNHICard, validateNationalId, validatePassport, validatePostalCode, validateResidentCertificate, validateUniformInvoice };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,17 +5,43 @@ interface ValidationResult {
|
|
|
5
5
|
type Gender = "male" | "female";
|
|
6
6
|
type ResidentCertificateType = "old" | "new";
|
|
7
7
|
type NationalIdType = "old" | "new";
|
|
8
|
+
interface NationalIdInfo {
|
|
9
|
+
isValid: boolean;
|
|
10
|
+
gender?: Gender;
|
|
11
|
+
region?: string;
|
|
12
|
+
message?: string;
|
|
13
|
+
}
|
|
14
|
+
interface ResidentCertificateInfo {
|
|
15
|
+
isValid: boolean;
|
|
16
|
+
format?: ResidentCertificateType;
|
|
17
|
+
gender?: Gender;
|
|
18
|
+
region?: string;
|
|
19
|
+
identityType?: "non-citizen" | "foreigner";
|
|
20
|
+
message?: string;
|
|
21
|
+
}
|
|
8
22
|
|
|
9
|
-
declare function validateNationalId(id: string,
|
|
23
|
+
declare function validateNationalId(id: string, _format?: NationalIdType): ValidationResult;
|
|
24
|
+
declare function parseNationalId(id: string): NationalIdInfo;
|
|
10
25
|
|
|
11
26
|
declare function validateBusinessNumber(number: string): ValidationResult;
|
|
12
27
|
|
|
28
|
+
declare function validateUniformInvoice(invoice: string): ValidationResult;
|
|
29
|
+
|
|
13
30
|
declare function validateResidentCertificate(id: string, format?: ResidentCertificateType): ValidationResult;
|
|
31
|
+
declare function parseResidentCertificate(id: string): ResidentCertificateInfo;
|
|
14
32
|
|
|
15
33
|
declare function validateMobilePhone(phone: string): ValidationResult;
|
|
16
34
|
|
|
35
|
+
declare function validateLandlinePhone(phone: string): ValidationResult;
|
|
36
|
+
|
|
37
|
+
declare function validatePostalCode(code: string | number): ValidationResult;
|
|
38
|
+
|
|
17
39
|
declare function validateCitizenCertificate(certNumber: string): ValidationResult;
|
|
18
40
|
|
|
41
|
+
declare function validateNHICard(cardNumber: string): ValidationResult;
|
|
42
|
+
|
|
43
|
+
declare function validatePassport(passport: string): ValidationResult;
|
|
44
|
+
|
|
19
45
|
declare function validateEInvoiceMobileBarcode(barcode: string): ValidationResult;
|
|
20
46
|
|
|
21
47
|
declare function validateEInvoiceDonationCode(code: string): ValidationResult;
|
|
@@ -29,4 +55,4 @@ declare function validateLicensePlate(plate: string, options?: {
|
|
|
29
55
|
detectType?: boolean;
|
|
30
56
|
}): LicensePlateValidationResult;
|
|
31
57
|
|
|
32
|
-
export { type Gender, type LicensePlateType, type LicensePlateValidationResult, type NationalIdType, type ResidentCertificateType, type ValidationResult, validateBusinessNumber, validateCitizenCertificate, validateEInvoiceDonationCode, validateEInvoiceMobileBarcode, validateLicensePlate, validateMobilePhone, validateNationalId, validateResidentCertificate };
|
|
58
|
+
export { type Gender, type LicensePlateType, type LicensePlateValidationResult, type NationalIdInfo, type NationalIdType, type ResidentCertificateInfo, type ResidentCertificateType, type ValidationResult, parseNationalId, parseResidentCertificate, validateBusinessNumber, validateCitizenCertificate, validateEInvoiceDonationCode, validateEInvoiceMobileBarcode, validateLandlinePhone, validateLicensePlate, validateMobilePhone, validateNHICard, validateNationalId, validatePassport, validatePostalCode, validateResidentCertificate, validateUniformInvoice };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var d={A:10,B:11,C:12,D:13,E:14,F:15,G:16,H:17,I:34,J:18,K:19,L:20,M:21,N:22,O:35,P:23,Q:24,R:25,S:26,T:27,U:28,V:29,W:32,X:30,Y:31,Z:33},p={A:"\u81FA\u5317\u5E02",B:"\u81FA\u4E2D\u5E02",C:"\u57FA\u9686\u5E02",D:"\u81FA\u5357\u5E02",E:"\u9AD8\u96C4\u5E02",F:"\u65B0\u5317\u5E02",G:"\u5B9C\u862D\u7E23",H:"\u6843\u5712\u5E02",I:"\u5609\u7FA9\u5E02",J:"\u65B0\u7AF9\u7E23",K:"\u82D7\u6817\u7E23",L:"\u81FA\u4E2D\u7E23",M:"\u5357\u6295\u7E23",N:"\u5F70\u5316\u7E23",O:"\u65B0\u7AF9\u5E02",P:"\u96F2\u6797\u7E23",Q:"\u5609\u7FA9\u7E23",R:"\u81FA\u5357\u7E23",S:"\u9AD8\u96C4\u7E23",T:"\u5C4F\u6771\u7E23",U:"\u82B1\u84EE\u7E23",V:"\u81FA\u6771\u7E23",W:"\u91D1\u9580\u7E23",X:"\u6F8E\u6E56\u7E23",Y:"\u967D\u660E\u5C71\u7BA1\u7406\u5C40",Z:"\u9023\u6C5F\u7E23"};function y(t){if(!/^[A-Z][12]\d{8}$/.test(t))return false;let i=t[0],s=t.slice(1),r=d[i],n=Math.floor(r/10),a=r%10,o=[1,9,8,7,6,5,4,3,2,1,1];return [n,a,...s.split("").map(Number)].reduce((c,f,u)=>c+f*o[u],0)%10===0}function A(t,e){if(!t||typeof t!="string")return {isValid:false,message:"\u8EAB\u5206\u8B49\u5B57\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let i=t.trim().toUpperCase(),s=y(i);return {isValid:s,message:s?void 0:"\u7121\u6548\u7684\u8EAB\u5206\u8B49\u5B57\u865F"}}function b(t){if(!t||typeof t!="string")return {isValid:false,message:"\u8EAB\u5206\u8B49\u5B57\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim().toUpperCase();if(!y(e))return {isValid:false,message:"\u7121\u6548\u7684\u8EAB\u5206\u8B49\u5B57\u865F"};let s=e[0],n=e[1]==="1"?"male":"female",a=p[s];return {isValid:true,gender:n,region:a}}function h(t){if(!t||typeof t!="string")return {isValid:false,message:"\u7D71\u4E00\u7DE8\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim();if(!/^\d{8}$/.test(e))return {isValid:false,message:"\u7D71\u4E00\u7DE8\u865F\u5FC5\u9808\u70BA8\u4F4D\u6578\u5B57"};let s=e.split("").map(Number),r=[1,2,1,2,1,2,4,1],n=0;for(let o=0;o<8;o++){let l=s[o]*r[o];l>=10&&(l=Math.floor(l/10)+l%10),n+=l;}let a=n%10===0||s[6]===7&&n%10===1;return {isValid:a,message:a?void 0:"\u7D71\u4E00\u7DE8\u865F\u6AA2\u67E5\u78BC\u932F\u8AA4"}}function $(t){if(!t||typeof t!="string")return {isValid:false,message:"\u767C\u7968\u865F\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.replace(/[-\s]/g,"").toUpperCase();return /^[A-Z]{2}\d{8}$/.test(e)?{isValid:true}:{isValid:false,message:"\u767C\u7968\u865F\u78BC\u683C\u5F0F\u5FC5\u9808\u70BA2\u78BC\u82F1\u6587\u82078\u78BC\u6578\u5B57"}}function g(t){if(!/^[A-Z][A-D]\d{8}$/.test(t))return false;let i=t[0],s=t[1],r=t.slice(2),n=d[i],a=d[s],o=Math.floor(n/10),l=n%10,m=a%10,c=[1,9,8,7,6,5,4,3,2,1,1];return [o,l,m,...r.split("").map(Number)].reduce((x,N,T)=>x+N*c[T],0)%10===0}function V(t){if(!/^[A-Z][89]\d{8}$/.test(t))return false;let i=t[0],s=t.slice(1),r=d[i],n=Math.floor(r/10),a=r%10,o=[1,9,8,7,6,5,4,3,2,1,1];return [n,a,...s.split("").map(Number)].reduce((c,f,u)=>c+f*o[u],0)%10===0}function R(t){return /^[A-Z][A-D]\d{8}$/.test(t)?"old":/^[A-Z][89]\d{8}$/.test(t)?"new":null}function E(t,e){if(!t||typeof t!="string")return {isValid:false,message:"\u5C45\u7559\u8B49\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let i=t.trim().toUpperCase();if(e==="old"){let n=g(i);return {isValid:n,message:n?void 0:"\u7121\u6548\u7684\u820A\u5F0F\u5C45\u7559\u8B49\u865F"}}if(e==="new"){let n=V(i);return {isValid:n,message:n?void 0:"\u7121\u6548\u7684\u65B0\u5F0F\u5C45\u7559\u8B49\u865F"}}let s=R(i);if(!s)return {isValid:false,message:"\u7121\u6548\u7684\u5C45\u7559\u8B49\u865F\u683C\u5F0F"};let r=s==="old"?g(i):V(i);return {isValid:r,message:r?void 0:`\u7121\u6548\u7684${s==="old"?"\u820A\u5F0F":"\u65B0\u5F0F"}\u5C45\u7559\u8B49\u865F`}}function z(t){if(!t||typeof t!="string")return {isValid:false,message:"\u5C45\u7559\u8B49\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim().toUpperCase(),i=R(e);if(!i)return {isValid:false,message:"\u7121\u6548\u7684\u5C45\u7559\u8B49\u865F\u683C\u5F0F"};if(!(i==="old"?g(e):V(e)))return {isValid:false,message:`\u7121\u6548\u7684${i==="old"?"\u820A\u5F0F":"\u65B0\u5F0F"}\u5C45\u7559\u8B49\u865F`};let r=e[0],n=e[1],a=p[r],o,l;return i==="new"?(o=n==="8"?"male":"female",l="foreigner"):(o=n==="A"||n==="C"?"male":"female",l=n==="A"||n==="B"?"non-citizen":"foreigner"),{isValid:true,format:i,gender:o,region:a,identityType:l}}function M(t){if(!t||typeof t!="string")return {isValid:false,message:"\u624B\u6A5F\u865F\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim().replace(/[\s\-()]/g,"");return /^09\d{8}$/.test(e)?{isValid:true}:{isValid:false,message:"\u624B\u6A5F\u865F\u78BC\u5FC5\u9808\u4EE509\u958B\u982D\u4E14\u70BA10\u4F4D\u6578\u5B57"}}var Z=[{code:"0826",totalLength:9},{code:"0836",totalLength:9},{code:"037",totalLength:9},{code:"049",totalLength:9},{code:"082",totalLength:9},{code:"089",totalLength:9},{code:"02",totalLength:10},{code:"03",totalLength:9},{code:"04",totalLength:10},{code:"05",totalLength:9},{code:"06",totalLength:9},{code:"07",totalLength:9},{code:"08",totalLength:9}];function G(t){if(!t||typeof t!="string")return {isValid:false,message:"\u96FB\u8A71\u865F\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.replace(/[^\d]/g,""),i=Z.find(s=>e.startsWith(s.code));return i?e.length!==i.totalLength?{isValid:false,message:`\u8A72\u5340\u78BC\u7684\u96FB\u8A71\u865F\u78BC\u9577\u5EA6\u61C9\u70BA ${i.totalLength} \u78BC`}:{isValid:true}:{isValid:false,message:"\u7121\u6548\u7684\u53F0\u7063\u5E02\u8A71\u5340\u78BC"}}function U(t){if(t==null)return {isValid:false,message:"\u90F5\u905E\u5340\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32\u6216\u6578\u5B57"};let e=String(t).trim().replace(/[-\s]/g,"");return /^[1-9]\d{2}$|^[1-9]\d{4}$|^[1-9]\d{5}$/.test(e)?{isValid:true}:{isValid:false,message:"\u90F5\u905E\u5340\u865F\u683C\u5F0F\u5FC5\u9808\u70BA\u9996\u78BC\u975E0\u76843\u78BC\u30015\u78BC\u62166\u78BC\u6578\u5B57"}}function w(t){if(!t||typeof t!="string")return {isValid:false,message:"\u81EA\u7136\u4EBA\u6191\u8B49\u7DE8\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim().toUpperCase();return /^[A-Z]{2}\d{14}$/.test(e)?{isValid:true}:{isValid:false,message:"\u81EA\u7136\u4EBA\u6191\u8B49\u7DE8\u865F\u5FC5\u9808\u70BA2\u500B\u82F1\u6587\u5B57\u6BCD\u52A0\u4E0A14\u4F4D\u6578\u5B57"}}function D(t){if(!t||typeof t!="string")return {isValid:false,message:"\u5065\u4FDD\u5361\u865F\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.replace(/[-\s]/g,"");return /^\d{12}$/.test(e)?{isValid:true}:{isValid:false,message:"\u5065\u4FDD\u5361\u865F\u683C\u5F0F\u5FC5\u9808\u70BA12\u78BC\u6578\u5B57"}}function O(t){if(!t||typeof t!="string")return {isValid:false,message:"\u8B77\u7167\u865F\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim();return /^\d{9}$/.test(e)?{isValid:true}:{isValid:false,message:"\u8B77\u7167\u865F\u78BC\u683C\u5F0F\u5FC5\u9808\u70BA9\u4F4D\u6578\u5B57"}}function _(t){if(!t||typeof t!="string")return {isValid:false,message:"\u624B\u6A5F\u689D\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim().toUpperCase();return /^\/[A-Z0-9+.-]{7}$/.test(e)?{isValid:true}:{isValid:false,message:"\u624B\u6A5F\u689D\u78BC\u5FC5\u9808\u4EE5 / \u958B\u982D\uFF0C\u5F8C\u63A57\u500B\u6709\u6548\u5B57\u5143\uFF08A-Z\u30010-9\u3001+\u3001-\u3001.\uFF09"}}function B(t){if(!t||typeof t!="string")return {isValid:false,message:"\u6350\u8D08\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let e=t.trim();return /^\d{3,7}$/.test(e)?{isValid:true}:{isValid:false,message:"\u6350\u8D08\u78BC\u5FC5\u9808\u70BA3\u81F37\u4F4D\u6578\u5B57"}}function P(t){return /^[A-HJ-NP-Z]{3}-\d{4}$/.test(t)?!t.split("-")[1].includes("4"):false}function L(t){return /^\d[A-Z]-\d{4}$/.test(t)}function C(t){return /^E[A-HJ-NP-Z]{2}-\d{4}$/.test(t)?!t.split("-")[1].includes("4"):false}function I(t){let e=/^\d{3}-[A-Z]{3}$/,i=/^[A-Z]{3}-\d{3}$/;return e.test(t)||i.test(t)}function v(t){return /^[A-Z]{2}\d-\d{3}$/.test(t)}function F(t){return C(t)?"electric-car":P(t)?"car":L(t)?"car-old":I(t)?"motorcycle-small":v(t)?"motorcycle":null}function k(t,e={}){let{type:i,detectType:s=true}=e;if(!t||typeof t!="string")return {isValid:false,message:"\u8ECA\u724C\u865F\u78BC\u5FC5\u9808\u70BA\u975E\u7A7A\u5B57\u4E32"};let r=t.trim().toUpperCase();if(i){let a=false;switch(i){case "car":a=P(r);break;case "car-old":a=L(r);break;case "electric-car":a=C(r);break;case "motorcycle-small":a=I(r);break;case "motorcycle":a=v(r);break}return {isValid:a,message:a?void 0:`\u7121\u6548\u7684${H(i)}\u8ECA\u724C\u865F\u78BC`,plateType:a&&s?i:void 0}}let n=F(r);return n?{isValid:true,plateType:s?n:void 0}:{isValid:false,message:"\u7121\u6548\u7684\u8ECA\u724C\u865F\u78BC\u683C\u5F0F"}}function H(t){return {car:"\u6C7D\u8ECA","car-old":"\u6C7D\u8ECA\uFF08\u820A\u5236\uFF09","electric-car":"\u96FB\u52D5\u6C7D\u8ECA","motorcycle-small":"\u5C0F\u578B\u6A5F\u8ECA",motorcycle:"\u6A5F\u8ECA"}[t]}export{b as parseNationalId,z as parseResidentCertificate,h as validateBusinessNumber,w as validateCitizenCertificate,B as validateEInvoiceDonationCode,_ as validateEInvoiceMobileBarcode,G as validateLandlinePhone,k as validateLicensePlate,M as validateMobilePhone,D as validateNHICard,A as validateNationalId,O as validatePassport,U as validatePostalCode,E as validateResidentCertificate,$ as validateUniformInvoice};//# sourceMappingURL=index.mjs.map
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|