zod-ir 1.1.2 → 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.md CHANGED
@@ -6,7 +6,7 @@
6
6
  <p>
7
7
  A lightweight, TypeScript-first extension for Zod.
8
8
  <br />
9
- Compatible with React Hook Form, Next.js, and Node.js.
9
+ Compatible with React Hook Form, Next.js, NestJS, and Node.js.
10
10
  </p>
11
11
 
12
12
  <p>
@@ -27,11 +27,14 @@
27
27
  ## Features ✨
28
28
 
29
29
  - ✅ **National Code:** Validates using the official checksum algorithm.
30
+ - 🏢 **Shenase Melli:** Validates Legal Person ID (Company ID).
30
31
  - 💳 **Bank Card:** Validates 16-digit card numbers (Luhn algorithm).
31
32
  - 📱 **Mobile Number:** Validates `09xx`, `+989xx`, `9xx`.
32
33
  - 🏦 **Sheba (IBAN):** Validates structure and checksum (ISO 7064).
34
+ - ✈️ **Passport:** Validates Iranian Passport numbers.
33
35
  - 📮 **Postal Code:** Validates 10-digit Iranian postal codes.
34
36
  - ☎️ **Landline:** Validates fixed line numbers with area codes.
37
+ - 🔄 **Auto-fix Digits:** Helper to automatically convert Persian/Arabic digits to English.
35
38
  - 🌍 **Bilingual:** Built-in error messages in **Persian** and **English**.
36
39
 
37
40
  ---
@@ -46,51 +49,52 @@ yarn add zod zod-ir
46
49
 
47
50
  ## Usage 🚀
48
51
 
49
- 1. Basic Validation
52
+ 1. Basic Validation & Auto-Fix
53
+ This example shows how to validate a form and automatically convert Persian digits (e.g., ۰۹۱۲) to English.
50
54
 
