@xeplr/utils 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/LICENSE +21 -0
- package/index.js +46 -0
- package/isomorphic/codes.js +51 -0
- package/isomorphic/countries.js +200 -0
- package/isomorphic/countries.json +786 -0
- package/isomorphic/crypto.js +123 -0
- package/isomorphic/emailNormalizer.js +56 -0
- package/isomorphic/helpers.js +52 -0
- package/isomorphic/index.js +93 -0
- package/isomorphic/messages.js +36 -0
- package/isomorphic/responseReader.js +57 -0
- package/isomorphic/states.js +44 -0
- package/isomorphic/states.json +296 -0
- package/lib/cache.js +166 -0
- package/lib/email.js +303 -0
- package/lib/fileUploader.js +135 -0
- package/lib/helpers.js +18 -0
- package/lib/logger.js +51 -0
- package/lib/queue.js +160 -0
- package/lib/rateLimiter.js +100 -0
- package/lib/response.js +86 -0
- package/lib/sms.js +278 -0
- package/package.json +40 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AES-256-GCM encrypt/decrypt — isomorphic (browser + Node).
|
|
3
|
+
* Uses Web Crypto API (available in all modern browsers and Node 16+).
|
|
4
|
+
* Zero dependencies.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* var { encrypt, decrypt } = require('@xeplr/utils/isomorphic/crypto');
|
|
8
|
+
*
|
|
9
|
+
* var encrypted = await encrypt('hello world', 'my-secret-key');
|
|
10
|
+
* var decrypted = await decrypt(encrypted, 'my-secret-key');
|
|
11
|
+
* // decrypted === 'hello world'
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
var subtle = typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.subtle
|
|
15
|
+
? globalThis.crypto.subtle
|
|
16
|
+
: null;
|
|
17
|
+
|
|
18
|
+
function getSubtle() {
|
|
19
|
+
if (subtle) return subtle;
|
|
20
|
+
// Node fallback
|
|
21
|
+
var nodeCrypto = require('crypto');
|
|
22
|
+
return nodeCrypto.webcrypto.subtle;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getRandomValues(buf) {
|
|
26
|
+
if (typeof globalThis !== 'undefined' && globalThis.crypto) {
|
|
27
|
+
return globalThis.crypto.getRandomValues(buf);
|
|
28
|
+
}
|
|
29
|
+
var nodeCrypto = require('crypto');
|
|
30
|
+
return nodeCrypto.webcrypto.getRandomValues(buf);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Derive a 256-bit key from a passphrase using PBKDF2.
|
|
35
|
+
*/
|
|
36
|
+
async function deriveKey(passphrase, salt) {
|
|
37
|
+
var s = getSubtle();
|
|
38
|
+
var enc = new TextEncoder();
|
|
39
|
+
var keyMaterial = await s.importKey('raw', enc.encode(passphrase), 'PBKDF2', false, ['deriveKey']);
|
|
40
|
+
return s.deriveKey(
|
|
41
|
+
{ name: 'PBKDF2', salt: salt, iterations: 100000, hash: 'SHA-256' },
|
|
42
|
+
keyMaterial,
|
|
43
|
+
{ name: 'AES-GCM', length: 256 },
|
|
44
|
+
false,
|
|
45
|
+
['encrypt', 'decrypt']
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function toBase64(buf) {
|
|
50
|
+
var bytes = new Uint8Array(buf);
|
|
51
|
+
if (typeof btoa === 'function') {
|
|
52
|
+
var binary = '';
|
|
53
|
+
for (var i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
54
|
+
return btoa(binary);
|
|
55
|
+
}
|
|
56
|
+
return Buffer.from(bytes).toString('base64');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function fromBase64(str) {
|
|
60
|
+
if (typeof atob === 'function') {
|
|
61
|
+
var binary = atob(str);
|
|
62
|
+
var bytes = new Uint8Array(binary.length);
|
|
63
|
+
for (var i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
64
|
+
return bytes;
|
|
65
|
+
}
|
|
66
|
+
return new Uint8Array(Buffer.from(str, 'base64'));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Encrypt a string with a passphrase.
|
|
71
|
+
* Returns a base64 string containing salt + iv + ciphertext.
|
|
72
|
+
*
|
|
73
|
+
* @param {string} plaintext - Text to encrypt
|
|
74
|
+
* @param {string} key - Passphrase
|
|
75
|
+
* @returns {Promise<string>} Base64-encoded encrypted payload
|
|
76
|
+
*/
|
|
77
|
+
async function encrypt(plaintext, key) {
|
|
78
|
+
var salt = new Uint8Array(16);
|
|
79
|
+
getRandomValues(salt);
|
|
80
|
+
var iv = new Uint8Array(12);
|
|
81
|
+
getRandomValues(iv);
|
|
82
|
+
|
|
83
|
+
var cryptoKey = await deriveKey(key, salt);
|
|
84
|
+
var enc = new TextEncoder();
|
|
85
|
+
var ciphertext = await getSubtle().encrypt(
|
|
86
|
+
{ name: 'AES-GCM', iv: iv },
|
|
87
|
+
cryptoKey,
|
|
88
|
+
enc.encode(plaintext)
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Pack: salt (16) + iv (12) + ciphertext
|
|
92
|
+
var packed = new Uint8Array(16 + 12 + ciphertext.byteLength);
|
|
93
|
+
packed.set(salt, 0);
|
|
94
|
+
packed.set(iv, 16);
|
|
95
|
+
packed.set(new Uint8Array(ciphertext), 28);
|
|
96
|
+
|
|
97
|
+
return toBase64(packed);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Decrypt a base64 payload with a passphrase.
|
|
102
|
+
*
|
|
103
|
+
* @param {string} encrypted - Base64-encoded encrypted payload from encrypt()
|
|
104
|
+
* @param {string} key - Passphrase (must match the one used to encrypt)
|
|
105
|
+
* @returns {Promise<string>} Decrypted plaintext
|
|
106
|
+
*/
|
|
107
|
+
async function decrypt(encrypted, key) {
|
|
108
|
+
var packed = fromBase64(encrypted);
|
|
109
|
+
var salt = packed.slice(0, 16);
|
|
110
|
+
var iv = packed.slice(16, 28);
|
|
111
|
+
var ciphertext = packed.slice(28);
|
|
112
|
+
|
|
113
|
+
var cryptoKey = await deriveKey(key, salt);
|
|
114
|
+
var decrypted = await getSubtle().decrypt(
|
|
115
|
+
{ name: 'AES-GCM', iv: iv },
|
|
116
|
+
cryptoKey,
|
|
117
|
+
ciphertext
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return new TextDecoder().decode(decrypted);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = { encrypt, decrypt };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email normalizer — strips dots and plus-aliases for known providers.
|
|
3
|
+
* Prevents vikas.bhandari@gmail.com and vikasbhandari@gmail.com from being different users.
|
|
4
|
+
*
|
|
5
|
+
* Known dot-insensitive providers:
|
|
6
|
+
* Gmail, Google Workspace, Googlemail
|
|
7
|
+
*
|
|
8
|
+
* Also strips +alias for these providers:
|
|
9
|
+
* vikas+test@gmail.com → vikas@gmail.com
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
var DOT_INSENSITIVE_DOMAINS = [
|
|
13
|
+
'gmail.com',
|
|
14
|
+
'googlemail.com'
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Normalize an email for duplicate detection.
|
|
19
|
+
* Returns lowercase, dot-stripped (for known providers), plus-alias stripped.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} email
|
|
22
|
+
* @returns {string} Normalized email
|
|
23
|
+
*/
|
|
24
|
+
function normalizeEmail(email) {
|
|
25
|
+
if (!email || typeof email !== 'string') return '';
|
|
26
|
+
|
|
27
|
+
var parts = email.trim().toLowerCase().split('@');
|
|
28
|
+
if (parts.length !== 2) return email.trim().toLowerCase();
|
|
29
|
+
|
|
30
|
+
var local = parts[0];
|
|
31
|
+
var domain = parts[1];
|
|
32
|
+
|
|
33
|
+
// Strip +alias for known providers
|
|
34
|
+
if (isDotInsensitive(domain)) {
|
|
35
|
+
var plusIndex = local.indexOf('+');
|
|
36
|
+
if (plusIndex > 0) {
|
|
37
|
+
local = local.slice(0, plusIndex);
|
|
38
|
+
}
|
|
39
|
+
// Strip dots
|
|
40
|
+
local = local.replace(/\./g, '');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return local + '@' + domain;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check if a domain ignores dots in the local part.
|
|
48
|
+
*/
|
|
49
|
+
function isDotInsensitive(domain) {
|
|
50
|
+
for (var i = 0; i < DOT_INSENSITIVE_DOMAINS.length; i++) {
|
|
51
|
+
if (domain === DOT_INSENSITIVE_DOMAINS[i]) return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { normalizeEmail, isDotInsensitive };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure JS helpers — safe for both browser and Node.
|
|
3
|
+
* No Node-specific APIs (crypto, fs, etc.).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Format a Date as ISO datetime string for database storage.
|
|
8
|
+
*/
|
|
9
|
+
function formatDbDateTime(date) {
|
|
10
|
+
return (date || new Date()).toISOString();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Format a Date for display (e.g. "15 Mar 2026").
|
|
15
|
+
*/
|
|
16
|
+
function formatDate(date, locale = 'en-GB') {
|
|
17
|
+
return (date || new Date()).toLocaleDateString(locale, {
|
|
18
|
+
day: 'numeric', month: 'short', year: 'numeric'
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Format a Date for display with time (e.g. "15 Mar 2026, 14:30").
|
|
24
|
+
*/
|
|
25
|
+
function formatDateTime(date, locale = 'en-GB') {
|
|
26
|
+
return (date || new Date()).toLocaleDateString(locale, {
|
|
27
|
+
day: 'numeric', month: 'short', year: 'numeric',
|
|
28
|
+
hour: '2-digit', minute: '2-digit'
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Simple deep-clone using structured clone (works in modern browsers + Node 17+).
|
|
34
|
+
* Falls back to JSON parse/stringify.
|
|
35
|
+
*/
|
|
36
|
+
function deepClone(obj) {
|
|
37
|
+
if (typeof structuredClone === 'function') return structuredClone(obj);
|
|
38
|
+
return JSON.parse(JSON.stringify(obj));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if a value is empty (null, undefined, '', [], {}).
|
|
43
|
+
*/
|
|
44
|
+
function isEmpty(value) {
|
|
45
|
+
if (value == null) return true;
|
|
46
|
+
if (typeof value === 'string') return value.trim() === '';
|
|
47
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
48
|
+
if (typeof value === 'object') return Object.keys(value).length === 0;
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = { formatDbDateTime, mysqlDateTime: formatDbDateTime, formatDate, formatDateTime, deepClone, isEmpty };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xeplr-utils/isomorphic — runs everywhere (browser + Node).
|
|
3
|
+
* Zero Node-specific dependencies.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* // Backend (CJS)
|
|
7
|
+
* const { HTTP, STATUS, MESSAGES, msg } = require('@xeplr/utils/isomorphic');
|
|
8
|
+
*
|
|
9
|
+
* // UI (ESM via Vite)
|
|
10
|
+
* import { HTTP, STATUS, MESSAGES, msg } from 'xeplr-utils/isomorphic';
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { HTTP, STATUS } = require('./codes');
|
|
14
|
+
const { MESSAGES } = require('./messages');
|
|
15
|
+
const { readResponse } = require('./responseReader');
|
|
16
|
+
const helpers = require('./helpers');
|
|
17
|
+
const { encrypt, decrypt } = require('./crypto');
|
|
18
|
+
const { normalizeEmail, isDotInsensitive } = require('./emailNormalizer');
|
|
19
|
+
const { COUNTRIES } = require('./countries');
|
|
20
|
+
const { STATES, STATES_IN } = require('./states');
|
|
21
|
+
|
|
22
|
+
// --- Language resolution ---
|
|
23
|
+
|
|
24
|
+
let _lang = null;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Set the active language explicitly.
|
|
28
|
+
* configureLang('fr');
|
|
29
|
+
*/
|
|
30
|
+
function configureLang(lang) {
|
|
31
|
+
_lang = lang;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get current language.
|
|
36
|
+
* Priority: configureLang() > APP_LANG env var > 'en'
|
|
37
|
+
*/
|
|
38
|
+
function getLang() {
|
|
39
|
+
if (_lang) return _lang;
|
|
40
|
+
|
|
41
|
+
if (typeof process !== 'undefined' && process.env && process.env.APP_LANG) {
|
|
42
|
+
return process.env.APP_LANG;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (typeof window !== 'undefined' && window.__APP_LANG__) {
|
|
46
|
+
return window.__APP_LANG__;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return 'en';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get the i18n message string for a message key.
|
|
54
|
+
* msg('success') → 'Success'
|
|
55
|
+
* msg('success', 'fr') → 'Succès'
|
|
56
|
+
*/
|
|
57
|
+
function msg(messageKey, lang) {
|
|
58
|
+
const l = lang || getLang();
|
|
59
|
+
const m = MESSAGES[messageKey];
|
|
60
|
+
if (!m) return messageKey;
|
|
61
|
+
return m[l] || m.en;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = {
|
|
65
|
+
// Language
|
|
66
|
+
configureLang,
|
|
67
|
+
getLang,
|
|
68
|
+
msg,
|
|
69
|
+
|
|
70
|
+
// Constants
|
|
71
|
+
HTTP,
|
|
72
|
+
STATUS,
|
|
73
|
+
MESSAGES,
|
|
74
|
+
|
|
75
|
+
// Response reader
|
|
76
|
+
readResponse,
|
|
77
|
+
|
|
78
|
+
// Helpers
|
|
79
|
+
...helpers,
|
|
80
|
+
|
|
81
|
+
// Crypto
|
|
82
|
+
encrypt,
|
|
83
|
+
decrypt,
|
|
84
|
+
|
|
85
|
+
// Email normalization
|
|
86
|
+
normalizeEmail,
|
|
87
|
+
isDotInsensitive,
|
|
88
|
+
|
|
89
|
+
// Static lookups
|
|
90
|
+
COUNTRIES,
|
|
91
|
+
STATES,
|
|
92
|
+
STATES_IN
|
|
93
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n message dictionary.
|
|
3
|
+
* Reusable strings keyed by a readable name.
|
|
4
|
+
* CODES reference these by message key.
|
|
5
|
+
*
|
|
6
|
+
* Add new languages by adding a key to each entry.
|
|
7
|
+
* Add new messages as needed — not limited to response codes.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const MESSAGES = {
|
|
11
|
+
success: { en: 'Success', fr: 'Succès' },
|
|
12
|
+
created: { en: 'Created successfully', fr: 'Créé avec succès' },
|
|
13
|
+
updated: { en: 'Updated successfully', fr: 'Mis à jour avec succès' },
|
|
14
|
+
deleted: { en: 'Deleted successfully', fr: 'Supprimé avec succès' },
|
|
15
|
+
|
|
16
|
+
bad_request: { en: 'Bad request', fr: 'Requête invalide' },
|
|
17
|
+
unauthorized: { en: 'Unauthorized', fr: 'Non autorisé' },
|
|
18
|
+
access_denied: { en: 'Access denied', fr: 'Accès refusé' },
|
|
19
|
+
not_found: { en: 'Not found', fr: 'Non trouvé' },
|
|
20
|
+
already_exists: { en: 'Already exists', fr: 'Existe déjà' },
|
|
21
|
+
validation_failed: { en: 'Validation failed', fr: 'Échec de validation' },
|
|
22
|
+
too_many_requests: { en: 'Too many requests', fr: 'Trop de requêtes' },
|
|
23
|
+
|
|
24
|
+
server_error: { en: 'Internal server error', fr: 'Erreur interne du serveur' },
|
|
25
|
+
service_unavailable: { en: 'Service temporarily unavailable', fr: 'Service temporairement indisponible' },
|
|
26
|
+
|
|
27
|
+
login_success: { en: 'Logged in successfully', fr: 'Connexion réussie' },
|
|
28
|
+
login_failed: { en: 'Invalid email or password', fr: 'Email ou mot de passe invalide' },
|
|
29
|
+
logout_success: { en: 'Logged out successfully', fr: 'Déconnexion réussie' },
|
|
30
|
+
account_not_active: { en: 'Account is not activated', fr: 'Le compte n\'est pas activé' },
|
|
31
|
+
password_reset_sent: { en: 'Password reset link sent', fr: 'Lien de réinitialisation envoyé' },
|
|
32
|
+
password_reset_success: { en: 'Password reset successfully', fr: 'Mot de passe réinitialisé avec succès' },
|
|
33
|
+
invalid_token: { en: 'Invalid or expired token', fr: 'Jeton invalide ou expiré' },
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
module.exports = { MESSAGES };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response reader — parses the standard API response format.
|
|
3
|
+
* Isomorphic — works in browser and Node.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* import { readResponse } from 'xeplr-utils/isomorphic';
|
|
7
|
+
*
|
|
8
|
+
* const result = readResponse(apiResponse);
|
|
9
|
+
* if (result.ok) {
|
|
10
|
+
* renderTable(result.dataArray);
|
|
11
|
+
* } else {
|
|
12
|
+
* showToast(result.message);
|
|
13
|
+
* console.log(result.error);
|
|
14
|
+
* }
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {object} response - The API response body
|
|
19
|
+
* @returns {object} Parsed response with convenience getters
|
|
20
|
+
*/
|
|
21
|
+
function readResponse(response) {
|
|
22
|
+
const code = response.code || '';
|
|
23
|
+
const message = response.message || '';
|
|
24
|
+
const error = response.error || null;
|
|
25
|
+
const dataArray = response.dataArray || [];
|
|
26
|
+
const updatedIds = response.updatedIds || [];
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
code,
|
|
30
|
+
message,
|
|
31
|
+
error,
|
|
32
|
+
dataArray,
|
|
33
|
+
updatedIds,
|
|
34
|
+
|
|
35
|
+
/** true if no error object present */
|
|
36
|
+
ok: error === null,
|
|
37
|
+
|
|
38
|
+
/** true if error object present */
|
|
39
|
+
hasError: error !== null,
|
|
40
|
+
|
|
41
|
+
/** true if dataArray has items */
|
|
42
|
+
hasData: dataArray.length > 0,
|
|
43
|
+
|
|
44
|
+
/** true if updatedIds has items */
|
|
45
|
+
hasUpdates: updatedIds.length > 0,
|
|
46
|
+
|
|
47
|
+
/** First item from dataArray, or null */
|
|
48
|
+
first: dataArray.length > 0 ? dataArray[0] : null,
|
|
49
|
+
|
|
50
|
+
/** Check if response code matches a specific STATUS */
|
|
51
|
+
is(statusCode) {
|
|
52
|
+
return code === statusCode;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { readResponse };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
var STATES_IN = [
|
|
2
|
+
{ code: 'AN', name: 'Andaman and Nicobar Islands' },
|
|
3
|
+
{ code: 'AP', name: 'Andhra Pradesh' },
|
|
4
|
+
{ code: 'AR', name: 'Arunachal Pradesh' },
|
|
5
|
+
{ code: 'AS', name: 'Assam' },
|
|
6
|
+
{ code: 'BR', name: 'Bihar' },
|
|
7
|
+
{ code: 'CH', name: 'Chandigarh' },
|
|
8
|
+
{ code: 'CT', name: 'Chhattisgarh' },
|
|
9
|
+
{ code: 'DN', name: 'Dadra and Nagar Haveli and Daman and Diu' },
|
|
10
|
+
{ code: 'DL', name: 'Delhi' },
|
|
11
|
+
{ code: 'GA', name: 'Goa' },
|
|
12
|
+
{ code: 'GJ', name: 'Gujarat' },
|
|
13
|
+
{ code: 'HR', name: 'Haryana' },
|
|
14
|
+
{ code: 'HP', name: 'Himachal Pradesh' },
|
|
15
|
+
{ code: 'JK', name: 'Jammu and Kashmir' },
|
|
16
|
+
{ code: 'JH', name: 'Jharkhand' },
|
|
17
|
+
{ code: 'KA', name: 'Karnataka' },
|
|
18
|
+
{ code: 'KL', name: 'Kerala' },
|
|
19
|
+
{ code: 'LA', name: 'Ladakh' },
|
|
20
|
+
{ code: 'LD', name: 'Lakshadweep' },
|
|
21
|
+
{ code: 'MP', name: 'Madhya Pradesh' },
|
|
22
|
+
{ code: 'MH', name: 'Maharashtra' },
|
|
23
|
+
{ code: 'MN', name: 'Manipur' },
|
|
24
|
+
{ code: 'ML', name: 'Meghalaya' },
|
|
25
|
+
{ code: 'MZ', name: 'Mizoram' },
|
|
26
|
+
{ code: 'NL', name: 'Nagaland' },
|
|
27
|
+
{ code: 'OR', name: 'Odisha' },
|
|
28
|
+
{ code: 'PY', name: 'Puducherry' },
|
|
29
|
+
{ code: 'PB', name: 'Punjab' },
|
|
30
|
+
{ code: 'RJ', name: 'Rajasthan' },
|
|
31
|
+
{ code: 'SK', name: 'Sikkim' },
|
|
32
|
+
{ code: 'TN', name: 'Tamil Nadu' },
|
|
33
|
+
{ code: 'TG', name: 'Telangana' },
|
|
34
|
+
{ code: 'TR', name: 'Tripura' },
|
|
35
|
+
{ code: 'UP', name: 'Uttar Pradesh' },
|
|
36
|
+
{ code: 'UT', name: 'Uttarakhand' },
|
|
37
|
+
{ code: 'WB', name: 'West Bengal' },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
var STATES = {
|
|
41
|
+
IN: STATES_IN,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
module.exports = { STATES, STATES_IN };
|