zod-ir 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/index.d.mts +68 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Zod Iranian Utils 🇮🇷
|
|
2
|
+
|
|
3
|
+
A lightweight, TypeScript-first collection of Zod validators for Iranian specific data structures.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Melli Code** (National ID) validation with official algorithm.
|
|
8
|
+
- 💳 **Bank Card** validation using Luhn algorithm.
|
|
9
|
+
- 📱 **Mobile Number** validation (supports +98, 09xx, 9xx).
|
|
10
|
+
- 🌍 **Bilingual Error Messages** (Persian & English).
|
|
11
|
+
- 🌲 **Tree-shakable** & Type-safe.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install zod zod-iranian-utils
|
|
17
|
+
# or
|
|
18
|
+
yarn add zod zod-iranian-utils
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validates Iranian National Code (Melli Code) based on the official algorithm.
|
|
5
|
+
* @param code The 10-digit national code string.
|
|
6
|
+
*/
|
|
7
|
+
declare function isMelliCode(code: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Validates Bank Card Number using the Luhn algorithm.
|
|
10
|
+
* @param code The 16-digit card number.
|
|
11
|
+
*/
|
|
12
|
+
declare function isCardNumber(code: string): boolean;
|
|
13
|
+
interface MobileValidationOptions {
|
|
14
|
+
/**
|
|
15
|
+
* strictZero:
|
|
16
|
+
* - true: Must start with 0 (e.g., 0912...)
|
|
17
|
+
* - false: Must NOT start with 0 (e.g., 912...)
|
|
18
|
+
* - "optional": Both are accepted (default)
|
|
19
|
+
*/
|
|
20
|
+
strictZero?: boolean | "optional";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Validates Iranian Mobile Number.
|
|
24
|
+
* Supports 09xx, 9xx, and +989xx formats.
|
|
25
|
+
*/
|
|
26
|
+
declare function isIranianMobile(mobile: string, { strictZero }?: MobileValidationOptions): boolean;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Default error messages for validation failures.
|
|
30
|
+
* Supports Persian (fa) and English (en).
|
|
31
|
+
*/
|
|
32
|
+
declare const ERROR_MESSAGES: {
|
|
33
|
+
readonly fa: {
|
|
34
|
+
readonly melliCode: "کد ملی نامعتبر است";
|
|
35
|
+
readonly cardNumber: "شماره کارت نامعتبر است";
|
|
36
|
+
readonly mobile: "شماره موبایل نامعتبر است";
|
|
37
|
+
};
|
|
38
|
+
readonly en: {
|
|
39
|
+
readonly melliCode: "Invalid national code";
|
|
40
|
+
readonly cardNumber: "Invalid card number";
|
|
41
|
+
readonly mobile: "Invalid mobile number";
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
type Language = keyof typeof ERROR_MESSAGES;
|
|
45
|
+
interface BaseOptions {
|
|
46
|
+
/** Custom error message to override the default one */
|
|
47
|
+
message?: string;
|
|
48
|
+
/** Language for the default error message (default: 'fa') */
|
|
49
|
+
locale?: Language;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Zod schema for validating Iranian National Code (Melli Code).
|
|
54
|
+
*/
|
|
55
|
+
declare const zMelliCode: (options?: BaseOptions) => z.ZodString;
|
|
56
|
+
/**
|
|
57
|
+
* Zod schema for validating Iranian Bank Card Numbers (Luhn Algorithm).
|
|
58
|
+
*/
|
|
59
|
+
declare const zCardNumber: (options?: BaseOptions) => z.ZodString;
|
|
60
|
+
interface MobileOptions extends BaseOptions {
|
|
61
|
+
strictZero?: boolean | "optional";
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Zod schema for validating Iranian Mobile Numbers.
|
|
65
|
+
*/
|
|
66
|
+
declare const zIranianMobile: (options?: MobileOptions) => z.ZodString;
|
|
67
|
+
|
|
68
|
+
export { isCardNumber, isIranianMobile, isMelliCode, zCardNumber, zIranianMobile, zMelliCode };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validates Iranian National Code (Melli Code) based on the official algorithm.
|
|
5
|
+
* @param code The 10-digit national code string.
|
|
6
|
+
*/
|
|
7
|
+
declare function isMelliCode(code: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Validates Bank Card Number using the Luhn algorithm.
|
|
10
|
+
* @param code The 16-digit card number.
|
|
11
|
+
*/
|
|
12
|
+
declare function isCardNumber(code: string): boolean;
|
|
13
|
+
interface MobileValidationOptions {
|
|
14
|
+
/**
|
|
15
|
+
* strictZero:
|
|
16
|
+
* - true: Must start with 0 (e.g., 0912...)
|
|
17
|
+
* - false: Must NOT start with 0 (e.g., 912...)
|
|
18
|
+
* - "optional": Both are accepted (default)
|
|
19
|
+
*/
|
|
20
|
+
strictZero?: boolean | "optional";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Validates Iranian Mobile Number.
|
|
24
|
+
* Supports 09xx, 9xx, and +989xx formats.
|
|
25
|
+
*/
|
|
26
|
+
declare function isIranianMobile(mobile: string, { strictZero }?: MobileValidationOptions): boolean;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Default error messages for validation failures.
|
|
30
|
+
* Supports Persian (fa) and English (en).
|
|
31
|
+
*/
|
|
32
|
+
declare const ERROR_MESSAGES: {
|
|
33
|
+
readonly fa: {
|
|
34
|
+
readonly melliCode: "کد ملی نامعتبر است";
|
|
35
|
+
readonly cardNumber: "شماره کارت نامعتبر است";
|
|
36
|
+
readonly mobile: "شماره موبایل نامعتبر است";
|
|
37
|
+
};
|
|
38
|
+
readonly en: {
|
|
39
|
+
readonly melliCode: "Invalid national code";
|
|
40
|
+
readonly cardNumber: "Invalid card number";
|
|
41
|
+
readonly mobile: "Invalid mobile number";
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
type Language = keyof typeof ERROR_MESSAGES;
|
|
45
|
+
interface BaseOptions {
|
|
46
|
+
/** Custom error message to override the default one */
|
|
47
|
+
message?: string;
|
|
48
|
+
/** Language for the default error message (default: 'fa') */
|
|
49
|
+
locale?: Language;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Zod schema for validating Iranian National Code (Melli Code).
|
|
54
|
+
*/
|
|
55
|
+
declare const zMelliCode: (options?: BaseOptions) => z.ZodString;
|
|
56
|
+
/**
|
|
57
|
+
* Zod schema for validating Iranian Bank Card Numbers (Luhn Algorithm).
|
|
58
|
+
*/
|
|
59
|
+
declare const zCardNumber: (options?: BaseOptions) => z.ZodString;
|
|
60
|
+
interface MobileOptions extends BaseOptions {
|
|
61
|
+
strictZero?: boolean | "optional";
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Zod schema for validating Iranian Mobile Numbers.
|
|
65
|
+
*/
|
|
66
|
+
declare const zIranianMobile: (options?: MobileOptions) => z.ZodString;
|
|
67
|
+
|
|
68
|
+
export { isCardNumber, isIranianMobile, isMelliCode, zCardNumber, zIranianMobile, zMelliCode };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var l=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var u=Object.prototype.hasOwnProperty;var b=(t,e)=>{for(var n in e)l(t,n,{get:e[n],enumerable:!0})},d=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of g(e))!u.call(t,o)&&o!==n&&l(t,o,{get:()=>e[o],enumerable:!(i=m(e,o))||i.enumerable});return t};var M=t=>d(l({},"__esModule",{value:!0}),t);var C={};b(C,{isCardNumber:()=>f,isIranianMobile:()=>c,isMelliCode:()=>p,zCardNumber:()=>E,zIranianMobile:()=>R,zMelliCode:()=>O});module.exports=M(C);var a=require("zod");function p(t){if(!/^\d{10}$/.test(t))return!1;let e=parseInt(t[9]);if(/^(\d)\1+$/.test(t))return!1;let n=t.substring(0,9).split("").reduce((i,o,r)=>i+parseInt(o)*(10-r),0)%11;return n<2?e===n:e===11-n}function f(t){let e=t.replace(/[\-\s]/g,"");if(!/^\d{16}$/.test(e))return!1;let n=0,i=!1;for(let o=e.length-1;o>=0;o--){let r=parseInt(e[o]);i&&(r*=2,r>9&&(r-=9)),n+=r,i=!i}return n%10===0}function c(t,{strictZero:e="optional"}={}){let n="9\\d{9}",i="";return e===!0?i=`^0${n}$`:e===!1?i=`^${n}$`:i=`^(?:0|\\+98)?${n}$`,new RegExp(i).test(t)}var x={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"},en:{melliCode:"Invalid national code",cardNumber:"Invalid card number",mobile:"Invalid mobile number"}},s=(t,e)=>{if(e?.message)return e.message;let n=e?.locale||"fa";return x[n][t]};var O=t=>a.z.string().refine(e=>p(e),{message:s("melliCode",t)}),E=t=>a.z.string().refine(e=>f(e),{message:s("cardNumber",t)}),R=t=>a.z.string().refine(e=>c(e,{strictZero:t?.strictZero}),{message:s("mobile",t)});0&&(module.exports={isCardNumber,isIranianMobile,isMelliCode,zCardNumber,zIranianMobile,zMelliCode});
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/constants.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { isMelliCode, isCardNumber, isIranianMobile } from \"./utils\";\nimport { getMessage, BaseOptions } from \"./constants\";\n\n/**\n * Zod schema for validating Iranian National Code (Melli Code).\n */\nexport const zMelliCode = (options?: BaseOptions) =>\n z.string().refine((val) => isMelliCode(val), {\n message: getMessage(\"melliCode\", options),\n });\n\n/**\n * Zod schema for validating Iranian Bank Card Numbers (Luhn Algorithm).\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\n/**\n * Zod schema for validating Iranian Mobile Numbers.\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\n// Export raw utility functions for non-Zod usage\nexport { isMelliCode, isCardNumber, isIranianMobile };\n","/**\n * Validates Iranian National Code (Melli Code) based on the official algorithm.\n * @param code The 10-digit national code string.\n */\nexport function isMelliCode(code: string): boolean {\n // Basic format check\n if (!/^\\d{10}$/.test(code)) return false;\n\n // Check for repeated digits (e.g., 1111111111) which are invalid\n const check = parseInt(code[9]);\n if (/^(\\d)\\1+$/.test(code)) return false;\n\n // Calculate control digit\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\n/**\n * Validates Bank Card Number using the Luhn algorithm.\n * @param code The 16-digit card number.\n */\nexport function isCardNumber(code: string): boolean {\n // Remove hyphens or spaces if present\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 // Traverse from right to left\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 /**\n * strictZero:\n * - true: Must start with 0 (e.g., 0912...)\n * - false: Must NOT start with 0 (e.g., 912...)\n * - \"optional\": Both are accepted (default)\n */\n strictZero?: boolean | \"optional\";\n}\n\n/**\n * Validates Iranian Mobile Number.\n * Supports 09xx, 9xx, and +989xx formats.\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","/**\n * Default error messages for validation failures.\n * Supports Persian (fa) and English (en).\n */\nexport const ERROR_MESSAGES = {\n fa: {\n melliCode: \"کد ملی نامعتبر است\",\n cardNumber: \"شماره کارت نامعتبر است\",\n mobile: \"شماره موبایل نامعتبر است\",\n },\n en: {\n melliCode: \"Invalid national code\",\n cardNumber: \"Invalid card number\",\n mobile: \"Invalid mobile number\",\n },\n} as const;\n\nexport type Language = keyof typeof ERROR_MESSAGES;\n\nexport interface BaseOptions {\n /** Custom error message to override the default one */\n message?: string;\n /** Language for the default error message (default: 'fa') */\n locale?: Language;\n}\n\n/**\n * Helper to resolve the error message based on options.\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":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,kBAAAE,EAAA,oBAAAC,EAAA,gBAAAC,EAAA,gBAAAC,EAAA,mBAAAC,EAAA,eAAAC,IAAA,eAAAC,EAAAR,GAAA,IAAAS,EAAkB,eCIX,SAASC,EAAYC,EAAuB,CAEjD,GAAI,CAAC,WAAW,KAAKA,CAAI,EAAG,MAAO,GAGnC,IAAMC,EAAQ,SAASD,EAAK,CAAC,CAAC,EAC9B,GAAI,YAAY,KAAKA,CAAI,EAAG,MAAO,GAGnC,IAAME,EACJF,EACG,UAAU,EAAG,CAAC,EACd,MAAM,EAAE,EACR,OAAO,CAACG,EAAKC,EAAGC,IAAMF,EAAM,SAASC,CAAC,GAAK,GAAKC,GAAI,CAAC,EAAI,GAE9D,OAAOH,EAAM,EAAID,IAAUC,EAAMD,IAAU,GAAKC,CAClD,CAMO,SAASI,EAAaN,EAAuB,CAElD,IAAMO,EAAYP,EAAK,QAAQ,UAAW,EAAE,EAC5C,GAAI,CAAC,WAAW,KAAKO,CAAS,EAAG,MAAO,GAExC,IAAIL,EAAM,EACNM,EAAe,GAGnB,QAASH,EAAIE,EAAU,OAAS,EAAGF,GAAK,EAAGA,IAAK,CAC9C,IAAII,EAAQ,SAASF,EAAUF,CAAC,CAAC,EAE7BG,IACFC,GAAS,EACLA,EAAQ,IAAGA,GAAS,IAG1BP,GAAOO,EACPD,EAAe,CAACA,CAClB,CAEA,OAAON,EAAM,KAAO,CACtB,CAgBO,SAASQ,EACdC,EACA,CAAE,WAAAC,EAAa,UAAW,EAA6B,CAAC,EAC/C,CACT,IAAMC,EAAc,UAChBC,EAAU,GAEd,OAAIF,IAAe,GACjBE,EAAU,KAAKD,CAAW,IACjBD,IAAe,GACxBE,EAAU,IAAID,CAAW,IAEzBC,EAAU,gBAAgBD,CAAW,IAGhC,IAAI,OAAOC,CAAO,EAAE,KAAKH,CAAM,CACxC,CC5EO,IAAMI,EAAiB,CAC5B,GAAI,CACF,UAAW,gGACX,WAAY,wHACZ,OAAQ,mIACV,EACA,GAAI,CACF,UAAW,wBACX,WAAY,sBACZ,OAAQ,uBACV,CACF,EAcaC,EAAa,CACxBC,EACAC,IACW,CACX,GAAIA,GAAS,QAAS,OAAOA,EAAQ,QACrC,IAAMC,EAAOD,GAAS,QAAU,KAChC,OAAOH,EAAeI,CAAI,EAAEF,CAAG,CACjC,EF7BO,IAAMG,EAAcC,GACzB,IAAE,OAAO,EAAE,OAAQC,GAAQC,EAAYD,CAAG,EAAG,CAC3C,QAASE,EAAW,YAAaH,CAAO,CAC1C,CAAC,EAKUI,EAAeJ,GAC1B,IAAE,OAAO,EAAE,OAAQC,GAAQI,EAAaJ,CAAG,EAAG,CAC5C,QAASE,EAAW,aAAcH,CAAO,CAC3C,CAAC,EASUM,EAAkBN,GAC7B,IACG,OAAO,EACP,OACEC,GAAQM,EAAgBN,EAAK,CAAE,WAAYD,GAAS,UAAW,CAAC,EACjE,CACE,QAASG,EAAW,SAAUH,CAAO,CACvC,CACF","names":["index_exports","__export","isCardNumber","isIranianMobile","isMelliCode","zCardNumber","zIranianMobile","zMelliCode","__toCommonJS","import_zod","isMelliCode","code","check","sum","acc","x","i","isCardNumber","sanitized","shouldDouble","digit","isIranianMobile","mobile","strictZero","corePattern","pattern","ERROR_MESSAGES","getMessage","key","options","lang","zMelliCode","options","val","isMelliCode","getMessage","zCardNumber","isCardNumber","zIranianMobile","isIranianMobile"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{z as a}from"zod";function l(t){if(!/^\d{10}$/.test(t))return!1;let e=parseInt(t[9]);if(/^(\d)\1+$/.test(t))return!1;let n=t.substring(0,9).split("").reduce((i,r,o)=>i+parseInt(r)*(10-o),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,i=!1;for(let r=e.length-1;r>=0;r--){let o=parseInt(e[r]);i&&(o*=2,o>9&&(o-=9)),n+=o,i=!i}return n%10===0}function f(t,{strictZero:e="optional"}={}){let n="9\\d{9}",i="";return e===!0?i=`^0${n}$`:e===!1?i=`^${n}$`:i=`^(?:0|\\+98)?${n}$`,new RegExp(i).test(t)}var c={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"},en:{melliCode:"Invalid national code",cardNumber:"Invalid card number",mobile:"Invalid mobile number"}},s=(t,e)=>{if(e?.message)return e.message;let n=e?.locale||"fa";return c[n][t]};var x=t=>a.string().refine(e=>l(e),{message:s("melliCode",t)}),O=t=>a.string().refine(e=>p(e),{message:s("cardNumber",t)}),E=t=>a.string().refine(e=>f(e,{strictZero:t?.strictZero}),{message:s("mobile",t)});export{p as isCardNumber,f as isIranianMobile,l as isMelliCode,O as zCardNumber,E as zIranianMobile,x as zMelliCode};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/constants.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { isMelliCode, isCardNumber, isIranianMobile } from \"./utils\";\nimport { getMessage, BaseOptions } from \"./constants\";\n\n/**\n * Zod schema for validating Iranian National Code (Melli Code).\n */\nexport const zMelliCode = (options?: BaseOptions) =>\n z.string().refine((val) => isMelliCode(val), {\n message: getMessage(\"melliCode\", options),\n });\n\n/**\n * Zod schema for validating Iranian Bank Card Numbers (Luhn Algorithm).\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\n/**\n * Zod schema for validating Iranian Mobile Numbers.\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\n// Export raw utility functions for non-Zod usage\nexport { isMelliCode, isCardNumber, isIranianMobile };\n","/**\n * Validates Iranian National Code (Melli Code) based on the official algorithm.\n * @param code The 10-digit national code string.\n */\nexport function isMelliCode(code: string): boolean {\n // Basic format check\n if (!/^\\d{10}$/.test(code)) return false;\n\n // Check for repeated digits (e.g., 1111111111) which are invalid\n const check = parseInt(code[9]);\n if (/^(\\d)\\1+$/.test(code)) return false;\n\n // Calculate control digit\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\n/**\n * Validates Bank Card Number using the Luhn algorithm.\n * @param code The 16-digit card number.\n */\nexport function isCardNumber(code: string): boolean {\n // Remove hyphens or spaces if present\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 // Traverse from right to left\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 /**\n * strictZero:\n * - true: Must start with 0 (e.g., 0912...)\n * - false: Must NOT start with 0 (e.g., 912...)\n * - \"optional\": Both are accepted (default)\n */\n strictZero?: boolean | \"optional\";\n}\n\n/**\n * Validates Iranian Mobile Number.\n * Supports 09xx, 9xx, and +989xx formats.\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","/**\n * Default error messages for validation failures.\n * Supports Persian (fa) and English (en).\n */\nexport const ERROR_MESSAGES = {\n fa: {\n melliCode: \"کد ملی نامعتبر است\",\n cardNumber: \"شماره کارت نامعتبر است\",\n mobile: \"شماره موبایل نامعتبر است\",\n },\n en: {\n melliCode: \"Invalid national code\",\n cardNumber: \"Invalid card number\",\n mobile: \"Invalid mobile number\",\n },\n} as const;\n\nexport type Language = keyof typeof ERROR_MESSAGES;\n\nexport interface BaseOptions {\n /** Custom error message to override the default one */\n message?: string;\n /** Language for the default error message (default: 'fa') */\n locale?: Language;\n}\n\n/**\n * Helper to resolve the error message based on options.\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":"AAAA,OAAS,KAAAA,MAAS,MCIX,SAASC,EAAYC,EAAuB,CAEjD,GAAI,CAAC,WAAW,KAAKA,CAAI,EAAG,MAAO,GAGnC,IAAMC,EAAQ,SAASD,EAAK,CAAC,CAAC,EAC9B,GAAI,YAAY,KAAKA,CAAI,EAAG,MAAO,GAGnC,IAAME,EACJF,EACG,UAAU,EAAG,CAAC,EACd,MAAM,EAAE,EACR,OAAO,CAACG,EAAKC,EAAGC,IAAMF,EAAM,SAASC,CAAC,GAAK,GAAKC,GAAI,CAAC,EAAI,GAE9D,OAAOH,EAAM,EAAID,IAAUC,EAAMD,IAAU,GAAKC,CAClD,CAMO,SAASI,EAAaN,EAAuB,CAElD,IAAMO,EAAYP,EAAK,QAAQ,UAAW,EAAE,EAC5C,GAAI,CAAC,WAAW,KAAKO,CAAS,EAAG,MAAO,GAExC,IAAIL,EAAM,EACNM,EAAe,GAGnB,QAASH,EAAIE,EAAU,OAAS,EAAGF,GAAK,EAAGA,IAAK,CAC9C,IAAII,EAAQ,SAASF,EAAUF,CAAC,CAAC,EAE7BG,IACFC,GAAS,EACLA,EAAQ,IAAGA,GAAS,IAG1BP,GAAOO,EACPD,EAAe,CAACA,CAClB,CAEA,OAAON,EAAM,KAAO,CACtB,CAgBO,SAASQ,EACdC,EACA,CAAE,WAAAC,EAAa,UAAW,EAA6B,CAAC,EAC/C,CACT,IAAMC,EAAc,UAChBC,EAAU,GAEd,OAAIF,IAAe,GACjBE,EAAU,KAAKD,CAAW,IACjBD,IAAe,GACxBE,EAAU,IAAID,CAAW,IAEzBC,EAAU,gBAAgBD,CAAW,IAGhC,IAAI,OAAOC,CAAO,EAAE,KAAKH,CAAM,CACxC,CC5EO,IAAMI,EAAiB,CAC5B,GAAI,CACF,UAAW,gGACX,WAAY,wHACZ,OAAQ,mIACV,EACA,GAAI,CACF,UAAW,wBACX,WAAY,sBACZ,OAAQ,uBACV,CACF,EAcaC,EAAa,CACxBC,EACAC,IACW,CACX,GAAIA,GAAS,QAAS,OAAOA,EAAQ,QACrC,IAAMC,EAAOD,GAAS,QAAU,KAChC,OAAOH,EAAeI,CAAI,EAAEF,CAAG,CACjC,EF7BO,IAAMG,EAAcC,GACzBC,EAAE,OAAO,EAAE,OAAQC,GAAQC,EAAYD,CAAG,EAAG,CAC3C,QAASE,EAAW,YAAaJ,CAAO,CAC1C,CAAC,EAKUK,EAAeL,GAC1BC,EAAE,OAAO,EAAE,OAAQC,GAAQI,EAAaJ,CAAG,EAAG,CAC5C,QAASE,EAAW,aAAcJ,CAAO,CAC3C,CAAC,EASUO,EAAkBP,GAC7BC,EACG,OAAO,EACP,OACEC,GAAQM,EAAgBN,EAAK,CAAE,WAAYF,GAAS,UAAW,CAAC,EACjE,CACE,QAASI,EAAW,SAAUJ,CAAO,CACvC,CACF","names":["z","isMelliCode","code","check","sum","acc","x","i","isCardNumber","sanitized","shouldDouble","digit","isIranianMobile","mobile","strictZero","corePattern","pattern","ERROR_MESSAGES","getMessage","key","options","lang","zMelliCode","options","z","val","isMelliCode","getMessage","zCardNumber","isCardNumber","zIranianMobile","isIranianMobile"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zod-ir",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Essential Zod validators for Iranian specific data (National Code, Card Number, Mobile)",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"import": "./dist/index.mjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"prepublishOnly": "npm run test && npm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"zod",
|
|
25
|
+
"iran",
|
|
26
|
+
"validation",
|
|
27
|
+
"melli-code",
|
|
28
|
+
"react-hook-form"
|
|
29
|
+
],
|
|
30
|
+
"author": "Your Name",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"tsup": "^8.0.0",
|
|
34
|
+
"typescript": "^5.0.0",
|
|
35
|
+
"vitest": "^1.0.0",
|
|
36
|
+
"zod": "^3.0.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"zod": ">=3"
|
|
40
|
+
}
|
|
41
|
+
}
|