51
55
  ```typescript
52
56
  import { z } from "zod";
53
57
  import {
54
58
  zMelliCode,
55
- zSheba,
59
+ zShenaseMelli,
56
60
  zIranianMobile,
57
61
  zCardNumber,
58
- zPostalCode,
59
- zLandline,
62
+ zSheba,
63
+ zPassport,
64
+ preprocessNumber,
60
65
  } from "zod-ir";
61
66
 
62
67
  const UserSchema = z.object({
63
- // Default Persian error message
64
- nationalCode: zMelliCode(),
68
+ // 1. National Code with Auto-Fix (Converts ۱۲۳ -> 123)
69
+ nationalCode: preprocessNumber(zMelliCode()),
65
70
 
66
- // Custom error message & Strict mode (must start with 0)
67
- mobile: zIranianMobile({
68
- strictZero: true,
69
- message: "شماره موبایل اشتباه است",
70
- }),
71
+ // 2. Company ID (Shenase Melli)
72
+ companyId: zShenaseMelli({ message: "شناسه ملی نامعتبر است" }),
71
73
 
72
- // English error message for Sheba
73
- iban: zSheba({ locale: "en" }),
74
+ // 3. Mobile (Strict Mode: Must start with 0)
75
+ mobile: zIranianMobile({ strictZero: true }),
74
76
 
75
- // Bank Card
77
+ // 4. Bank Card
76
78
  card: zCardNumber(),
77
79
 
78
- // Postal Code
79
- postal: zPostalCode(),
80
+ // 5. Sheba (IBAN) - English Error
81
+ iban: zSheba({ locale: "en" }),
80
82
 
81
- // Landline (Phone)
82
- phone: zLandline(),
83
+ // 6. Passport
84
+ passport: zPassport(),
83
85
  });
84
86
 
87
+ // Example Usage
85
88
  try {
86
- UserSchema.parse({
87
- nationalCode: "1234567891",
89
+ const result = UserSchema.parse({
90
+ nationalCode: "۱۲۳۴۵۶۷۸۹۱", // User typed in Farsi
91
+ companyId: "10100448712",
88
92
  mobile: "09121234567",
89
- iban: "IR120770000000000000000001",
90
- card: "6037991155667788",
91
- postal: "1234567890",
92
- phone: "02122334455",
93
+ card: "6362147010005732",
94
+ iban: "IR330620000000202901868005",
95
+ passport: "A12345678",
93
96
  });
97
+ console.log("Valid Data:", result);
94
98
  } catch (err) {
95
99
  console.log(err);
96
100
  }
@@ -102,10 +106,11 @@ try {
102
106
  import { useForm } from "react-hook-form";
103
107
  import { zodResolver } from "@hookform/resolvers/zod";
104
108
  import { z } from "zod";
105
- import { zMelliCode } from "zod-ir";
109
+ import { zMelliCode, preprocessNumber } from "zod-ir";
106
110
 
107
111
  const schema = z.object({
108
- nationalId: zMelliCode({ message: "کد ملی صحیح نیست" }),
112
+ // Automatically fixes Persian digits typed by user
113
+ nationalId: preprocessNumber(zMelliCode({ message: "کد ملی صحیح نیست" })),
109
114
  });
110
115
 
111
116
  export default function MyForm() {
@@ -119,8 +124,12 @@ export default function MyForm() {
119
124
 
120
125
  return (
121
126
  <form onSubmit={handleSubmit((d) => console.log(d))}>
122
- <input {...register("nationalId")} placeholder="Code Melli" />
123
- <p>{errors.nationalId?.message}</p>
127
+ <input
128
+ {...register("nationalId")}
129
+ placeholder="کد ملی (حتی فارسی)"
130
+ dir="auto"
131
+ />
132
+ <p style={{ color: "red" }}>{errors.nationalId?.message}</p>
124
133
  <button type="submit">Submit</button>
125
134
  </form>
126
135
  );
@@ -129,23 +138,27 @@ export default function MyForm() {
129
138
 
130
139
  ## API Reference 📚
131
140
 
132
- | Validator | Description | Options |
133
- | :--------------- | :---------------------------------------------------- | :-------------------------------- |
134
- | `zMelliCode` | Validates Iranian National Code (Melli Code) | `message`, `locale` |
135
- | `zCardNumber` | Validates 16-digit bank card numbers (Luhn algorithm) | `message`, `locale` |
136
- | `zIranianMobile` | Validates Iranian mobile numbers | `strictZero`, `message`, `locale` |
137
- | `zSheba` | Validates Sheba (IBAN) structure and checksum | `message`, `locale` |
138
- | `zPostalCode` | Validates 10-digit Iranian postal codes | `message`, `locale` |
139
- | `zLandline` | Validates landline phone numbers with area codes | `message`, `locale` |
141
+ | Validator | Description |
142
+ | :----------------- | :----------------------------------------------------------------- |
143
+ | `zMelliCode` | Validates Iranian National Code (Melli Code). |
144
+ | `zShenaseMelli` | Validates Legal Person ID (Company ID). |
145
+ | `zCardNumber` | Validates 16-digit bank card numbers (Luhn). |
146
+ | `zIranianMobile` | Validates Iranian mobile numbers. |
147
+ | `zSheba` | Validates IBAN (Sheba) structure and checksum. |
148
+ | `zPassport` | Validates Iranian Passport numbers. |
149
+ | `zPostalCode` | Validates 10-digit Iranian postal codes. |
150
+ | `zLandline` | Validates landline phone numbers with area codes. |
151
+ | `preprocessNumber` | Utility: Wraps any validator to convert Persian digits to English. |
140
152
 
141
153
  #### Options Interface
154
+
142
155
  All validators accept an optional configuration object to customize behavior.
143
156
 
144
157
  | Name | Type | Description |
145
158
  | :----------- | :-------------------- | :--------------------------------------------------------- |
146
159
  | `message` | `string` | Custom error message to display when validation fails. |
147
160
  | `locale` | `"fa"`, `"en"` | Language for the default error message (defaults to "fa"). |
148
- | `strictZero` | `boolean`, `optional` | (Mobile Only) Indicates strictness of the leading zero. |
161
+ | `strictZero` | `boolean`, `optional` | (Mobile Only) If true, input must start with 0. |
149
162
 
150
163
  ## License
151
164
 
package/dist/index.d.mts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  declare function isMelliCode(code: string): boolean;
4
+ declare function isShenaseMelli(code: string): boolean;
5
+ declare function isPassport(code: string): boolean;
4
6
  declare function isCardNumber(code: string): boolean;
5
7
  interface MobileValidationOptions {
6
8
  strictZero?: boolean | "optional";
@@ -9,10 +11,13 @@ declare function isIranianMobile(mobile: string, { strictZero }?: MobileValidati
9
11
  declare function isSheba(code: string): boolean;
10
12
  declare function isPostalCode(code: string): boolean;
11
13
  declare function isLandline(code: string): boolean;
14
+ declare function verifyAndNormalize(value: string): string;
12
15
 
13
16
  declare const ERROR_MESSAGES: {
14
17
  readonly fa: {
15
18
  readonly melliCode: "کد ملی نامعتبر است";
19
+ readonly shenaseMelli: "شناسه ملی نامعتبر است";
20
+ readonly passport: "شماره گذرنامه نامعتبر است";
16
21
  readonly cardNumber: "شماره کارت نامعتبر است";
17
22
  readonly mobile: "شماره موبایل نامعتبر است";
18
23
  readonly sheba: "شماره شبا نامعتبر است";
@@ -21,6 +26,8 @@ declare const ERROR_MESSAGES: {
21
26
  };
22
27
  readonly en: {
23
28
  readonly melliCode: "Invalid national code";
29
+ readonly shenaseMelli: "Invalid legal person ID (Shenase Melli)";
30
+ readonly passport: "Invalid passport number";
24
31
  readonly cardNumber: "Invalid card number";
25
32
  readonly mobile: "Invalid mobile number";
26
33
  readonly sheba: "Invalid Sheba (IBAN) number";
@@ -35,6 +42,8 @@ interface BaseOptions {
35
42
  }
36
43
 
37
44
  declare const zMelliCode: (options?: BaseOptions) => z.ZodString;
45
+ declare const zShenaseMelli: (options?: BaseOptions) => z.ZodString;
46
+ declare const zPassport: (options?: BaseOptions) => z.ZodString;
38
47
  declare const zCardNumber: (options?: BaseOptions) => z.ZodString;
39
48
  interface MobileOptions extends BaseOptions {
40
49
  strictZero?: boolean | "optional";
@@ -43,5 +52,6 @@ declare const zIranianMobile: (options?: MobileOptions) => z.ZodString;
43
52
  declare const zSheba: (options?: BaseOptions) => z.ZodString;
44
53
  declare const zPostalCode: (options?: BaseOptions) => z.ZodString;
45
54
  declare const zLandline: (options?: BaseOptions) => z.ZodString;
55
+ declare const preprocessNumber: (schema: z.ZodTypeAny) => z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
46
56
 
47
- export { isCardNumber, isIranianMobile, isLandline, isMelliCode, isPostalCode, isSheba, zCardNumber, zIranianMobile, zLandline, zMelliCode, zPostalCode, zSheba };
57
+ export { isCardNumber, isIranianMobile, isLandline, isMelliCode, isPassport, isPostalCode, isSheba, isShenaseMelli, preprocessNumber, verifyAndNormalize, zCardNumber, zIranianMobile, zLandline, zMelliCode, zPassport, zPostalCode, zSheba, zShenaseMelli };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  declare function isMelliCode(code: string): boolean;
4
+ declare function isShenaseMelli(code: string): boolean;
5
+ declare function isPassport(code: string): boolean;
4
6
  declare function isCardNumber(code: string): boolean;
5
7
  interface MobileValidationOptions {
6
8
  strictZero?: boolean | "optional";
@@ -9,10 +11,13 @@ declare function isIranianMobile(mobile: string, { strictZero }?: MobileValidati
9
11
  declare function isSheba(code: string): boolean;
10
12
  declare function isPostalCode(code: string): boolean;
11
13
  declare function isLandline(code: string): boolean;
14
+ declare function verifyAndNormalize(value: string): string;
12
15
 
13
16
  declare const ERROR_MESSAGES: {
14
17
  readonly fa: {
15
18
  readonly melliCode: "کد ملی نامعتبر است";
19
+ readonly shenaseMelli: "شناسه ملی نامعتبر است";
20
+ readonly passport: "شماره گذرنامه نامعتبر است";
16
21
  readonly cardNumber: "شماره کارت نامعتبر است";
17
22
  readonly mobile: "شماره موبایل نامعتبر است";
18
23
  readonly sheba: "شماره شبا نامعتبر است";
@@ -21,6 +26,8 @@ declare const ERROR_MESSAGES: {
21
26
  };
22
27
  readonly en: {
23
28
  readonly melliCode: "Invalid national code";
29
+ readonly shenaseMelli: "Invalid legal person ID (Shenase Melli)";
30
+ readonly passport: "Invalid passport number";
24
31
  readonly cardNumber: "Invalid card number";
25
32
  readonly mobile: "Invalid mobile number";
26
33
  readonly sheba: "Invalid Sheba (IBAN) number";
@@ -35,6 +42,8 @@ interface BaseOptions {
35
42
  }
36
43
 
37
44
  declare const zMelliCode: (options?: BaseOptions) => z.ZodString;
45
+ declare const zShenaseMelli: (options?: BaseOptions) => z.ZodString;
46
+ declare const zPassport: (options?: BaseOptions) => z.ZodString;
38
47
  declare const zCardNumber: (options?: BaseOptions) => z.ZodString;
39
48
  interface MobileOptions extends BaseOptions {
40
49
  strictZero?: boolean | "optional";
@@ -43,5 +52,6 @@ declare const zIranianMobile: (options?: MobileOptions) => z.ZodString;
43
52
  declare const zSheba: (options?: BaseOptions) => z.ZodString;
44
53
  declare const zPostalCode: (options?: BaseOptions) => z.ZodString;
45
54
  declare const zLandline: (options?: BaseOptions) => z.ZodString;
55
+ declare const preprocessNumber: (schema: z.ZodTypeAny) => z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
46
56
 
47
- export { isCardNumber, isIranianMobile, isLandline, isMelliCode, isPostalCode, isSheba, zCardNumber, zIranianMobile, zLandline, zMelliCode, zPostalCode, zSheba };
57
+ export { isCardNumber, isIranianMobile, isLandline, isMelliCode, isPassport, isPostalCode, isSheba, isShenaseMelli, preprocessNumber, verifyAndNormalize, zCardNumber, zIranianMobile, zLandline, zMelliCode, zPassport, zPostalCode, zSheba, zShenaseMelli };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var l=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var m=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var C=(t,e)=>{for(var n in e)l(t,n,{get:e[n],enumerable:!0})},I=(t,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of m(e))!x.call(t,i)&&i!==n&&l(t,i,{get:()=>e[i],enumerable:!(s=b(e,i))||s.enumerable});return t};var S=t=>I(l({},"__esModule",{value:!0}),t);var z={};C(z,{isCardNumber:()=>d,isIranianMobile:()=>c,isLandline:()=>u,isMelliCode:()=>p,isPostalCode:()=>f,isSheba:()=>g,zCardNumber:()=>h,zIranianMobile:()=>B,zLandline:()=>E,zMelliCode:()=>M,zPostalCode:()=>$,zSheba:()=>R});module.exports=S(z);var a=require("zod");function p(t){if(!/^\d{10}$/.test(t)||/^(\d)\1+$/.test(t))return!1;let e=parseInt(t[9]),n=t.substring(0,9).split("").reduce((s,i,o)=>s+parseInt(i)*(10-o),0)%11;return n<2?e===n:e===11-n}function d(t){let e=t.replace(/[\-\s]/g,"");if(!/^\d{16}$/.test(e))return!1;let n=0,s=!1;for(let i=e.length-1;i>=0;i--){let o=parseInt(e[i]);s&&(o*=2,o>9&&(o-=9)),n+=o,s=!s}return n%10===0}function c(t,{strictZero:e="optional"}={}){let n="9\\d{9}",s="";return e===!0?s=`^0${n}$`:e===!1?s=`^${n}$`:s=`^(?:0|\\+98)?${n}$`,new RegExp(s).test(t)}function g(t){let e=t.toUpperCase().replace(/[\-\s]/g,"");if(e.length!==26||!e.startsWith("IR"))return!1;let s=(e.substring(4)+e.substring(0,4)).split("").map(i=>{let o=i.charCodeAt(0);return o>=48&&o<=57?i:(o-55).toString()}).join("");try{return BigInt(s)%BigInt(97)===BigInt(1)}catch{return!1}}function f(t){return/^[1-9]\d{9}$/.test(t)}function u(t){return/^0\d{2}\d{8}$/.test(t)}var O={fa:{melliCode:"\u06A9\u062F \u0645\u0644\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",cardNumber:"\u0634\u0645\u0627\u0631\u0647 \u06A9\u0627\u0631\u062A \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",mobile:"\u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",sheba:"\u0634\u0645\u0627\u0631\u0647 \u0634\u0628\u0627 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",postalCode:"\u06A9\u062F \u067E\u0633\u062A\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",landline:"\u0634\u0645\u0627\u0631\u0647 \u062A\u0644\u0641\u0646 \u062B\u0627\u0628\u062A \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A"},en:{melliCode:"Invalid national code",cardNumber:"Invalid card number",mobile:"Invalid mobile number",sheba:"Invalid Sheba (IBAN) number",postalCode:"Invalid postal code",landline:"Invalid landline number"}},r=(t,e)=>{if(e?.message)return e.message;let n=e?.locale||"fa";return O[n][t]};var M=t=>a.z.string().refine(e=>p(e),{message:r("melliCode",t)}),h=t=>a.z.string().refine(e=>d(e),{message:r("cardNumber",t)}),B=t=>a.z.string().refine(e=>c(e,{strictZero:t?.strictZero}),{message:r("mobile",t)}),R=t=>a.z.string().refine(e=>g(e),{message:r("sheba",t)}),$=t=>a.z.string().refine(e=>f(e),{message:r("postalCode",t)}),E=t=>a.z.string().refine(e=>u(e),{message:r("landline",t)});0&&(module.exports={isCardNumber,isIranianMobile,isLandline,isMelliCode,isPostalCode,isSheba,zCardNumber,zIranianMobile,zLandline,zMelliCode,zPostalCode,zSheba});
1
+ "use strict";var p=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var O=(t,e)=>{for(var n in e)p(t,n,{get:e[n],enumerable:!0})},B=(t,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of M(e))!C.call(t,r)&&r!==n&&p(t,r,{get:()=>e[r],enumerable:!(s=S(e,r))||s.enumerable});return t};var z=t=>B(p({},"__esModule",{value:!0}),t);var Z={};O(Z,{isCardNumber:()=>u,isIranianMobile:()=>d,isLandline:()=>x,isMelliCode:()=>c,isPassport:()=>f,isPostalCode:()=>m,isSheba:()=>b,isShenaseMelli:()=>g,preprocessNumber:()=>L,verifyAndNormalize:()=>h,zCardNumber:()=>N,zIranianMobile:()=>A,zLandline:()=>k,zMelliCode:()=>y,zPassport:()=>E,zPostalCode:()=>P,zSheba:()=>v,zShenaseMelli:()=>R});module.exports=z(Z);var o=require("zod");function c(t){if(!/^\d{10}$/.test(t)||/^(\d)\1+$/.test(t))return!1;let e=parseInt(t[9]),n=t.substring(0,9).split("").reduce((s,r,i)=>s+parseInt(r)*(10-i),0)%11;return n<2?e===n:e===11-n}function g(t){if(t.length!==11||!/^\d{11}$/.test(t))return!1;let e=parseInt(t[9]),n=parseInt(t[10]),s=[29,27,23,19,17,29,27,23,19,17],r=0;for(let l=0;l<10;l++){let I=parseInt(t[l]);r+=(I+e+2)*s[l]}let i=r%11;return(i===10?0:i)===n}function f(t){return/^[A-Za-z][0-9]{8,9}$/.test(t)}function u(t){let e=t.replace(/[\-\s]/g,"");if(!/^\d{16}$/.test(e))return!1;let n=0,s=!1;for(let r=e.length-1;r>=0;r--){let i=parseInt(e[r]);s&&(i*=2,i>9&&(i-=9)),n+=i,s=!s}return n%10===0}function d(t,{strictZero:e="optional"}={}){let n="9\\d{9}",s="";return e===!0?s=`^0${n}$`:e===!1?s=`^${n}$`:s=`^(?:0|\\+98)?${n}$`,new RegExp(s).test(t)}function b(t){let e=t.toUpperCase().replace(/[\-\s]/g,"");if(e.length!==26||!e.startsWith("IR"))return!1;let s=(e.substring(4)+e.substring(0,4)).split("").map(r=>{let i=r.charCodeAt(0);return i>=48&&i<=57?r:(i-55).toString()}).join("");try{return BigInt(s)%BigInt(97)===BigInt(1)}catch{return!1}}function m(t){return/^[1-9]\d{9}$/.test(t)}function x(t){return/^0\d{2}\d{8}$/.test(t)}function h(t){return t.replace(/[۰-۹]/g,e=>"\u06F0\u06F1\u06F2\u06F3\u06F4\u06F5\u06F6\u06F7\u06F8\u06F9".indexOf(e).toString()).replace(/[٠-٩]/g,e=>"\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669".indexOf(e).toString())}var $={fa:{melliCode:"\u06A9\u062F \u0645\u0644\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",shenaseMelli:"\u0634\u0646\u0627\u0633\u0647 \u0645\u0644\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",passport:"\u0634\u0645\u0627\u0631\u0647 \u06AF\u0630\u0631\u0646\u0627\u0645\u0647 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",cardNumber:"\u0634\u0645\u0627\u0631\u0647 \u06A9\u0627\u0631\u062A \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",mobile:"\u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",sheba:"\u0634\u0645\u0627\u0631\u0647 \u0634\u0628\u0627 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",postalCode:"\u06A9\u062F \u067E\u0633\u062A\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",landline:"\u0634\u0645\u0627\u0631\u0647 \u062A\u0644\u0641\u0646 \u062B\u0627\u0628\u062A \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A"},en:{melliCode:"Invalid national code",shenaseMelli:"Invalid legal person ID (Shenase Melli)",passport:"Invalid passport number",cardNumber:"Invalid card number",mobile:"Invalid mobile number",sheba:"Invalid Sheba (IBAN) number",postalCode:"Invalid postal code",landline:"Invalid landline number"}},a=(t,e)=>{if(e?.message)return e.message;let n=e?.locale||"fa";return $[n][t]};var y=t=>o.z.string().refine(e=>c(e),{message:a("melliCode",t)}),R=t=>o.z.string().refine(e=>g(e),{message:a("shenaseMelli",t)}),E=t=>o.z.string().refine(e=>f(e),{message:a("passport",t)}),N=t=>o.z.string().refine(e=>u(e),{message:a("cardNumber",t)}),A=t=>o.z.string().refine(e=>d(e,{strictZero:t?.strictZero}),{message:a("mobile",t)}),v=t=>o.z.string().refine(e=>b(e),{message:a("sheba",t)}),P=t=>o.z.string().refine(e=>m(e),{message:a("postalCode",t)}),k=t=>o.z.string().refine(e=>x(e),{message:a("landline",t)}),L=t=>o.z.preprocess(e=>typeof e=="string"?h(e):e,t);0&&(module.exports={isCardNumber,isIranianMobile,isLandline,isMelliCode,isPassport,isPostalCode,isSheba,isShenaseMelli,preprocessNumber,verifyAndNormalize,zCardNumber,zIranianMobile,zLandline,zMelliCode,zPassport,zPostalCode,zSheba,zShenaseMelli});
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/constants.ts"],"sourcesContent":["import { z } from \"zod\";\nimport {\n isMelliCode,\n isCardNumber,\n isIranianMobile,\n isSheba,\n isPostalCode,\n isLandline,\n} from \"./utils\";\nimport { getMessage, BaseOptions } from \"./constants\";\n\nexport const zMelliCode = (options?: BaseOptions) =>\n z.string().refine((val) => isMelliCode(val), {\n message: getMessage(\"melliCode\", options),\n });\n\nexport const zCardNumber = (options?: BaseOptions) =>\n z.string().refine((val) => isCardNumber(val), {\n message: getMessage(\"cardNumber\", options),\n });\n\ninterface MobileOptions extends BaseOptions {\n strictZero?: boolean | \"optional\";\n}\n\nexport const zIranianMobile = (options?: MobileOptions) =>\n z\n .string()\n .refine(\n (val) => isIranianMobile(val, { strictZero: options?.strictZero }),\n {\n message: getMessage(\"mobile\", options),\n }\n );\n\nexport const zSheba = (options?: BaseOptions) =>\n z.string().refine((val) => isSheba(val), {\n message: getMessage(\"sheba\", options),\n });\n\nexport const zPostalCode = (options?: BaseOptions) =>\n z.string().refine((val) => isPostalCode(val), {\n message: getMessage(\"postalCode\", options),\n });\n\nexport const zLandline = (options?: BaseOptions) =>\n z.string().refine((val) => isLandline(val), {\n message: getMessage(\"landline\", options),\n });\n\nexport {\n isMelliCode,\n isCardNumber,\n isIranianMobile,\n isSheba,\n isPostalCode,\n isLandline,\n};\n","export function isMelliCode(code: string): boolean {\n if (!/^\\d{10}$/.test(code)) return false;\n if (/^(\\d)\\1+$/.test(code)) return false;\n\n const check = parseInt(code[9]);\n const sum =\n code\n .substring(0, 9)\n .split(\"\")\n .reduce((acc, x, i) => acc + parseInt(x) * (10 - i), 0) % 11;\n\n return sum < 2 ? check === sum : check === 11 - sum;\n}\n\nexport function isCardNumber(code: string): boolean {\n const sanitized = code.replace(/[\\-\\s]/g, \"\");\n if (!/^\\d{16}$/.test(sanitized)) return false;\n\n let sum = 0;\n let shouldDouble = false;\n\n for (let i = sanitized.length - 1; i >= 0; i--) {\n let digit = parseInt(sanitized[i]);\n\n if (shouldDouble) {\n digit *= 2;\n if (digit > 9) digit -= 9;\n }\n\n sum += digit;\n shouldDouble = !shouldDouble;\n }\n\n return sum % 10 === 0;\n}\n\ninterface MobileValidationOptions {\n strictZero?: boolean | \"optional\";\n}\n\nexport function isIranianMobile(\n mobile: string,\n { strictZero = \"optional\" }: MobileValidationOptions = {}\n): boolean {\n const corePattern = \"9\\\\d{9}\";\n let pattern = \"\";\n\n if (strictZero === true) {\n pattern = `^0${corePattern}$`;\n } else if (strictZero === false) {\n pattern = `^${corePattern}$`;\n } else {\n pattern = `^(?:0|\\\\+98)?${corePattern}$`;\n }\n\n return new RegExp(pattern).test(mobile);\n}\n\nexport function isSheba(code: string): boolean {\n const iban = code.toUpperCase().replace(/[\\-\\s]/g, \"\");\n\n if (iban.length !== 26 || !iban.startsWith(\"IR\")) return false;\n\n const newStr = iban.substring(4) + iban.substring(0, 4);\n\n const numericString = newStr\n .split(\"\")\n .map((char) => {\n const code = char.charCodeAt(0);\n return code >= 48 && code <= 57 ? char : (code - 55).toString();\n })\n .join(\"\");\n\n try {\n const remainder = BigInt(numericString) % BigInt(97);\n return remainder === BigInt(1);\n } catch {\n return false;\n }\n}\n\nexport function isPostalCode(code: string): boolean {\n return /^[1-9]\\d{9}$/.test(code);\n}\n\nexport function isLandline(code: string): boolean {\n return /^0\\d{2}\\d{8}$/.test(code);\n}\n","export const ERROR_MESSAGES = {\n fa: {\n melliCode: \"کد ملی نامعتبر است\",\n cardNumber: \"شماره کارت نامعتبر است\",\n mobile: \"شماره موبایل نامعتبر است\",\n sheba: \"شماره شبا نامعتبر است\",\n postalCode: \"کد پستی نامعتبر است\",\n landline: \"شماره تلفن ثابت نامعتبر است\",\n },\n en: {\n melliCode: \"Invalid national code\",\n cardNumber: \"Invalid card number\",\n mobile: \"Invalid mobile number\",\n sheba: \"Invalid Sheba (IBAN) number\",\n postalCode: \"Invalid postal code\",\n landline: \"Invalid landline number\",\n },\n} as const;\n\nexport type Language = keyof typeof ERROR_MESSAGES;\n\nexport interface BaseOptions {\n message?: string;\n locale?: Language;\n}\n\nexport const getMessage = (\n key: keyof (typeof ERROR_MESSAGES)[\"fa\"],\n options?: BaseOptions\n): string => {\n if (options?.message) return options.message;\n const lang = options?.locale || \"fa\";\n return ERROR_MESSAGES[lang][key];\n};\n"],"mappings":"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","names":["index_exports","__export","isCardNumber","isIranianMobile","isLandline","isMelliCode","isPostalCode","isSheba","zCardNumber","zIranianMobile","zLandline","zMelliCode","zPostalCode","zSheba","__toCommonJS","import_zod","isMelliCode","code","check","sum","acc","x","i","isCardNumber","sanitized","shouldDouble","digit","isIranianMobile","mobile","strictZero","corePattern","pattern","isSheba","iban","numericString","char","isPostalCode","isLandline","ERROR_MESSAGES","getMessage","key","options","lang","zMelliCode","options","val","isMelliCode","getMessage","zCardNumber","isCardNumber","zIranianMobile","isIranianMobile","zSheba","isSheba","zPostalCode","isPostalCode","zLandline","isLandline"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/constants.ts"],"sourcesContent":["import { z } from \"zod\";\nimport {\n isMelliCode,\n isShenaseMelli,\n isPassport,\n isCardNumber,\n isIranianMobile,\n isSheba,\n isPostalCode,\n isLandline,\n verifyAndNormalize,\n} from \"./utils\";\nimport { getMessage, BaseOptions } from \"./constants\";\n\nexport const zMelliCode = (options?: BaseOptions) =>\n z.string().refine((val) => isMelliCode(val), {\n message: getMessage(\"melliCode\", options),\n });\n\nexport const zShenaseMelli = (options?: BaseOptions) =>\n z.string().refine((val) => isShenaseMelli(val), {\n message: getMessage(\"shenaseMelli\", options),\n });\n\nexport const zPassport = (options?: BaseOptions) =>\n z.string().refine((val) => isPassport(val), {\n message: getMessage(\"passport\", options),\n });\n\nexport const zCardNumber = (options?: BaseOptions) =>\n z.string().refine((val) => isCardNumber(val), {\n message: getMessage(\"cardNumber\", options),\n });\n\ninterface MobileOptions extends BaseOptions {\n strictZero?: boolean | \"optional\";\n}\n\nexport const zIranianMobile = (options?: MobileOptions) =>\n z\n .string()\n .refine(\n (val) => isIranianMobile(val, { strictZero: options?.strictZero }),\n {\n message: getMessage(\"mobile\", options),\n }\n );\n\nexport const zSheba = (options?: BaseOptions) =>\n z.string().refine((val) => isSheba(val), {\n message: getMessage(\"sheba\", options),\n });\n\nexport const zPostalCode = (options?: BaseOptions) =>\n z.string().refine((val) => isPostalCode(val), {\n message: getMessage(\"postalCode\", options),\n });\n\nexport const zLandline = (options?: BaseOptions) =>\n z.string().refine((val) => isLandline(val), {\n message: getMessage(\"landline\", options),\n });\n\nexport const preprocessNumber = (schema: z.ZodTypeAny) =>\n z.preprocess((val) => {\n if (typeof val === \"string\") {\n return verifyAndNormalize(val);\n }\n return val;\n }, schema);\n\nexport {\n isMelliCode,\n isShenaseMelli,\n isPassport,\n isCardNumber,\n isIranianMobile,\n isSheba,\n isPostalCode,\n isLandline,\n verifyAndNormalize,\n};\n","export function isMelliCode(code: string): boolean {\n if (!/^\\d{10}$/.test(code)) return false;\n if (/^(\\d)\\1+$/.test(code)) return false;\n\n const check = parseInt(code[9]);\n const sum =\n code\n .substring(0, 9)\n .split(\"\")\n .reduce((acc, x, i) => acc + parseInt(x) * (10 - i), 0) % 11;\n\n return sum < 2 ? check === sum : check === 11 - sum;\n}\n\nexport function isShenaseMelli(code: string): boolean {\n if (code.length !== 11 || !/^\\d{11}$/.test(code)) return false;\n\n const tenth = parseInt(code[9]);\n const inputCheck = parseInt(code[10]);\n const coefficients = [29, 27, 23, 19, 17, 29, 27, 23, 19, 17];\n\n let sum = 0;\n for (let i = 0; i < 10; i++) {\n const digit = parseInt(code[i]);\n sum += (digit + tenth + 2) * coefficients[i];\n }\n\n const remainder = sum % 11;\n const calculatedCheck = remainder === 10 ? 0 : remainder;\n\n return calculatedCheck === inputCheck;\n}\n\nexport function isPassport(code: string): boolean {\n return /^[A-Za-z][0-9]{8,9}$/.test(code);\n}\n\nexport function isCardNumber(code: string): boolean {\n const sanitized = code.replace(/[\\-\\s]/g, \"\");\n if (!/^\\d{16}$/.test(sanitized)) return false;\n\n let sum = 0;\n let shouldDouble = false;\n for (let i = sanitized.length - 1; i >= 0; i--) {\n let digit = parseInt(sanitized[i]);\n if (shouldDouble) {\n digit *= 2;\n if (digit > 9) digit -= 9;\n }\n sum += digit;\n shouldDouble = !shouldDouble;\n }\n return sum % 10 === 0;\n}\n\ninterface MobileValidationOptions {\n strictZero?: boolean | \"optional\";\n}\n\nexport function isIranianMobile(\n mobile: string,\n { strictZero = \"optional\" }: MobileValidationOptions = {}\n): boolean {\n const corePattern = \"9\\\\d{9}\";\n let pattern = \"\";\n if (strictZero === true) pattern = `^0${corePattern}$`;\n else if (strictZero === false) pattern = `^${corePattern}$`;\n else pattern = `^(?:0|\\\\+98)?${corePattern}$`;\n\n return new RegExp(pattern).test(mobile);\n}\n\nexport function isSheba(code: string): boolean {\n const iban = code.toUpperCase().replace(/[\\-\\s]/g, \"\");\n if (iban.length !== 26 || !iban.startsWith(\"IR\")) return false;\n\n const newStr = iban.substring(4) + iban.substring(0, 4);\n const numericString = newStr\n .split(\"\")\n .map((c) => {\n const code = c.charCodeAt(0);\n return code >= 48 && code <= 57 ? c : (code - 55).toString();\n })\n .join(\"\");\n\n try {\n return BigInt(numericString) % BigInt(97) === BigInt(1);\n } catch {\n return false;\n }\n}\n\nexport function isPostalCode(code: string): boolean {\n return /^[1-9]\\d{9}$/.test(code);\n}\n\nexport function isLandline(code: string): boolean {\n return /^0\\d{2}\\d{8}$/.test(code);\n}\n\nexport function verifyAndNormalize(value: string): string {\n return value\n .replace(/[۰-۹]/g, (d) => \"۰۱۲۳۴۵۶۷۸۹\".indexOf(d).toString())\n .replace(/[٠-٩]/g, (d) => \"٠١٢٣٤٥٦٧٨٩\".indexOf(d).toString());\n}\n","export const ERROR_MESSAGES = {\n fa: {\n melliCode: \"کد ملی نامعتبر است\",\n shenaseMelli: \"شناسه ملی نامعتبر است\",\n passport: \"شماره گذرنامه نامعتبر است\",\n cardNumber: \"شماره کارت نامعتبر است\",\n mobile: \"شماره موبایل نامعتبر است\",\n sheba: \"شماره شبا نامعتبر است\",\n postalCode: \"کد پستی نامعتبر است\",\n landline: \"شماره تلفن ثابت نامعتبر است\",\n },\n en: {\n melliCode: \"Invalid national code\",\n shenaseMelli: \"Invalid legal person ID (Shenase Melli)\",\n passport: \"Invalid passport number\",\n cardNumber: \"Invalid card number\",\n mobile: \"Invalid mobile number\",\n sheba: \"Invalid Sheba (IBAN) number\",\n postalCode: \"Invalid postal code\",\n landline: \"Invalid landline number\",\n },\n} as const;\n\nexport type Language = keyof typeof ERROR_MESSAGES;\n\nexport interface BaseOptions {\n message?: string;\n locale?: Language;\n}\n\nexport const getMessage = (\n key: keyof typeof ERROR_MESSAGES[\"fa\"],\n options?: BaseOptions\n): string => {\n if (options?.message) return options.message;\n const lang = options?.locale || \"fa\";\n return ERROR_MESSAGES[lang][key];\n};"],"mappings":"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","names":["index_exports","__export","isCardNumber","isIranianMobile","isLandline","isMelliCode","isPassport","isPostalCode","isSheba","isShenaseMelli","preprocessNumber","verifyAndNormalize","zCardNumber","zIranianMobile","zLandline","zMelliCode","zPassport","zPostalCode","zSheba","zShenaseMelli","__toCommonJS","import_zod","isMelliCode","code","check","sum","acc","x","isShenaseMelli","tenth","inputCheck","coefficients","i","digit","remainder","isPassport","isCardNumber","sanitized","shouldDouble","isIranianMobile","mobile","strictZero","corePattern","pattern","isSheba","iban","numericString","c","isPostalCode","isLandline","verifyAndNormalize","value","d","ERROR_MESSAGES","getMessage","key","options","lang","zMelliCode","options","val","isMelliCode","getMessage","zShenaseMelli","isShenaseMelli","zPassport","isPassport","zCardNumber","isCardNumber","zIranianMobile","isIranianMobile","zSheba","isSheba","zPostalCode","isPostalCode","zLandline","isLandline","preprocessNumber","schema","verifyAndNormalize"]}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{z as a}from"zod";function l(t){if(!/^\d{10}$/.test(t)||/^(\d)\1+$/.test(t))return!1;let e=parseInt(t[9]),n=t.substring(0,9).split("").reduce((s,o,i)=>s+parseInt(o)*(10-i),0)%11;return n<2?e===n:e===11-n}function p(t){let e=t.replace(/[\-\s]/g,"");if(!/^\d{16}$/.test(e))return!1;let n=0,s=!1;for(let o=e.length-1;o>=0;o--){let i=parseInt(e[o]);s&&(i*=2,i>9&&(i-=9)),n+=i,s=!s}return n%10===0}function d(t,{strictZero:e="optional"}={}){let n="9\\d{9}",s="";return e===!0?s=`^0${n}$`:e===!1?s=`^${n}$`:s=`^(?:0|\\+98)?${n}$`,new RegExp(s).test(t)}function c(t){let e=t.toUpperCase().replace(/[\-\s]/g,"");if(e.length!==26||!e.startsWith("IR"))return!1;let s=(e.substring(4)+e.substring(0,4)).split("").map(o=>{let i=o.charCodeAt(0);return i>=48&&i<=57?o:(i-55).toString()}).join("");try{return BigInt(s)%BigInt(97)===BigInt(1)}catch{return!1}}function g(t){return/^[1-9]\d{9}$/.test(t)}function f(t){return/^0\d{2}\d{8}$/.test(t)}var u={fa:{melliCode:"\u06A9\u062F \u0645\u0644\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",cardNumber:"\u0634\u0645\u0627\u0631\u0647 \u06A9\u0627\u0631\u062A \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",mobile:"\u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",sheba:"\u0634\u0645\u0627\u0631\u0647 \u0634\u0628\u0627 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",postalCode:"\u06A9\u062F \u067E\u0633\u062A\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",landline:"\u0634\u0645\u0627\u0631\u0647 \u062A\u0644\u0641\u0646 \u062B\u0627\u0628\u062A \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A"},en:{melliCode:"Invalid national code",cardNumber:"Invalid card number",mobile:"Invalid mobile number",sheba:"Invalid Sheba (IBAN) number",postalCode:"Invalid postal code",landline:"Invalid landline number"}},r=(t,e)=>{if(e?.message)return e.message;let n=e?.locale||"fa";return u[n][t]};var O=t=>a.string().refine(e=>l(e),{message:r("melliCode",t)}),M=t=>a.string().refine(e=>p(e),{message:r("cardNumber",t)}),h=t=>a.string().refine(e=>d(e,{strictZero:t?.strictZero}),{message:r("mobile",t)}),B=t=>a.string().refine(e=>c(e),{message:r("sheba",t)}),R=t=>a.string().refine(e=>g(e),{message:r("postalCode",t)}),$=t=>a.string().refine(e=>f(e),{message:r("landline",t)});export{p as isCardNumber,d as isIranianMobile,f as isLandline,l as isMelliCode,g as isPostalCode,c as isSheba,M as zCardNumber,h as zIranianMobile,$ as zLandline,O as zMelliCode,R as zPostalCode,B as zSheba};
1
+ import{z as a}from"zod";function p(e){if(!/^\d{10}$/.test(e)||/^(\d)\1+$/.test(e))return!1;let t=parseInt(e[9]),n=e.substring(0,9).split("").reduce((s,i,r)=>s+parseInt(i)*(10-r),0)%11;return n<2?t===n:t===11-n}function c(e){if(e.length!==11||!/^\d{11}$/.test(e))return!1;let t=parseInt(e[9]),n=parseInt(e[10]),s=[29,27,23,19,17,29,27,23,19,17],i=0;for(let l=0;l<10;l++){let h=parseInt(e[l]);i+=(h+t+2)*s[l]}let r=i%11;return(r===10?0:r)===n}function g(e){return/^[A-Za-z][0-9]{8,9}$/.test(e)}function f(e){let t=e.replace(/[\-\s]/g,"");if(!/^\d{16}$/.test(t))return!1;let n=0,s=!1;for(let i=t.length-1;i>=0;i--){let r=parseInt(t[i]);s&&(r*=2,r>9&&(r-=9)),n+=r,s=!s}return n%10===0}function u(e,{strictZero:t="optional"}={}){let n="9\\d{9}",s="";return t===!0?s=`^0${n}$`:t===!1?s=`^${n}$`:s=`^(?:0|\\+98)?${n}$`,new RegExp(s).test(e)}function d(e){let t=e.toUpperCase().replace(/[\-\s]/g,"");if(t.length!==26||!t.startsWith("IR"))return!1;let s=(t.substring(4)+t.substring(0,4)).split("").map(i=>{let r=i.charCodeAt(0);return r>=48&&r<=57?i:(r-55).toString()}).join("");try{return BigInt(s)%BigInt(97)===BigInt(1)}catch{return!1}}function b(e){return/^[1-9]\d{9}$/.test(e)}function m(e){return/^0\d{2}\d{8}$/.test(e)}function x(e){return e.replace(/[۰-۹]/g,t=>"\u06F0\u06F1\u06F2\u06F3\u06F4\u06F5\u06F6\u06F7\u06F8\u06F9".indexOf(t).toString()).replace(/[٠-٩]/g,t=>"\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669".indexOf(t).toString())}var I={fa:{melliCode:"\u06A9\u062F \u0645\u0644\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",shenaseMelli:"\u0634\u0646\u0627\u0633\u0647 \u0645\u0644\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",passport:"\u0634\u0645\u0627\u0631\u0647 \u06AF\u0630\u0631\u0646\u0627\u0645\u0647 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",cardNumber:"\u0634\u0645\u0627\u0631\u0647 \u06A9\u0627\u0631\u062A \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",mobile:"\u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",sheba:"\u0634\u0645\u0627\u0631\u0647 \u0634\u0628\u0627 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",postalCode:"\u06A9\u062F \u067E\u0633\u062A\u06CC \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A",landline:"\u0634\u0645\u0627\u0631\u0647 \u062A\u0644\u0641\u0646 \u062B\u0627\u0628\u062A \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u0627\u0633\u062A"},en:{melliCode:"Invalid national code",shenaseMelli:"Invalid legal person ID (Shenase Melli)",passport:"Invalid passport number",cardNumber:"Invalid card number",mobile:"Invalid mobile number",sheba:"Invalid Sheba (IBAN) number",postalCode:"Invalid postal code",landline:"Invalid landline number"}},o=(e,t)=>{if(t?.message)return t.message;let n=t?.locale||"fa";return I[n][e]};var y=e=>a.string().refine(t=>p(t),{message:o("melliCode",e)}),R=e=>a.string().refine(t=>c(t),{message:o("shenaseMelli",e)}),E=e=>a.string().refine(t=>g(t),{message:o("passport",e)}),N=e=>a.string().refine(t=>f(t),{message:o("cardNumber",e)}),A=e=>a.string().refine(t=>u(t,{strictZero:e?.strictZero}),{message:o("mobile",e)}),v=e=>a.string().refine(t=>d(t),{message:o("sheba",e)}),P=e=>a.string().refine(t=>b(t),{message:o("postalCode",e)}),k=e=>a.string().refine(t=>m(t),{message:o("landline",e)}),L=e=>a.preprocess(t=>typeof t=="string"?x(t):t,e);export{f as isCardNumber,u as isIranianMobile,m as isLandline,p as isMelliCode,g as isPassport,b as isPostalCode,d as isSheba,c as isShenaseMelli,L as preprocessNumber,x as verifyAndNormalize,N as zCardNumber,A as zIranianMobile,k as zLandline,y as zMelliCode,E as zPassport,P as zPostalCode,v as zSheba,R as zShenaseMelli};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/constants.ts"],"sourcesContent":["import { z } from \"zod\";\nimport {\n isMelliCode,\n isCardNumber,\n isIranianMobile,\n isSheba,\n isPostalCode,\n isLandline,\n} from \"./utils\";\nimport { getMessage, BaseOptions } from \"./constants\";\n\nexport const zMelliCode = (options?: BaseOptions) =>\n z.string().refine((val) => isMelliCode(val), {\n message: getMessage(\"melliCode\", options),\n });\n\nexport const zCardNumber = (options?: BaseOptions) =>\n z.string().refine((val) => isCardNumber(val), {\n message: getMessage(\"cardNumber\", options),\n });\n\ninterface MobileOptions extends BaseOptions {\n strictZero?: boolean | \"optional\";\n}\n\nexport const zIranianMobile = (options?: MobileOptions) =>\n z\n .string()\n .refine(\n (val) => isIranianMobile(val, { strictZero: options?.strictZero }),\n {\n message: getMessage(\"mobile\", options),\n }\n );\n\nexport const zSheba = (options?: BaseOptions) =>\n z.string().refine((val) => isSheba(val), {\n message: getMessage(\"sheba\", options),\n });\n\nexport const zPostalCode = (options?: BaseOptions) =>\n z.string().refine((val) => isPostalCode(val), {\n message: getMessage(\"postalCode\", options),\n });\n\nexport const zLandline = (options?: BaseOptions) =>\n z.string().refine((val) => isLandline(val), {\n message: getMessage(\"landline\", options),\n });\n\nexport {\n isMelliCode,\n isCardNumber,\n isIranianMobile,\n isSheba,\n isPostalCode,\n isLandline,\n};\n","export function isMelliCode(code: string): boolean {\n if (!/^\\d{10}$/.test(code)) return false;\n if (/^(\\d)\\1+$/.test(code)) return false;\n\n const check = parseInt(code[9]);\n const sum =\n code\n .substring(0, 9)\n .split(\"\")\n .reduce((acc, x, i) => acc + parseInt(x) * (10 - i), 0) % 11;\n\n return sum < 2 ? check === sum : check === 11 - sum;\n}\n\nexport function isCardNumber(code: string): boolean {\n const sanitized = code.replace(/[\\-\\s]/g, \"\");\n if (!/^\\d{16}$/.test(sanitized)) return false;\n\n let sum = 0;\n let shouldDouble = false;\n\n for (let i = sanitized.length - 1; i >= 0; i--) {\n let digit = parseInt(sanitized[i]);\n\n if (shouldDouble) {\n digit *= 2;\n if (digit > 9) digit -= 9;\n }\n\n sum += digit;\n shouldDouble = !shouldDouble;\n }\n\n return sum % 10 === 0;\n}\n\ninterface MobileValidationOptions {\n strictZero?: boolean | \"optional\";\n}\n\nexport function isIranianMobile(\n mobile: string,\n { strictZero = \"optional\" }: MobileValidationOptions = {}\n): boolean {\n const corePattern = \"9\\\\d{9}\";\n let pattern = \"\";\n\n if (strictZero === true) {\n pattern = `^0${corePattern}$`;\n } else if (strictZero === false) {\n pattern = `^${corePattern}$`;\n } else {\n pattern = `^(?:0|\\\\+98)?${corePattern}$`;\n }\n\n return new RegExp(pattern).test(mobile);\n}\n\nexport function isSheba(code: string): boolean {\n const iban = code.toUpperCase().replace(/[\\-\\s]/g, \"\");\n\n if (iban.length !== 26 || !iban.startsWith(\"IR\")) return false;\n\n const newStr = iban.substring(4) + iban.substring(0, 4);\n\n const numericString = newStr\n .split(\"\")\n .map((char) => {\n const code = char.charCodeAt(0);\n return code >= 48 && code <= 57 ? char : (code - 55).toString();\n })\n .join(\"\");\n\n try {\n const remainder = BigInt(numericString) % BigInt(97);\n return remainder === BigInt(1);\n } catch {\n return false;\n }\n}\n\nexport function isPostalCode(code: string): boolean {\n return /^[1-9]\\d{9}$/.test(code);\n}\n\nexport function isLandline(code: string): boolean {\n return /^0\\d{2}\\d{8}$/.test(code);\n}\n","export const ERROR_MESSAGES = {\n fa: {\n melliCode: \"کد ملی نامعتبر است\",\n cardNumber: \"شماره کارت نامعتبر است\",\n mobile: \"شماره موبایل نامعتبر است\",\n sheba: \"شماره شبا نامعتبر است\",\n postalCode: \"کد پستی نامعتبر است\",\n landline: \"شماره تلفن ثابت نامعتبر است\",\n },\n en: {\n melliCode: \"Invalid national code\",\n cardNumber: \"Invalid card number\",\n mobile: \"Invalid mobile number\",\n sheba: \"Invalid Sheba (IBAN) number\",\n postalCode: \"Invalid postal code\",\n landline: \"Invalid landline number\",\n },\n} as const;\n\nexport type Language = keyof typeof ERROR_MESSAGES;\n\nexport interface BaseOptions {\n message?: string;\n locale?: Language;\n}\n\nexport const getMessage = (\n key: keyof (typeof ERROR_MESSAGES)[\"fa\"],\n options?: BaseOptions\n): string => {\n if (options?.message) return options.message;\n const lang = options?.locale || \"fa\";\n return ERROR_MESSAGES[lang][key];\n};\n"],"mappings":"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","names":["z","isMelliCode","code","check","sum","acc","x","isCardNumber","sanitized","shouldDouble","i","digit","isIranianMobile","mobile","strictZero","corePattern","pattern","isSheba","iban","numericString","char","isPostalCode","isLandline","ERROR_MESSAGES","getMessage","key","options","lang","zMelliCode","options","z","val","isMelliCode","getMessage","zCardNumber","isCardNumber","zIranianMobile","isIranianMobile","zSheba","isSheba","zPostalCode","isPostalCode","zLandline","isLandline"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/constants.ts"],"sourcesContent":["import { z } from \"zod\";\nimport {\n isMelliCode,\n isShenaseMelli,\n isPassport,\n isCardNumber,\n isIranianMobile,\n isSheba,\n isPostalCode,\n isLandline,\n verifyAndNormalize,\n} from \"./utils\";\nimport { getMessage, BaseOptions } from \"./constants\";\n\nexport const zMelliCode = (options?: BaseOptions) =>\n z.string().refine((val) => isMelliCode(val), {\n message: getMessage(\"melliCode\", options),\n });\n\nexport const zShenaseMelli = (options?: BaseOptions) =>\n z.string().refine((val) => isShenaseMelli(val), {\n message: getMessage(\"shenaseMelli\", options),\n });\n\nexport const zPassport = (options?: BaseOptions) =>\n z.string().refine((val) => isPassport(val), {\n message: getMessage(\"passport\", options),\n });\n\nexport const zCardNumber = (options?: BaseOptions) =>\n z.string().refine((val) => isCardNumber(val), {\n message: getMessage(\"cardNumber\", options),\n });\n\ninterface MobileOptions extends BaseOptions {\n strictZero?: boolean | \"optional\";\n}\n\nexport const zIranianMobile = (options?: MobileOptions) =>\n z\n .string()\n .refine(\n (val) => isIranianMobile(val, { strictZero: options?.strictZero }),\n {\n message: getMessage(\"mobile\", options),\n }\n );\n\nexport const zSheba = (options?: BaseOptions) =>\n z.string().refine((val) => isSheba(val), {\n message: getMessage(\"sheba\", options),\n });\n\nexport const zPostalCode = (options?: BaseOptions) =>\n z.string().refine((val) => isPostalCode(val), {\n message: getMessage(\"postalCode\", options),\n });\n\nexport const zLandline = (options?: BaseOptions) =>\n z.string().refine((val) => isLandline(val), {\n message: getMessage(\"landline\", options),\n });\n\nexport const preprocessNumber = (schema: z.ZodTypeAny) =>\n z.preprocess((val) => {\n if (typeof val === \"string\") {\n return verifyAndNormalize(val);\n }\n return val;\n }, schema);\n\nexport {\n isMelliCode,\n isShenaseMelli,\n isPassport,\n isCardNumber,\n isIranianMobile,\n isSheba,\n isPostalCode,\n isLandline,\n verifyAndNormalize,\n};\n","export function isMelliCode(code: string): boolean {\n if (!/^\\d{10}$/.test(code)) return false;\n if (/^(\\d)\\1+$/.test(code)) return false;\n\n const check = parseInt(code[9]);\n const sum =\n code\n .substring(0, 9)\n .split(\"\")\n .reduce((acc, x, i) => acc + parseInt(x) * (10 - i), 0) % 11;\n\n return sum < 2 ? check === sum : check === 11 - sum;\n}\n\nexport function isShenaseMelli(code: string): boolean {\n if (code.length !== 11 || !/^\\d{11}$/.test(code)) return false;\n\n const tenth = parseInt(code[9]);\n const inputCheck = parseInt(code[10]);\n const coefficients = [29, 27, 23, 19, 17, 29, 27, 23, 19, 17];\n\n let sum = 0;\n for (let i = 0; i < 10; i++) {\n const digit = parseInt(code[i]);\n sum += (digit + tenth + 2) * coefficients[i];\n }\n\n const remainder = sum % 11;\n const calculatedCheck = remainder === 10 ? 0 : remainder;\n\n return calculatedCheck === inputCheck;\n}\n\nexport function isPassport(code: string): boolean {\n return /^[A-Za-z][0-9]{8,9}$/.test(code);\n}\n\nexport function isCardNumber(code: string): boolean {\n const sanitized = code.replace(/[\\-\\s]/g, \"\");\n if (!/^\\d{16}$/.test(sanitized)) return false;\n\n let sum = 0;\n let shouldDouble = false;\n for (let i = sanitized.length - 1; i >= 0; i--) {\n let digit = parseInt(sanitized[i]);\n if (shouldDouble) {\n digit *= 2;\n if (digit > 9) digit -= 9;\n }\n sum += digit;\n shouldDouble = !shouldDouble;\n }\n return sum % 10 === 0;\n}\n\ninterface MobileValidationOptions {\n strictZero?: boolean | \"optional\";\n}\n\nexport function isIranianMobile(\n mobile: string,\n { strictZero = \"optional\" }: MobileValidationOptions = {}\n): boolean {\n const corePattern = \"9\\\\d{9}\";\n let pattern = \"\";\n if (strictZero === true) pattern = `^0${corePattern}$`;\n else if (strictZero === false) pattern = `^${corePattern}$`;\n else pattern = `^(?:0|\\\\+98)?${corePattern}$`;\n\n return new RegExp(pattern).test(mobile);\n}\n\nexport function isSheba(code: string): boolean {\n const iban = code.toUpperCase().replace(/[\\-\\s]/g, \"\");\n if (iban.length !== 26 || !iban.startsWith(\"IR\")) return false;\n\n const newStr = iban.substring(4) + iban.substring(0, 4);\n const numericString = newStr\n .split(\"\")\n .map((c) => {\n const code = c.charCodeAt(0);\n return code >= 48 && code <= 57 ? c : (code - 55).toString();\n })\n .join(\"\");\n\n try {\n return BigInt(numericString) % BigInt(97) === BigInt(1);\n } catch {\n return false;\n }\n}\n\nexport function isPostalCode(code: string): boolean {\n return /^[1-9]\\d{9}$/.test(code);\n}\n\nexport function isLandline(code: string): boolean {\n return /^0\\d{2}\\d{8}$/.test(code);\n}\n\nexport function verifyAndNormalize(value: string): string {\n return value\n .replace(/[۰-۹]/g, (d) => \"۰۱۲۳۴۵۶۷۸۹\".indexOf(d).toString())\n .replace(/[٠-٩]/g, (d) => \"٠١٢٣٤٥٦٧٨٩\".indexOf(d).toString());\n}\n","export const ERROR_MESSAGES = {\n fa: {\n melliCode: \"کد ملی نامعتبر است\",\n shenaseMelli: \"شناسه ملی نامعتبر است\",\n passport: \"شماره گذرنامه نامعتبر است\",\n cardNumber: \"شماره کارت نامعتبر است\",\n mobile: \"شماره موبایل نامعتبر است\",\n sheba: \"شماره شبا نامعتبر است\",\n postalCode: \"کد پستی نامعتبر است\",\n landline: \"شماره تلفن ثابت نامعتبر است\",\n },\n en: {\n melliCode: \"Invalid national code\",\n shenaseMelli: \"Invalid legal person ID (Shenase Melli)\",\n passport: \"Invalid passport number\",\n cardNumber: \"Invalid card number\",\n mobile: \"Invalid mobile number\",\n sheba: \"Invalid Sheba (IBAN) number\",\n postalCode: \"Invalid postal code\",\n landline: \"Invalid landline number\",\n },\n} as const;\n\nexport type Language = keyof typeof ERROR_MESSAGES;\n\nexport interface BaseOptions {\n message?: string;\n locale?: Language;\n}\n\nexport const getMessage = (\n key: keyof typeof ERROR_MESSAGES[\"fa\"],\n options?: BaseOptions\n): string => {\n if (options?.message) return options.message;\n const lang = options?.locale || \"fa\";\n return ERROR_MESSAGES[lang][key];\n};"],"mappings":"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","names":["z","isMelliCode","code","check","sum","acc","x","i","isShenaseMelli","tenth","inputCheck","coefficients","digit","remainder","isPassport","isCardNumber","sanitized","shouldDouble","isIranianMobile","mobile","strictZero","corePattern","pattern","isSheba","iban","numericString","c","isPostalCode","isLandline","verifyAndNormalize","value","d","ERROR_MESSAGES","getMessage","key","options","lang","zMelliCode","options","z","val","isMelliCode","getMessage","zShenaseMelli","isShenaseMelli","zPassport","isPassport","zCardNumber","isCardNumber","zIranianMobile","isIranianMobile","zSheba","isSheba","zPostalCode","isPostalCode","zLandline","isLandline","preprocessNumber","schema","verifyAndNormalize"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod-ir",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "Essential Zod validators for Iranian specific data (National Code, Card Number, Mobile)",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",