lanka-kit 0.0.0-stage → 1.1.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/README.md +328 -2
- package/lib/banks.d.ts +41 -0
- package/lib/banks.js +136 -0
- package/lib/currency.d.ts +25 -0
- package/lib/currency.js +251 -0
- package/lib/geo.d.ts +18 -0
- package/lib/geo.js +197 -0
- package/lib/identity-extra.d.ts +42 -0
- package/lib/identity-extra.js +146 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.js +16 -0
- package/lib/lankaqr.d.ts +13 -0
- package/lib/lankaqr.js +113 -0
- package/lib/license.d.ts +16 -0
- package/lib/license.js +42 -0
- package/lib/nic.d.ts +38 -0
- package/lib/nic.js +268 -0
- package/lib/phone.d.ts +24 -0
- package/lib/phone.js +158 -0
- package/lib/types.d.ts +81 -0
- package/lib/types.js +4 -0
- package/lib/unicode.d.ts +29 -0
- package/lib/unicode.js +70 -0
- package/lib/vehicle.d.ts +28 -0
- package/lib/vehicle.js +146 -0
- package/lib/zod.d.ts +69 -0
- package/lib/zod.js +98 -0
- package/package.json +43 -4
package/lib/currency.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LKR currency formatting and amount-to-words converter.
|
|
3
|
+
*/
|
|
4
|
+
const ONES_EN = [
|
|
5
|
+
'',
|
|
6
|
+
'One',
|
|
7
|
+
'Two',
|
|
8
|
+
'Three',
|
|
9
|
+
'Four',
|
|
10
|
+
'Five',
|
|
11
|
+
'Six',
|
|
12
|
+
'Seven',
|
|
13
|
+
'Eight',
|
|
14
|
+
'Nine',
|
|
15
|
+
'Ten',
|
|
16
|
+
'Eleven',
|
|
17
|
+
'Twelve',
|
|
18
|
+
'Thirteen',
|
|
19
|
+
'Fourteen',
|
|
20
|
+
'Fifteen',
|
|
21
|
+
'Sixteen',
|
|
22
|
+
'Seventeen',
|
|
23
|
+
'Eighteen',
|
|
24
|
+
'Nineteen',
|
|
25
|
+
];
|
|
26
|
+
const TENS_EN = [
|
|
27
|
+
'',
|
|
28
|
+
'',
|
|
29
|
+
'Twenty',
|
|
30
|
+
'Thirty',
|
|
31
|
+
'Forty',
|
|
32
|
+
'Fifty',
|
|
33
|
+
'Sixty',
|
|
34
|
+
'Seventy',
|
|
35
|
+
'Eighty',
|
|
36
|
+
'Ninety',
|
|
37
|
+
];
|
|
38
|
+
function convertThreeDigitsEn(num) {
|
|
39
|
+
let result = '';
|
|
40
|
+
const hundreds = Math.floor(num / 100);
|
|
41
|
+
const remainder = num % 100;
|
|
42
|
+
if (hundreds > 0) {
|
|
43
|
+
result += ONES_EN[hundreds] + ' Hundred';
|
|
44
|
+
if (remainder > 0) {
|
|
45
|
+
result += ' ';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (remainder > 0) {
|
|
49
|
+
if (remainder < 20) {
|
|
50
|
+
result += ONES_EN[remainder];
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const tens = Math.floor(remainder / 10);
|
|
54
|
+
const ones = remainder % 10;
|
|
55
|
+
result += TENS_EN[tens];
|
|
56
|
+
if (ones > 0) {
|
|
57
|
+
result += ' ' + ONES_EN[ones];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Format a numeric amount to Sri Lankan Rupee standard display.
|
|
65
|
+
* Example: 1540000.5 -> "Rs. 1,540,000.50"
|
|
66
|
+
*/
|
|
67
|
+
export function formatLkr(amount, options = {}) {
|
|
68
|
+
if (typeof amount !== 'number' || isNaN(amount)) {
|
|
69
|
+
return 'Rs. 0.00';
|
|
70
|
+
}
|
|
71
|
+
const prefix = options.prefix !== undefined ? options.prefix : 'Rs. ';
|
|
72
|
+
const suffix = options.suffix || '';
|
|
73
|
+
const decimals = options.decimals !== undefined ? options.decimals : 2;
|
|
74
|
+
const isNegative = amount < 0;
|
|
75
|
+
const absAmount = Math.abs(amount);
|
|
76
|
+
const parts = absAmount.toFixed(decimals).split('.');
|
|
77
|
+
const intPart = parts[0];
|
|
78
|
+
const decPart = parts[1];
|
|
79
|
+
// International grouping format
|
|
80
|
+
const withCommas = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
81
|
+
const formattedNum = decimals > 0 ? `${withCommas}.${decPart}` : withCommas;
|
|
82
|
+
const sign = isNegative ? '-' : '';
|
|
83
|
+
return `${sign}${prefix}${formattedNum}${suffix}`;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Convert a numeric monetary amount to English words for cheques and invoices.
|
|
87
|
+
* Example: 1540.50 -> "One Thousand Five Hundred Forty Rupees and Fifty Cents Only"
|
|
88
|
+
*/
|
|
89
|
+
function integerToWordsEn(num) {
|
|
90
|
+
if (num === 0)
|
|
91
|
+
return 'Zero';
|
|
92
|
+
const scales = ['', 'Thousand', 'Million', 'Billion', 'Trillion'];
|
|
93
|
+
let chunks = [];
|
|
94
|
+
let temp = num;
|
|
95
|
+
while (temp > 0) {
|
|
96
|
+
chunks.push(temp % 1000);
|
|
97
|
+
temp = Math.floor(temp / 1000);
|
|
98
|
+
}
|
|
99
|
+
let words = [];
|
|
100
|
+
for (let i = chunks.length - 1; i >= 0; i--) {
|
|
101
|
+
const chunk = chunks[i];
|
|
102
|
+
if (chunk > 0) {
|
|
103
|
+
const chunkWords = convertThreeDigitsEn(chunk);
|
|
104
|
+
const scale = scales[i];
|
|
105
|
+
words.push(scale ? `${chunkWords} ${scale}` : chunkWords);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return words.join(' ');
|
|
109
|
+
}
|
|
110
|
+
// Sinhala Numbers Data
|
|
111
|
+
const SI_ONES = {
|
|
112
|
+
0: 'බිංදුව',
|
|
113
|
+
1: 'එක',
|
|
114
|
+
2: 'දෙක',
|
|
115
|
+
3: 'තුන',
|
|
116
|
+
4: 'හතර',
|
|
117
|
+
5: 'පහ',
|
|
118
|
+
6: 'හය',
|
|
119
|
+
7: 'හත',
|
|
120
|
+
8: 'අට',
|
|
121
|
+
9: 'නවය',
|
|
122
|
+
10: 'දහය',
|
|
123
|
+
11: 'එකොළහ',
|
|
124
|
+
12: 'දොළහ',
|
|
125
|
+
13: 'දහතුන',
|
|
126
|
+
14: 'දාහතර',
|
|
127
|
+
15: 'පහළොව',
|
|
128
|
+
16: 'දාසය',
|
|
129
|
+
17: 'දාහත',
|
|
130
|
+
18: 'දහඅට',
|
|
131
|
+
19: 'දහනවය',
|
|
132
|
+
20: 'විස්ස',
|
|
133
|
+
30: 'තිහ',
|
|
134
|
+
40: 'හතළිහ',
|
|
135
|
+
50: 'පනහ',
|
|
136
|
+
60: 'හැට',
|
|
137
|
+
70: 'හැත්තෑව',
|
|
138
|
+
80: 'අසූව',
|
|
139
|
+
90: 'අනූව',
|
|
140
|
+
};
|
|
141
|
+
const SI_PREFIX = {
|
|
142
|
+
1: 'එක්',
|
|
143
|
+
2: 'දෙ',
|
|
144
|
+
3: 'තුන්',
|
|
145
|
+
4: 'හාර',
|
|
146
|
+
5: 'පන්',
|
|
147
|
+
6: 'හය',
|
|
148
|
+
7: 'හත්',
|
|
149
|
+
8: 'අට',
|
|
150
|
+
9: 'නවසීය',
|
|
151
|
+
};
|
|
152
|
+
function integerToWordsSi(num) {
|
|
153
|
+
if (num === 0)
|
|
154
|
+
return 'බිංදුව';
|
|
155
|
+
if (num < 0)
|
|
156
|
+
return 'සෘණ ' + integerToWordsSi(Math.abs(num));
|
|
157
|
+
if (num <= 20) {
|
|
158
|
+
return SI_ONES[num];
|
|
159
|
+
}
|
|
160
|
+
if (num < 100) {
|
|
161
|
+
const tens = Math.floor(num / 10) * 10;
|
|
162
|
+
const remainder = num % 10;
|
|
163
|
+
if (remainder === 0)
|
|
164
|
+
return SI_ONES[tens];
|
|
165
|
+
const tensPrefix = {
|
|
166
|
+
20: 'විසි',
|
|
167
|
+
30: 'තිස්',
|
|
168
|
+
40: 'හතළිස්',
|
|
169
|
+
50: 'පනස්',
|
|
170
|
+
60: 'හැට',
|
|
171
|
+
70: 'හැත්තෑ',
|
|
172
|
+
80: 'අසූ',
|
|
173
|
+
90: 'අනූ',
|
|
174
|
+
};
|
|
175
|
+
return (tensPrefix[tens] || '') + ' ' + SI_ONES[remainder];
|
|
176
|
+
}
|
|
177
|
+
if (num < 1000) {
|
|
178
|
+
const hundreds = Math.floor(num / 100);
|
|
179
|
+
const remainder = num % 100;
|
|
180
|
+
const hText = hundreds === 1 ? 'එක්සිය' : (SI_PREFIX[hundreds] || SI_ONES[hundreds]) + 'සිය';
|
|
181
|
+
if (remainder === 0)
|
|
182
|
+
return hText + 'යයි';
|
|
183
|
+
return hText + ' ' + integerToWordsSi(remainder);
|
|
184
|
+
}
|
|
185
|
+
if (num < 1000000) {
|
|
186
|
+
const thousands = Math.floor(num / 1000);
|
|
187
|
+
const remainder = num % 1000;
|
|
188
|
+
const tText = thousands === 1 ? 'එක්දහස්' : integerToWordsSi(thousands) + ' දහස්';
|
|
189
|
+
if (remainder === 0)
|
|
190
|
+
return thousands === 1 ? 'එක්දහසයි' : tText + 'යි';
|
|
191
|
+
return tText + ' ' + integerToWordsSi(remainder);
|
|
192
|
+
}
|
|
193
|
+
const millions = Math.floor(num / 1000000);
|
|
194
|
+
const remainder = num % 1000000;
|
|
195
|
+
const mText = integerToWordsSi(millions) + ' මිලියන';
|
|
196
|
+
if (remainder === 0)
|
|
197
|
+
return mText;
|
|
198
|
+
return mText + ' ' + integerToWordsSi(remainder);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Convert numeric amount into formal words.
|
|
202
|
+
*
|
|
203
|
+
* @param amount - Monetary number (e.g., 1540.50)
|
|
204
|
+
* @param options - Language and formatting options
|
|
205
|
+
* @returns Standard phrase (e.g., "One Thousand Five Hundred Forty Rupees and Fifty Cents Only")
|
|
206
|
+
*/
|
|
207
|
+
export function amountToWords(amount, options = {}) {
|
|
208
|
+
if (typeof amount !== 'number' || isNaN(amount)) {
|
|
209
|
+
return options.language === 'si' ? 'රුපියල් බිංදුවයි පමණි' : 'Zero Rupees Only';
|
|
210
|
+
}
|
|
211
|
+
const lang = options.language || 'en';
|
|
212
|
+
const includeCurrency = options.includeCurrency !== false;
|
|
213
|
+
const isNegative = amount < 0;
|
|
214
|
+
const absAmount = Math.abs(amount);
|
|
215
|
+
const rupees = Math.floor(absAmount);
|
|
216
|
+
const cents = Math.round((absAmount - rupees) * 100);
|
|
217
|
+
if (lang === 'si') {
|
|
218
|
+
let result = '';
|
|
219
|
+
if (rupees > 0) {
|
|
220
|
+
result += 'රුපියල් ' + integerToWordsSi(rupees);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
result += 'රුපියල් බිංදුවයි';
|
|
224
|
+
}
|
|
225
|
+
if (cents > 0) {
|
|
226
|
+
result += ' ශත ' + integerToWordsSi(cents);
|
|
227
|
+
}
|
|
228
|
+
result += ' පමණි';
|
|
229
|
+
return isNegative ? 'සෘණ ' + result : result;
|
|
230
|
+
}
|
|
231
|
+
// English
|
|
232
|
+
const rupeesWords = integerToWordsEn(rupees);
|
|
233
|
+
let result = '';
|
|
234
|
+
if (includeCurrency) {
|
|
235
|
+
const rupeeText = rupees === 1 ? 'Rupee' : 'Rupees';
|
|
236
|
+
result = `${rupeesWords} ${rupeeText}`;
|
|
237
|
+
if (cents > 0) {
|
|
238
|
+
const centsWords = integerToWordsEn(cents);
|
|
239
|
+
const centText = cents === 1 ? 'Cent' : 'Cents';
|
|
240
|
+
result += ` and ${centsWords} ${centText}`;
|
|
241
|
+
}
|
|
242
|
+
result += ' Only';
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
result = rupeesWords;
|
|
246
|
+
if (cents > 0) {
|
|
247
|
+
result += ` Point ${integerToWordsEn(cents)}`;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return isNegative ? `Negative ${result}` : result;
|
|
251
|
+
}
|
package/lib/geo.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { DistrictInfo, PostalLocation } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* 25 Administrative Districts of Sri Lanka mapped to their 9 Provinces.
|
|
4
|
+
*/
|
|
5
|
+
export declare const SRI_LANKA_DISTRICTS: DistrictInfo[];
|
|
6
|
+
export declare const SRI_LANKA_PROVINCES: readonly ["Western", "Central", "Southern", "Northern", "Eastern", "North Western", "North Central", "Uva", "Sabaragamuwa"];
|
|
7
|
+
/**
|
|
8
|
+
* Look up location information by Sri Lankan postal code.
|
|
9
|
+
*/
|
|
10
|
+
export declare function lookupPostalCode(postalCode: string | number): PostalLocation | null;
|
|
11
|
+
/**
|
|
12
|
+
* Returns all districts in a given province.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getDistrictsByProvince(provinceName: string): DistrictInfo[];
|
|
15
|
+
/**
|
|
16
|
+
* Searches Sri Lankan cities and postal codes by keyword.
|
|
17
|
+
*/
|
|
18
|
+
export declare function searchLocations(query: string): PostalLocation[];
|
package/lib/geo.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 25 Administrative Districts of Sri Lanka mapped to their 9 Provinces.
|
|
3
|
+
*/
|
|
4
|
+
export const SRI_LANKA_DISTRICTS = [
|
|
5
|
+
// Western Province
|
|
6
|
+
{ name: 'Colombo', code: 'COL', province: 'Western', postalCodeRange: [0, 10999] },
|
|
7
|
+
{ name: 'Gampaha', code: 'GAM', province: 'Western', postalCodeRange: [11000, 11999] },
|
|
8
|
+
{ name: 'Kalutara', code: 'KAL', province: 'Western', postalCodeRange: [12000, 12999] },
|
|
9
|
+
// Central Province
|
|
10
|
+
{ name: 'Kandy', code: 'KAN', province: 'Central', postalCodeRange: [20000, 20999] },
|
|
11
|
+
{ name: 'Matale', code: 'MTL', province: 'Central', postalCodeRange: [21000, 21999] },
|
|
12
|
+
{ name: 'Nuwara Eliya', code: 'NEL', province: 'Central', postalCodeRange: [22000, 22999] },
|
|
13
|
+
// Southern Province
|
|
14
|
+
{ name: 'Galle', code: 'GAL', province: 'Southern', postalCodeRange: [80000, 80999] },
|
|
15
|
+
{ name: 'Matara', code: 'MAT', province: 'Southern', postalCodeRange: [81000, 81999] },
|
|
16
|
+
{ name: 'Hambantota', code: 'HAM', province: 'Southern', postalCodeRange: [82000, 82999] },
|
|
17
|
+
// Northern Province
|
|
18
|
+
{ name: 'Jaffna', code: 'JAF', province: 'Northern', postalCodeRange: [40000, 40999] },
|
|
19
|
+
{ name: 'Kilinochchi', code: 'KIL', province: 'Northern', postalCodeRange: [44000, 44999] },
|
|
20
|
+
{ name: 'Mannar', code: 'MAN', province: 'Northern', postalCodeRange: [41000, 41999] },
|
|
21
|
+
{ name: 'Vavuniya', code: 'VAV', province: 'Northern', postalCodeRange: [43000, 43999] },
|
|
22
|
+
{ name: 'Mullaitivu', code: 'MUL', province: 'Northern', postalCodeRange: [42000, 42999] },
|
|
23
|
+
// Eastern Province
|
|
24
|
+
{ name: 'Batticaloa', code: 'BAT', province: 'Eastern', postalCodeRange: [30000, 30999] },
|
|
25
|
+
{ name: 'Ampara', code: 'AMP', province: 'Eastern', postalCodeRange: [32000, 32999] },
|
|
26
|
+
{ name: 'Trincomalee', code: 'TRI', province: 'Eastern', postalCodeRange: [31000, 31999] },
|
|
27
|
+
// North Western Province
|
|
28
|
+
{ name: 'Kurunegala', code: 'KUR', province: 'North Western', postalCodeRange: [60000, 60999] },
|
|
29
|
+
{ name: 'Puttalam', code: 'PUT', province: 'North Western', postalCodeRange: [61000, 61999] },
|
|
30
|
+
// North Central Province
|
|
31
|
+
{ name: 'Anuradhapura', code: 'ANU', province: 'North Central', postalCodeRange: [50000, 50999] },
|
|
32
|
+
{ name: 'Polonnaruwa', code: 'POL', province: 'North Central', postalCodeRange: [51000, 51999] },
|
|
33
|
+
// Uva Province
|
|
34
|
+
{ name: 'Badulla', code: 'BAD', province: 'Uva', postalCodeRange: [90000, 90999] },
|
|
35
|
+
{ name: 'Monaragala', code: 'MON', province: 'Uva', postalCodeRange: [91000, 91999] },
|
|
36
|
+
// Sabaragamuwa Province
|
|
37
|
+
{ name: 'Ratnapura', code: 'RAT', province: 'Sabaragamuwa', postalCodeRange: [70000, 70999] },
|
|
38
|
+
{ name: 'Kegalle', code: 'KEG', province: 'Sabaragamuwa', postalCodeRange: [71000, 71999] },
|
|
39
|
+
];
|
|
40
|
+
export const SRI_LANKA_PROVINCES = [
|
|
41
|
+
'Western',
|
|
42
|
+
'Central',
|
|
43
|
+
'Southern',
|
|
44
|
+
'Northern',
|
|
45
|
+
'Eastern',
|
|
46
|
+
'North Western',
|
|
47
|
+
'North Central',
|
|
48
|
+
'Uva',
|
|
49
|
+
'Sabaragamuwa',
|
|
50
|
+
];
|
|
51
|
+
/**
|
|
52
|
+
* Key Sri Lankan Postal Codes Registry
|
|
53
|
+
*/
|
|
54
|
+
const POSTAL_CODES = {
|
|
55
|
+
// Colombo postal zones (00100 - 01500)
|
|
56
|
+
'00100': { city: 'Colombo 01 (Fort)', district: 'Colombo', province: 'Western' },
|
|
57
|
+
'00200': { city: 'Colombo 02 (Slave Island)', district: 'Colombo', province: 'Western' },
|
|
58
|
+
'00300': { city: 'Colombo 03 (Kollupitiya)', district: 'Colombo', province: 'Western' },
|
|
59
|
+
'00400': { city: 'Colombo 04 (Bambalapitiya)', district: 'Colombo', province: 'Western' },
|
|
60
|
+
'00500': { city: 'Colombo 05 (Havelock Town / Kirulapone)', district: 'Colombo', province: 'Western' },
|
|
61
|
+
'00600': { city: 'Colombo 06 (Wellawatte / Pamankada)', district: 'Colombo', province: 'Western' },
|
|
62
|
+
'00700': { city: 'Colombo 07 (Cinnamon Gardens)', district: 'Colombo', province: 'Western' },
|
|
63
|
+
'00800': { city: 'Colombo 08 (Borella)', district: 'Colombo', province: 'Western' },
|
|
64
|
+
'00900': { city: 'Colombo 09 (Dematagoda)', district: 'Colombo', province: 'Western' },
|
|
65
|
+
'01000': { city: 'Colombo 10 (Maradana / Panchikawatte)', district: 'Colombo', province: 'Western' },
|
|
66
|
+
'01100': { city: 'Colombo 11 (Pettah)', district: 'Colombo', province: 'Western' },
|
|
67
|
+
'01200': { city: 'Colombo 12 (Hultsdorf)', district: 'Colombo', province: 'Western' },
|
|
68
|
+
'01300': { city: 'Colombo 13 (Kotahena / Bloemendhal)', district: 'Colombo', province: 'Western' },
|
|
69
|
+
'01400': { city: 'Colombo 14 (Grandpass)', district: 'Colombo', province: 'Western' },
|
|
70
|
+
'01500': { city: 'Colombo 15 (Mutwal / Modara / Mattakkuliya)', district: 'Colombo', province: 'Western' },
|
|
71
|
+
// Colombo Suburbs
|
|
72
|
+
'10100': { city: 'Battaramulla', district: 'Colombo', province: 'Western' },
|
|
73
|
+
'10115': { city: 'Rajagiriya', district: 'Colombo', province: 'Western' },
|
|
74
|
+
'10120': { city: 'Kotte (Sri Jayawardenepura)', district: 'Colombo', province: 'Western' },
|
|
75
|
+
'10200': { city: 'Malabe', district: 'Colombo', province: 'Western' },
|
|
76
|
+
'10230': { city: 'Kottawa', district: 'Colombo', province: 'Western' },
|
|
77
|
+
'10250': { city: 'Nugegoda', district: 'Colombo', province: 'Western' },
|
|
78
|
+
'10280': { city: 'Maharagama', district: 'Colombo', province: 'Western' },
|
|
79
|
+
'10300': { city: 'Piliyandala', district: 'Colombo', province: 'Western' },
|
|
80
|
+
'10350': { city: 'Dehiwala', district: 'Colombo', province: 'Western' },
|
|
81
|
+
'10370': { city: 'Mount Lavinia', district: 'Colombo', province: 'Western' },
|
|
82
|
+
'10400': { city: 'Moratuwa', district: 'Colombo', province: 'Western' },
|
|
83
|
+
'10500': { city: 'Homagama', district: 'Colombo', province: 'Western' },
|
|
84
|
+
'10640': { city: 'Avissawella', district: 'Colombo', province: 'Western' },
|
|
85
|
+
// Gampaha District
|
|
86
|
+
'11000': { city: 'Gampaha', district: 'Gampaha', province: 'Western' },
|
|
87
|
+
'11300': { city: 'Kelaniya', district: 'Gampaha', province: 'Western' },
|
|
88
|
+
'11500': { city: 'Negombo', district: 'Gampaha', province: 'Western' },
|
|
89
|
+
'11540': { city: 'Katunayake (BIA)', district: 'Gampaha', province: 'Western' },
|
|
90
|
+
'11600': { city: 'Ja-Ela', district: 'Gampaha', province: 'Western' },
|
|
91
|
+
'11700': { city: 'Wattala', district: 'Gampaha', province: 'Western' },
|
|
92
|
+
'11850': { city: 'Kiribathgoda', district: 'Gampaha', province: 'Western' },
|
|
93
|
+
// Kalutara District
|
|
94
|
+
'12000': { city: 'Kalutara', district: 'Kalutara', province: 'Western' },
|
|
95
|
+
'12500': { city: 'Panadura', district: 'Kalutara', province: 'Western' },
|
|
96
|
+
'12580': { city: 'Horana', district: 'Kalutara', province: 'Western' },
|
|
97
|
+
'12070': { city: 'Beruwala', district: 'Kalutara', province: 'Western' },
|
|
98
|
+
'12090': { city: 'Aluthgama', district: 'Kalutara', province: 'Western' },
|
|
99
|
+
// Kandy District
|
|
100
|
+
'20000': { city: 'Kandy', district: 'Kandy', province: 'Central' },
|
|
101
|
+
'20400': { city: 'Peradeniya', district: 'Kandy', province: 'Central' },
|
|
102
|
+
'20500': { city: 'Gampola', district: 'Kandy', province: 'Central' },
|
|
103
|
+
'20100': { city: 'Katugastota', district: 'Kandy', province: 'Central' },
|
|
104
|
+
// Galle District
|
|
105
|
+
'80000': { city: 'Galle', district: 'Galle', province: 'Southern' },
|
|
106
|
+
'80240': { city: 'Hikkaduwa', district: 'Galle', province: 'Southern' },
|
|
107
|
+
'80300': { city: 'Ambalangoda', district: 'Galle', province: 'Southern' },
|
|
108
|
+
'80480': { city: 'Karapitiya', district: 'Galle', province: 'Southern' },
|
|
109
|
+
// Matara District
|
|
110
|
+
'81000': { city: 'Matara', district: 'Matara', province: 'Southern' },
|
|
111
|
+
'81100': { city: 'Mirissa', district: 'Matara', province: 'Southern' },
|
|
112
|
+
'81200': { city: 'Weligama', district: 'Matara', province: 'Southern' },
|
|
113
|
+
// Hambantota District
|
|
114
|
+
'82000': { city: 'Hambantota', district: 'Hambantota', province: 'Southern' },
|
|
115
|
+
'82200': { city: 'Tangalle', district: 'Hambantota', province: 'Southern' },
|
|
116
|
+
// Jaffna District
|
|
117
|
+
'40000': { city: 'Jaffna', district: 'Jaffna', province: 'Northern' },
|
|
118
|
+
'40030': { city: 'Nallur', district: 'Jaffna', province: 'Northern' },
|
|
119
|
+
'40080': { city: 'Chavakachcheri', district: 'Jaffna', province: 'Northern' },
|
|
120
|
+
'40200': { city: 'Point Pedro', district: 'Jaffna', province: 'Northern' },
|
|
121
|
+
// Kurunegala District
|
|
122
|
+
'60000': { city: 'Kurunegala', district: 'Kurunegala', province: 'North Western' },
|
|
123
|
+
'60200': { city: 'Kuliyapitiya', district: 'Kurunegala', province: 'North Western' },
|
|
124
|
+
// Anuradhapura District
|
|
125
|
+
'50000': { city: 'Anuradhapura', district: 'Anuradhapura', province: 'North Central' },
|
|
126
|
+
// Ratnapura District
|
|
127
|
+
'70000': { city: 'Ratnapura', district: 'Ratnapura', province: 'Sabaragamuwa' },
|
|
128
|
+
// Badulla District
|
|
129
|
+
'90000': { city: 'Badulla', district: 'Badulla', province: 'Uva' },
|
|
130
|
+
'90100': { city: 'Bandarawela', district: 'Badulla', province: 'Uva' },
|
|
131
|
+
'90090': { city: 'Ella', district: 'Badulla', province: 'Uva' },
|
|
132
|
+
// Batticaloa & Trincomalee
|
|
133
|
+
'30000': { city: 'Batticaloa', district: 'Batticaloa', province: 'Eastern' },
|
|
134
|
+
'31000': { city: 'Trincomalee', district: 'Trincomalee', province: 'Eastern' },
|
|
135
|
+
'32000': { city: 'Ampara', district: 'Ampara', province: 'Eastern' },
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Look up location information by Sri Lankan postal code.
|
|
139
|
+
*/
|
|
140
|
+
export function lookupPostalCode(postalCode) {
|
|
141
|
+
const code = String(postalCode).trim().padStart(5, '0');
|
|
142
|
+
const found = POSTAL_CODES[code];
|
|
143
|
+
if (found) {
|
|
144
|
+
return {
|
|
145
|
+
postalCode: code,
|
|
146
|
+
city: found.city,
|
|
147
|
+
district: found.district,
|
|
148
|
+
province: found.province,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// Fallback: estimate district and province from postal code range
|
|
152
|
+
const numCode = parseInt(code, 10);
|
|
153
|
+
if (!isNaN(numCode)) {
|
|
154
|
+
const matchedDistrict = SRI_LANKA_DISTRICTS.find((d) => d.postalCodeRange &&
|
|
155
|
+
numCode >= d.postalCodeRange[0] &&
|
|
156
|
+
numCode <= d.postalCodeRange[1]);
|
|
157
|
+
if (matchedDistrict) {
|
|
158
|
+
return {
|
|
159
|
+
postalCode: code,
|
|
160
|
+
city: `${matchedDistrict.name} Region`,
|
|
161
|
+
district: matchedDistrict.name,
|
|
162
|
+
province: matchedDistrict.province,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Returns all districts in a given province.
|
|
170
|
+
*/
|
|
171
|
+
export function getDistrictsByProvince(provinceName) {
|
|
172
|
+
const norm = provinceName.trim().toLowerCase();
|
|
173
|
+
return SRI_LANKA_DISTRICTS.filter((d) => d.province.toLowerCase() === norm);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Searches Sri Lankan cities and postal codes by keyword.
|
|
177
|
+
*/
|
|
178
|
+
export function searchLocations(query) {
|
|
179
|
+
const q = query.trim().toLowerCase();
|
|
180
|
+
if (!q)
|
|
181
|
+
return [];
|
|
182
|
+
const results = [];
|
|
183
|
+
for (const [code, info] of Object.entries(POSTAL_CODES)) {
|
|
184
|
+
if (code.includes(q) ||
|
|
185
|
+
info.city.toLowerCase().includes(q) ||
|
|
186
|
+
info.district.toLowerCase().includes(q) ||
|
|
187
|
+
info.province.toLowerCase().includes(q)) {
|
|
188
|
+
results.push({
|
|
189
|
+
postalCode: code,
|
|
190
|
+
city: info.city,
|
|
191
|
+
district: info.district,
|
|
192
|
+
province: info.province,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return results;
|
|
197
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation for Sri Lankan passports, TIN, and business registration numbers.
|
|
3
|
+
*/
|
|
4
|
+
export interface PassportResult {
|
|
5
|
+
isValid: boolean;
|
|
6
|
+
clean: string;
|
|
7
|
+
type: 'Regular (N-Series)' | 'Diplomatic (D-Series)' | 'Official (O-Series)' | 'Legacy (M-Series)' | 'Biometric (PA-Series)' | 'Unknown';
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface TinResult {
|
|
11
|
+
isValid: boolean;
|
|
12
|
+
clean: string;
|
|
13
|
+
format: 'NEW_10_DIGIT' | 'LEGACY_9_DIGIT' | 'UNKNOWN';
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface BrnResult {
|
|
17
|
+
isValid: boolean;
|
|
18
|
+
clean: string;
|
|
19
|
+
type: 'Private Limited (PV)' | 'Public Limited (PB)' | 'Guarantee (GA/GL)' | 'Foreign/Other' | 'Unknown';
|
|
20
|
+
registrationNumber: string;
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Validates a Sri Lankan Passport Number.
|
|
25
|
+
* Standard citizen passports start with 'N' followed by 7 digits (e.g., N1234567).
|
|
26
|
+
* Diplomatic ('D'), Official ('O'), Legacy ('M'), and new e-passports ('PA') are also supported.
|
|
27
|
+
*/
|
|
28
|
+
export declare function validatePassport(input: string): PassportResult;
|
|
29
|
+
/**
|
|
30
|
+
* Validates an Inland Revenue Department (IRD) Taxpayer Identification Number (TIN).
|
|
31
|
+
* Mandatory in Sri Lanka for opening bank accounts, motor vehicle registrations, etc.
|
|
32
|
+
* Formats: 9-digit or modern 10-digit numeric.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateTin(input: string): TinResult;
|
|
35
|
+
/**
|
|
36
|
+
* Validates a Sri Lankan Business Registration Number (Registrar of Companies - ROC).
|
|
37
|
+
* Examples:
|
|
38
|
+
* - PV 12345 / PV 00123456 (Private Limited)
|
|
39
|
+
* - PB 1234 (Public Limited)
|
|
40
|
+
* - GA 1234 / GL 1234 (Company Limited by Guarantee)
|
|
41
|
+
*/
|
|
42
|
+
export declare function validateBrn(input: string): BrnResult;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation for Sri Lankan passports, TIN, and business registration numbers.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Validates a Sri Lankan Passport Number.
|
|
6
|
+
* Standard citizen passports start with 'N' followed by 7 digits (e.g., N1234567).
|
|
7
|
+
* Diplomatic ('D'), Official ('O'), Legacy ('M'), and new e-passports ('PA') are also supported.
|
|
8
|
+
*/
|
|
9
|
+
export function validatePassport(input) {
|
|
10
|
+
if (!input || typeof input !== 'string') {
|
|
11
|
+
return { isValid: false, clean: '', type: 'Unknown', error: 'Input must be a non-empty string' };
|
|
12
|
+
}
|
|
13
|
+
const clean = input.trim().toUpperCase().replace(/[\s-]/g, '');
|
|
14
|
+
// 1. Regular N-series: N + 7 digits (e.g., N1234567)
|
|
15
|
+
if (/^N\d{7}$/.test(clean)) {
|
|
16
|
+
return {
|
|
17
|
+
isValid: true,
|
|
18
|
+
clean,
|
|
19
|
+
type: 'Regular (N-Series)',
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// 2. Biometric PA-series: PA + 7 digits
|
|
23
|
+
if (/^PA\d{7}$/.test(clean)) {
|
|
24
|
+
return {
|
|
25
|
+
isValid: true,
|
|
26
|
+
clean,
|
|
27
|
+
type: 'Biometric (PA-Series)',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// 3. Diplomatic D-series: D + 7 digits
|
|
31
|
+
if (/^D\d{7}$/.test(clean)) {
|
|
32
|
+
return {
|
|
33
|
+
isValid: true,
|
|
34
|
+
clean,
|
|
35
|
+
type: 'Diplomatic (D-Series)',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// 4. Official O-series: O + 7 digits
|
|
39
|
+
if (/^O\d{7}$/.test(clean)) {
|
|
40
|
+
return {
|
|
41
|
+
isValid: true,
|
|
42
|
+
clean,
|
|
43
|
+
type: 'Official (O-Series)',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// 5. Legacy M-series: M + 7 digits
|
|
47
|
+
if (/^M\d{7}$/.test(clean)) {
|
|
48
|
+
return {
|
|
49
|
+
isValid: true,
|
|
50
|
+
clean,
|
|
51
|
+
type: 'Legacy (M-Series)',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
isValid: false,
|
|
56
|
+
clean,
|
|
57
|
+
type: 'Unknown',
|
|
58
|
+
error: 'Invalid Sri Lankan passport number format. Expected format like N1234567 or PA1234567.',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Validates an Inland Revenue Department (IRD) Taxpayer Identification Number (TIN).
|
|
63
|
+
* Mandatory in Sri Lanka for opening bank accounts, motor vehicle registrations, etc.
|
|
64
|
+
* Formats: 9-digit or modern 10-digit numeric.
|
|
65
|
+
*/
|
|
66
|
+
export function validateTin(input) {
|
|
67
|
+
if (!input || typeof input !== 'string') {
|
|
68
|
+
return { isValid: false, clean: '', format: 'UNKNOWN', error: 'Input must be a non-empty string' };
|
|
69
|
+
}
|
|
70
|
+
const clean = input.trim().replace(/[\s-]/g, '');
|
|
71
|
+
if (!/^\d+$/.test(clean)) {
|
|
72
|
+
return {
|
|
73
|
+
isValid: false,
|
|
74
|
+
clean,
|
|
75
|
+
format: 'UNKNOWN',
|
|
76
|
+
error: 'TIN must contain only numeric digits',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (clean.length === 10) {
|
|
80
|
+
return {
|
|
81
|
+
isValid: true,
|
|
82
|
+
clean,
|
|
83
|
+
format: 'NEW_10_DIGIT',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (clean.length === 9) {
|
|
87
|
+
return {
|
|
88
|
+
isValid: true,
|
|
89
|
+
clean,
|
|
90
|
+
format: 'LEGACY_9_DIGIT',
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
isValid: false,
|
|
95
|
+
clean,
|
|
96
|
+
format: 'UNKNOWN',
|
|
97
|
+
error: `Invalid TIN length (${clean.length} digits). Expected 9 or 10 digits.`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Validates a Sri Lankan Business Registration Number (Registrar of Companies - ROC).
|
|
102
|
+
* Examples:
|
|
103
|
+
* - PV 12345 / PV 00123456 (Private Limited)
|
|
104
|
+
* - PB 1234 (Public Limited)
|
|
105
|
+
* - GA 1234 / GL 1234 (Company Limited by Guarantee)
|
|
106
|
+
*/
|
|
107
|
+
export function validateBrn(input) {
|
|
108
|
+
if (!input || typeof input !== 'string') {
|
|
109
|
+
return {
|
|
110
|
+
isValid: false,
|
|
111
|
+
clean: '',
|
|
112
|
+
type: 'Unknown',
|
|
113
|
+
registrationNumber: '',
|
|
114
|
+
error: 'Input must be a non-empty string',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const clean = input.trim().toUpperCase().replace(/\s+/g, ' ');
|
|
118
|
+
// Match ROC formats: PV 12345, PV00123456, PB 1234, GA 1234
|
|
119
|
+
const rocRegex = /^(PV|PB|GA|GL)\s*(\d{4,8})$/;
|
|
120
|
+
const match = clean.match(rocRegex);
|
|
121
|
+
if (match) {
|
|
122
|
+
const prefix = match[1];
|
|
123
|
+
const number = match[2];
|
|
124
|
+
const standardized = `${prefix} ${number}`;
|
|
125
|
+
let type = 'Foreign/Other';
|
|
126
|
+
if (prefix === 'PV')
|
|
127
|
+
type = 'Private Limited (PV)';
|
|
128
|
+
else if (prefix === 'PB')
|
|
129
|
+
type = 'Public Limited (PB)';
|
|
130
|
+
else if (prefix === 'GA' || prefix === 'GL')
|
|
131
|
+
type = 'Guarantee (GA/GL)';
|
|
132
|
+
return {
|
|
133
|
+
isValid: true,
|
|
134
|
+
clean: standardized,
|
|
135
|
+
type,
|
|
136
|
+
registrationNumber: number,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
isValid: false,
|
|
141
|
+
clean,
|
|
142
|
+
type: 'Unknown',
|
|
143
|
+
registrationNumber: '',
|
|
144
|
+
error: 'Invalid Sri Lankan ROC registration number. Expected format like PV 12345 or PB 00123456.',
|
|
145
|
+
};
|
|
146
|
+
}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lanka-kit
|
|
3
|
+
* Sri Lankan developer utility toolkit.
|
|
4
|
+
*/
|
|
5
|
+
export * from './types';
|
|
6
|
+
export * from './nic';
|
|
7
|
+
export * from './phone';
|
|
8
|
+
export * from './geo';
|
|
9
|
+
export * from './license';
|
|
10
|
+
export * from './lankaqr';
|
|
11
|
+
export * from './banks';
|
|
12
|
+
export * from './currency';
|
|
13
|
+
export * from './vehicle';
|
|
14
|
+
export * from './identity-extra';
|
|
15
|
+
export * from './unicode';
|
|
16
|
+
export * from './zod';
|