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.
@@ -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';