@qinisolabs/qiniso 0.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,1892 @@
1
+ // ../identifiers/dist/data/iban-countries.json
2
+ var iban_countries_default = {
3
+ AD: { length: 24, name: "Andorra" },
4
+ AE: { length: 23, name: "United Arab Emirates" },
5
+ AL: { length: 28, name: "Albania" },
6
+ AT: { length: 20, name: "Austria" },
7
+ AZ: { length: 28, name: "Azerbaijan" },
8
+ BA: { length: 20, name: "Bosnia and Herzegovina" },
9
+ BE: { length: 16, name: "Belgium" },
10
+ BG: { length: 22, name: "Bulgaria" },
11
+ BH: { length: 22, name: "Bahrain" },
12
+ BR: { length: 29, name: "Brazil" },
13
+ BY: { length: 28, name: "Belarus" },
14
+ CH: { length: 21, name: "Switzerland" },
15
+ CR: { length: 22, name: "Costa Rica" },
16
+ CY: { length: 28, name: "Cyprus" },
17
+ CZ: { length: 24, name: "Czechia" },
18
+ DE: { length: 22, name: "Germany" },
19
+ DK: { length: 18, name: "Denmark" },
20
+ DO: { length: 28, name: "Dominican Republic" },
21
+ EE: { length: 20, name: "Estonia" },
22
+ EG: { length: 29, name: "Egypt" },
23
+ ES: { length: 24, name: "Spain" },
24
+ FI: { length: 18, name: "Finland" },
25
+ FO: { length: 18, name: "Faroe Islands" },
26
+ FR: { length: 27, name: "France" },
27
+ GB: { length: 22, name: "United Kingdom" },
28
+ GE: { length: 22, name: "Georgia" },
29
+ GI: { length: 23, name: "Gibraltar" },
30
+ GL: { length: 18, name: "Greenland" },
31
+ GR: { length: 27, name: "Greece" },
32
+ GT: { length: 28, name: "Guatemala" },
33
+ HR: { length: 21, name: "Croatia" },
34
+ HU: { length: 28, name: "Hungary" },
35
+ IE: { length: 22, name: "Ireland" },
36
+ IL: { length: 23, name: "Israel" },
37
+ IQ: { length: 23, name: "Iraq" },
38
+ IS: { length: 26, name: "Iceland" },
39
+ IT: { length: 27, name: "Italy" },
40
+ JO: { length: 30, name: "Jordan" },
41
+ KW: { length: 30, name: "Kuwait" },
42
+ KZ: { length: 20, name: "Kazakhstan" },
43
+ LB: { length: 28, name: "Lebanon" },
44
+ LC: { length: 32, name: "Saint Lucia" },
45
+ LI: { length: 21, name: "Liechtenstein" },
46
+ LT: { length: 20, name: "Lithuania" },
47
+ LU: { length: 20, name: "Luxembourg" },
48
+ LV: { length: 21, name: "Latvia" },
49
+ LY: { length: 25, name: "Libya" },
50
+ MC: { length: 27, name: "Monaco" },
51
+ MD: { length: 24, name: "Moldova" },
52
+ ME: { length: 22, name: "Montenegro" },
53
+ MK: { length: 19, name: "North Macedonia" },
54
+ MR: { length: 27, name: "Mauritania" },
55
+ MT: { length: 31, name: "Malta" },
56
+ MU: { length: 30, name: "Mauritius" },
57
+ NL: { length: 18, name: "Netherlands" },
58
+ NO: { length: 15, name: "Norway" },
59
+ PK: { length: 24, name: "Pakistan" },
60
+ PL: { length: 28, name: "Poland" },
61
+ PS: { length: 29, name: "Palestine" },
62
+ PT: { length: 25, name: "Portugal" },
63
+ QA: { length: 29, name: "Qatar" },
64
+ RO: { length: 24, name: "Romania" },
65
+ RS: { length: 22, name: "Serbia" },
66
+ SA: { length: 24, name: "Saudi Arabia" },
67
+ SC: { length: 31, name: "Seychelles" },
68
+ SE: { length: 24, name: "Sweden" },
69
+ SI: { length: 19, name: "Slovenia" },
70
+ SK: { length: 24, name: "Slovakia" },
71
+ SM: { length: 27, name: "San Marino" },
72
+ TN: { length: 24, name: "Tunisia" },
73
+ TR: { length: 26, name: "Turkey" },
74
+ UA: { length: 29, name: "Ukraine" },
75
+ VA: { length: 22, name: "Vatican City" },
76
+ VG: { length: 24, name: "British Virgin Islands" },
77
+ XK: { length: 20, name: "Kosovo" }
78
+ };
79
+
80
+ // ../identifiers/dist/iban.js
81
+ var REGISTRY = iban_countries_default;
82
+ function mod97(rearranged) {
83
+ let remainder = 0;
84
+ for (const ch of rearranged) {
85
+ const code = ch.charCodeAt(0);
86
+ const value = code >= 65 && code <= 90 ? code - 55 : code - 48;
87
+ remainder = value > 9 ? (remainder * 100 + value) % 97 : (remainder * 10 + value) % 97;
88
+ }
89
+ return remainder;
90
+ }
91
+ function normalizeIban(input) {
92
+ return input.replace(/[\s]/g, "").toUpperCase();
93
+ }
94
+ function validateIban(input) {
95
+ const normalized = normalizeIban(input);
96
+ const result = {
97
+ input,
98
+ normalized,
99
+ valid: false,
100
+ countryCode: null,
101
+ country: null,
102
+ checkDigits: null,
103
+ bban: null,
104
+ errors: []
105
+ };
106
+ if (!/^[A-Z0-9]+$/.test(normalized)) {
107
+ result.errors.push("IBAN contains characters other than letters and digits.");
108
+ return result;
109
+ }
110
+ if (normalized.length < 4) {
111
+ result.errors.push("IBAN is too short to contain a country code and check digits.");
112
+ return result;
113
+ }
114
+ const cc = normalized.slice(0, 2);
115
+ const checkDigits = normalized.slice(2, 4);
116
+ const bban = normalized.slice(4);
117
+ result.countryCode = cc;
118
+ result.checkDigits = checkDigits;
119
+ result.bban = bban;
120
+ const spec = REGISTRY[cc];
121
+ if (!spec) {
122
+ result.errors.push(`Unknown or unsupported IBAN country code "${cc}".`);
123
+ return result;
124
+ }
125
+ result.country = spec.name;
126
+ if (!/^[A-Z]{2}[0-9]{2}/.test(normalized)) {
127
+ result.errors.push("Country code must be 2 letters followed by 2 check digits.");
128
+ return result;
129
+ }
130
+ if (normalized.length !== spec.length) {
131
+ result.errors.push(`Wrong length for ${spec.name}: expected ${spec.length}, got ${normalized.length}.`);
132
+ return result;
133
+ }
134
+ const rearranged = normalized.slice(4) + normalized.slice(0, 4);
135
+ if (mod97(rearranged) !== 1) {
136
+ result.errors.push("Checksum failed (ISO 7064 mod-97): the IBAN is not valid.");
137
+ return result;
138
+ }
139
+ result.valid = true;
140
+ return result;
141
+ }
142
+ function ibanCheckDigits(countryCode, bban) {
143
+ const cc = countryCode.toUpperCase();
144
+ const rearranged = bban.toUpperCase() + cc + "00";
145
+ const remainder = mod97(rearranged);
146
+ return String(98 - remainder).padStart(2, "0");
147
+ }
148
+ function supportedIbanCountries() {
149
+ return Object.keys(REGISTRY);
150
+ }
151
+
152
+ // ../identifiers/dist/data/card-bins.json
153
+ var card_bins_default = {
154
+ note: "IIN/BIN brand ranges. Detection uses longest-prefix-wins across all rules.",
155
+ brands: [
156
+ {
157
+ brand: "Visa",
158
+ rules: [{ min: 4, max: 4, len: 1 }],
159
+ lengths: [13, 16, 19]
160
+ },
161
+ {
162
+ brand: "Mastercard",
163
+ rules: [
164
+ { min: 51, max: 55, len: 2 },
165
+ { min: 2221, max: 2720, len: 4 }
166
+ ],
167
+ lengths: [16]
168
+ },
169
+ {
170
+ brand: "American Express",
171
+ rules: [
172
+ { min: 34, max: 34, len: 2 },
173
+ { min: 37, max: 37, len: 2 }
174
+ ],
175
+ lengths: [15]
176
+ },
177
+ {
178
+ brand: "Discover",
179
+ rules: [
180
+ { min: 6011, max: 6011, len: 4 },
181
+ { min: 644, max: 649, len: 3 },
182
+ { min: 65, max: 65, len: 2 },
183
+ { min: 622126, max: 622925, len: 6 }
184
+ ],
185
+ lengths: [16, 19]
186
+ },
187
+ {
188
+ brand: "Diners Club",
189
+ rules: [
190
+ { min: 300, max: 305, len: 3 },
191
+ { min: 3095, max: 3095, len: 4 },
192
+ { min: 36, max: 36, len: 2 },
193
+ { min: 38, max: 39, len: 2 }
194
+ ],
195
+ lengths: [14, 16, 19]
196
+ },
197
+ {
198
+ brand: "JCB",
199
+ rules: [{ min: 3528, max: 3589, len: 4 }],
200
+ lengths: [16, 17, 18, 19]
201
+ },
202
+ {
203
+ brand: "UnionPay",
204
+ rules: [{ min: 62, max: 62, len: 2 }],
205
+ lengths: [16, 17, 18, 19]
206
+ }
207
+ ]
208
+ };
209
+
210
+ // ../identifiers/dist/card.js
211
+ var BRANDS = card_bins_default.brands;
212
+ function luhnValid(digits2) {
213
+ if (!/^\d+$/.test(digits2))
214
+ return false;
215
+ let sum = 0;
216
+ let double = false;
217
+ for (let i = digits2.length - 1; i >= 0; i--) {
218
+ let d = digits2.charCodeAt(i) - 48;
219
+ if (double) {
220
+ d *= 2;
221
+ if (d > 9)
222
+ d -= 9;
223
+ }
224
+ sum += d;
225
+ double = !double;
226
+ }
227
+ return sum % 10 === 0;
228
+ }
229
+ function luhnCheckDigit(partial) {
230
+ let sum = 0;
231
+ let double = true;
232
+ for (let i = partial.length - 1; i >= 0; i--) {
233
+ let d = partial.charCodeAt(i) - 48;
234
+ if (double) {
235
+ d *= 2;
236
+ if (d > 9)
237
+ d -= 9;
238
+ }
239
+ sum += d;
240
+ double = !double;
241
+ }
242
+ return String((10 - sum % 10) % 10);
243
+ }
244
+ function detectBrand(digits2) {
245
+ if (!/^\d+$/.test(digits2))
246
+ return null;
247
+ let best = null;
248
+ for (const spec of BRANDS) {
249
+ for (const rule of spec.rules) {
250
+ if (digits2.length < rule.len)
251
+ continue;
252
+ const prefix = Number(digits2.slice(0, rule.len));
253
+ if (prefix >= rule.min && prefix <= rule.max) {
254
+ if (!best || rule.len > best.len) {
255
+ best = { brand: spec.brand, lengths: spec.lengths, len: rule.len };
256
+ }
257
+ }
258
+ }
259
+ }
260
+ return best ? { brand: best.brand, lengths: best.lengths } : null;
261
+ }
262
+ function validateCard(input) {
263
+ const normalized = input.replace(/[\s-]/g, "");
264
+ const result = {
265
+ input,
266
+ normalized,
267
+ valid: false,
268
+ luhnValid: false,
269
+ brand: null,
270
+ lengthValid: null,
271
+ errors: []
272
+ };
273
+ if (!/^\d+$/.test(normalized)) {
274
+ result.errors.push("Card number contains non-digit characters.");
275
+ return result;
276
+ }
277
+ if (normalized.length < 12 || normalized.length > 19) {
278
+ result.errors.push("Card number length is outside the valid 12-19 digit range.");
279
+ return result;
280
+ }
281
+ result.luhnValid = luhnValid(normalized);
282
+ if (!result.luhnValid)
283
+ result.errors.push("Luhn checksum failed.");
284
+ const detected = detectBrand(normalized);
285
+ if (detected) {
286
+ result.brand = detected.brand;
287
+ result.lengthValid = detected.lengths.includes(normalized.length);
288
+ if (!result.lengthValid) {
289
+ result.errors.push(`Length ${normalized.length} is not valid for ${detected.brand} (expected ${detected.lengths.join("/")}).`);
290
+ }
291
+ }
292
+ result.valid = result.luhnValid && result.lengthValid !== false;
293
+ return result;
294
+ }
295
+
296
+ // ../identifiers/dist/isbn.js
297
+ function isbn13CheckDigit(first12) {
298
+ if (!/^\d{12}$/.test(first12)) {
299
+ throw new Error("isbn13CheckDigit expects exactly 12 digits.");
300
+ }
301
+ let sum = 0;
302
+ for (let i = 0; i < 12; i++) {
303
+ const d = first12.charCodeAt(i) - 48;
304
+ sum += i % 2 === 0 ? d : d * 3;
305
+ }
306
+ return String((10 - sum % 10) % 10);
307
+ }
308
+ function validateIsbn13(input) {
309
+ const normalized = input.replace(/[\s-]/g, "");
310
+ const result = {
311
+ input,
312
+ normalized,
313
+ valid: false,
314
+ type: null,
315
+ checkDigit: null,
316
+ errors: []
317
+ };
318
+ if (!/^\d{13}$/.test(normalized)) {
319
+ result.errors.push("ISBN-13 must be exactly 13 digits (after removing hyphens/spaces).");
320
+ return result;
321
+ }
322
+ if (!/^(978|979)/.test(normalized)) {
323
+ result.errors.push('ISBN-13 must begin with the "978" or "979" GS1 prefix.');
324
+ return result;
325
+ }
326
+ result.type = "ISBN-13";
327
+ const expected = isbn13CheckDigit(normalized.slice(0, 12));
328
+ result.checkDigit = expected;
329
+ if (expected !== normalized[12]) {
330
+ result.errors.push(`Check digit failed: expected ${expected}, got ${normalized[12]}.`);
331
+ return result;
332
+ }
333
+ result.valid = true;
334
+ return result;
335
+ }
336
+
337
+ // ../identifiers/dist/vin.js
338
+ var TRANSLIT = {
339
+ A: 1,
340
+ B: 2,
341
+ C: 3,
342
+ D: 4,
343
+ E: 5,
344
+ F: 6,
345
+ G: 7,
346
+ H: 8,
347
+ J: 1,
348
+ K: 2,
349
+ L: 3,
350
+ M: 4,
351
+ N: 5,
352
+ P: 7,
353
+ R: 9,
354
+ S: 2,
355
+ T: 3,
356
+ U: 4,
357
+ V: 5,
358
+ W: 6,
359
+ X: 7,
360
+ Y: 8,
361
+ Z: 9,
362
+ "0": 0,
363
+ "1": 1,
364
+ "2": 2,
365
+ "3": 3,
366
+ "4": 4,
367
+ "5": 5,
368
+ "6": 6,
369
+ "7": 7,
370
+ "8": 8,
371
+ "9": 9
372
+ };
373
+ var WEIGHTS = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2];
374
+ function vinCheckDigit(vin) {
375
+ const v = vin.toUpperCase();
376
+ if (v.length !== 17 || !/^[A-HJ-NPR-Z0-9]{17}$/.test(v)) {
377
+ throw new Error("vinCheckDigit expects 17 chars from the VIN alphabet (no I/O/Q).");
378
+ }
379
+ let sum = 0;
380
+ for (let i = 0; i < 17; i++) {
381
+ if (i === 8)
382
+ continue;
383
+ sum += TRANSLIT[v[i]] * WEIGHTS[i];
384
+ }
385
+ const r = sum % 11;
386
+ return r === 10 ? "X" : String(r);
387
+ }
388
+ function validateVin(input) {
389
+ const normalized = input.trim().toUpperCase();
390
+ const result = {
391
+ input,
392
+ normalized,
393
+ valid: false,
394
+ checkDigit: null,
395
+ expectedCheckDigit: null,
396
+ errors: []
397
+ };
398
+ if (normalized.length !== 17) {
399
+ result.errors.push(`VIN must be exactly 17 characters (got ${normalized.length}).`);
400
+ return result;
401
+ }
402
+ if (/[IOQ]/.test(normalized)) {
403
+ result.errors.push("VIN must not contain the letters I, O or Q.");
404
+ return result;
405
+ }
406
+ if (!/^[A-HJ-NPR-Z0-9]{17}$/.test(normalized)) {
407
+ result.errors.push("VIN contains characters outside the allowed alphabet.");
408
+ return result;
409
+ }
410
+ const expected = vinCheckDigit(normalized);
411
+ result.checkDigit = normalized[8];
412
+ result.expectedCheckDigit = expected;
413
+ if (expected !== normalized[8]) {
414
+ result.errors.push(`Check digit (position 9) failed: expected ${expected}, got ${normalized[8]}.`);
415
+ return result;
416
+ }
417
+ result.valid = true;
418
+ return result;
419
+ }
420
+
421
+ // ../network/dist/data/tlds.json
422
+ var tlds_default = ["aaa", "aarp", "abb", "abbott", "abbvie", "abc", "able", "abogado", "abudhabi", "ac", "academy", "accenture", "accountant", "accountants", "aco", "actor", "ad", "ads", "adult", "ae", "aeg", "aero", "aetna", "af", "afl", "africa", "ag", "agakhan", "agency", "ai", "aig", "airbus", "airforce", "airtel", "akdn", "al", "alibaba", "alipay", "allfinanz", "allstate", "ally", "alsace", "alstom", "am", "amazon", "americanexpress", "americanfamily", "amex", "amfam", "amica", "amsterdam", "analytics", "android", "anquan", "anz", "ao", "aol", "apartments", "app", "apple", "aq", "aquarelle", "ar", "arab", "aramco", "archi", "army", "arpa", "art", "arte", "as", "asda", "asia", "associates", "at", "athleta", "attorney", "au", "auction", "audi", "audible", "audio", "auspost", "author", "auto", "autos", "aw", "aws", "ax", "axa", "az", "azure", "ba", "baby", "baidu", "banamex", "band", "bank", "bar", "barcelona", "barclaycard", "barclays", "barefoot", "bargains", "baseball", "basketball", "bauhaus", "bayern", "bb", "bbc", "bbt", "bbva", "bcg", "bcn", "bd", "be", "beats", "beauty", "beer", "berlin", "best", "bestbuy", "bet", "bf", "bg", "bh", "bharti", "bi", "bible", "bid", "bike", "bing", "bingo", "bio", "biz", "bj", "black", "blackfriday", "blockbuster", "blog", "bloomberg", "blue", "bm", "bms", "bmw", "bn", "bnpparibas", "bo", "boats", "boehringer", "bofa", "bom", "bond", "boo", "book", "booking", "bosch", "bostik", "boston", "bot", "boutique", "box", "br", "bradesco", "bridgestone", "broadway", "broker", "brother", "brussels", "bs", "bt", "build", "builders", "business", "buy", "buzz", "bv", "bw", "by", "bz", "bzh", "ca", "cab", "cafe", "cal", "call", "calvinklein", "cam", "camera", "camp", "canon", "capetown", "capital", "capitalone", "car", "caravan", "cards", "care", "career", "careers", "cars", "casa", "case", "cash", "casino", "cat", "catering", "catholic", "cba", "cbn", "cbre", "cc", "cd", "center", "ceo", "cern", "cf", "cfa", "cfd", "cg", "ch", "chanel", "channel", "charity", "chase", "chat", "cheap", "chintai", "christmas", "chrome", "church", "ci", "cipriani", "circle", "cisco", "citadel", "citi", "citic", "city", "ck", "cl", "claims", "cleaning", "click", "clinic", "clinique", "clothing", "cloud", "club", "clubmed", "cm", "cn", "co", "coach", "codes", "coffee", "college", "cologne", "com", "commbank", "community", "company", "compare", "computer", "comsec", "condos", "construction", "consulting", "contact", "contractors", "cooking", "cool", "coop", "corsica", "country", "coupon", "coupons", "courses", "cpa", "cr", "credit", "creditcard", "creditunion", "cricket", "crown", "crs", "cruise", "cruises", "cu", "cuisinella", "cv", "cw", "cx", "cy", "cymru", "cyou", "cz", "dad", "dance", "data", "date", "dating", "datsun", "day", "dclk", "dds", "de", "deal", "dealer", "deals", "degree", "delivery", "dell", "deloitte", "delta", "democrat", "dental", "dentist", "desi", "design", "dev", "dhl", "diamonds", "diet", "digital", "direct", "directory", "discount", "discover", "dish", "diy", "dj", "dk", "dm", "dnp", "do", "docs", "doctor", "dog", "domains", "dot", "download", "drive", "dtv", "dubai", "dupont", "durban", "dvag", "dvr", "dz", "earth", "eat", "ec", "eco", "edeka", "edu", "education", "ee", "eg", "email", "emerck", "energy", "engineer", "engineering", "enterprises", "epson", "equipment", "er", "ericsson", "erni", "es", "esq", "estate", "et", "eu", "eurovision", "eus", "events", "exchange", "expert", "exposed", "express", "extraspace", "fage", "fail", "fairwinds", "faith", "family", "fan", "fans", "farm", "farmers", "fashion", "fast", "fedex", "feedback", "ferrari", "ferrero", "fi", "fidelity", "fido", "film", "final", "finance", "financial", "fire", "firestone", "firmdale", "fish", "fishing", "fit", "fitness", "fj", "fk", "flickr", "flights", "flir", "florist", "flowers", "fly", "fm", "fo", "foo", "food", "football", "ford", "forex", "forsale", "forum", "foundation", "fox", "fr", "free", "fresenius", "frl", "frogans", "frontier", "ftr", "fujitsu", "fun", "fund", "furniture", "futbol", "fyi", "ga", "gal", "gallery", "gallo", "gallup", "game", "games", "gap", "garden", "gay", "gb", "gbiz", "gd", "gdn", "ge", "gea", "gent", "genting", "george", "gf", "gg", "ggee", "gh", "gi", "gift", "gifts", "gives", "giving", "gl", "glass", "gle", "global", "globo", "gm", "gmail", "gmbh", "gmo", "gmx", "gn", "godaddy", "gold", "goldpoint", "golf", "goodyear", "goog", "google", "gop", "got", "gov", "gp", "gq", "gr", "grainger", "graphics", "gratis", "green", "gripe", "grocery", "group", "gs", "gt", "gu", "gucci", "guge", "guide", "guitars", "guru", "gw", "gy", "hair", "hamburg", "hangout", "haus", "hbo", "hdfc", "hdfcbank", "health", "healthcare", "help", "helsinki", "here", "hermes", "hiphop", "hisamitsu", "hitachi", "hiv", "hk", "hkt", "hm", "hn", "hockey", "holdings", "holiday", "homedepot", "homegoods", "homes", "homesense", "honda", "horse", "hospital", "host", "hosting", "hot", "hotels", "hotmail", "house", "how", "hr", "hsbc", "ht", "hu", "hughes", "hyatt", "hyundai", "ibm", "icbc", "ice", "icu", "id", "ie", "ieee", "ifm", "ikano", "il", "im", "imamat", "imdb", "immo", "immobilien", "in", "inc", "industries", "infiniti", "info", "ing", "ink", "institute", "insurance", "insure", "int", "international", "intuit", "investments", "io", "ipiranga", "iq", "ir", "irish", "is", "ismaili", "ist", "istanbul", "it", "itau", "itv", "jaguar", "java", "jcb", "je", "jeep", "jetzt", "jewelry", "jio", "jll", "jm", "jmp", "jnj", "jo", "jobs", "joburg", "jot", "joy", "jp", "jpmorgan", "jprs", "juegos", "juniper", "kaufen", "kddi", "ke", "kerryhotels", "kerryproperties", "kfh", "kg", "kh", "ki", "kia", "kids", "kim", "kindle", "kitchen", "kiwi", "km", "kn", "koeln", "komatsu", "kosher", "kp", "kpmg", "kpn", "kr", "krd", "kred", "kuokgroup", "kw", "ky", "kyoto", "kz", "la", "lacaixa", "lamborghini", "lamer", "land", "landrover", "lanxess", "lasalle", "lat", "latino", "latrobe", "law", "lawyer", "lb", "lc", "lds", "lease", "leclerc", "lefrak", "legal", "lego", "lexus", "lgbt", "li", "lidl", "life", "lifeinsurance", "lifestyle", "lighting", "like", "lilly", "limited", "limo", "lincoln", "link", "live", "living", "lk", "llc", "llp", "loan", "loans", "locker", "locus", "lol", "london", "lotte", "lotto", "love", "lpl", "lplfinancial", "lr", "ls", "lt", "ltd", "ltda", "lu", "lundbeck", "luxe", "luxury", "lv", "ly", "ma", "madrid", "maif", "maison", "makeup", "man", "management", "mango", "map", "market", "marketing", "markets", "marriott", "marshalls", "mattel", "mba", "mc", "mckinsey", "md", "me", "med", "media", "meet", "melbourne", "meme", "memorial", "men", "menu", "merck", "merckmsd", "mg", "mh", "miami", "microsoft", "mil", "mini", "mint", "mit", "mitsubishi", "mk", "ml", "mlb", "mls", "mm", "mma", "mn", "mo", "mobi", "mobile", "moda", "moe", "moi", "mom", "monash", "money", "monster", "mormon", "mortgage", "moscow", "moto", "motorcycles", "mov", "movie", "mp", "mq", "mr", "ms", "msd", "mt", "mtn", "mtr", "mu", "museum", "music", "mv", "mw", "mx", "my", "mz", "na", "nab", "nagoya", "name", "navy", "nba", "nc", "ne", "nec", "net", "netbank", "netflix", "network", "neustar", "new", "news", "next", "nextdirect", "nexus", "nf", "nfl", "ng", "ngo", "nhk", "ni", "nico", "nike", "nikon", "ninja", "nissan", "nissay", "nl", "no", "nokia", "norton", "now", "nowruz", "nowtv", "np", "nr", "nra", "nrw", "ntt", "nu", "nyc", "nz", "obi", "observer", "office", "okinawa", "olayan", "olayangroup", "ollo", "om", "omega", "one", "ong", "onl", "online", "ooo", "open", "oracle", "orange", "org", "organic", "origins", "osaka", "otsuka", "ott", "ovh", "pa", "page", "panasonic", "paris", "pars", "partners", "parts", "party", "pay", "pccw", "pe", "pet", "pf", "pfizer", "pg", "ph", "pharmacy", "phd", "philips", "phone", "photo", "photography", "photos", "physio", "pics", "pictet", "pictures", "pid", "pin", "ping", "pink", "pioneer", "pizza", "pk", "pl", "place", "play", "playstation", "plumbing", "plus", "pm", "pn", "pnc", "pohl", "poker", "politie", "porn", "post", "pr", "praxi", "press", "prime", "pro", "prod", "productions", "prof", "progressive", "promo", "properties", "property", "protection", "pru", "prudential", "ps", "pt", "pub", "pw", "pwc", "py", "qa", "qpon", "quebec", "quest", "racing", "radio", "re", "read", "realestate", "realtor", "realty", "recipes", "red", "redumbrella", "rehab", "reise", "reisen", "reit", "reliance", "ren", "rent", "rentals", "repair", "report", "republican", "rest", "restaurant", "review", "reviews", "rexroth", "rich", "richardli", "ricoh", "ril", "rio", "rip", "ro", "rocks", "rodeo", "rogers", "room", "rs", "rsvp", "ru", "rugby", "ruhr", "run", "rw", "rwe", "ryukyu", "sa", "saarland", "safe", "safety", "sakura", "sale", "salon", "samsclub", "samsung", "sandvik", "sandvikcoromant", "sanofi", "sap", "sarl", "sas", "save", "saxo", "sb", "sbi", "sbs", "sc", "scb", "schaeffler", "schmidt", "scholarships", "school", "schule", "schwarz", "science", "scot", "sd", "se", "search", "seat", "secure", "security", "seek", "select", "sener", "services", "seven", "sew", "sex", "sexy", "sfr", "sg", "sh", "shangrila", "sharp", "shell", "shia", "shiksha", "shoes", "shop", "shopping", "shouji", "show", "si", "silk", "sina", "singles", "site", "sj", "sk", "ski", "skin", "sky", "skype", "sl", "sling", "sm", "smart", "smile", "sn", "sncf", "so", "soccer", "social", "softbank", "software", "sohu", "solar", "solutions", "song", "sony", "soy", "spa", "space", "sport", "spot", "sr", "srl", "ss", "st", "stada", "staples", "star", "statebank", "statefarm", "stc", "stcgroup", "stockholm", "storage", "store", "stream", "studio", "study", "style", "su", "sucks", "supplies", "supply", "support", "surf", "surgery", "suzuki", "sv", "swatch", "swiss", "sx", "sy", "sydney", "systems", "sz", "tab", "taipei", "talk", "taobao", "target", "tatamotors", "tatar", "tattoo", "tax", "taxi", "tc", "tci", "td", "tdk", "team", "tech", "technology", "tel", "temasek", "tennis", "teva", "tf", "tg", "th", "thd", "theater", "theatre", "tiaa", "tickets", "tienda", "tips", "tires", "tirol", "tj", "tjmaxx", "tjx", "tk", "tkmaxx", "tl", "tm", "tmall", "tn", "to", "today", "tokyo", "tools", "top", "toray", "toshiba", "total", "tours", "town", "toyota", "toys", "tr", "trade", "trading", "training", "travel", "travelers", "travelersinsurance", "trust", "trv", "tt", "tube", "tui", "tunes", "tushu", "tv", "tvs", "tw", "tz", "ua", "ubank", "ubs", "ug", "uk", "unicom", "university", "uno", "uol", "ups", "us", "uy", "uz", "va", "vacations", "vana", "vanguard", "vc", "ve", "vegas", "ventures", "verisign", "versicherung", "vet", "vg", "vi", "viajes", "video", "vig", "viking", "villas", "vin", "vip", "virgin", "visa", "vision", "viva", "vivo", "vlaanderen", "vn", "vodka", "volvo", "vote", "voting", "voto", "voyage", "vu", "wales", "walmart", "walter", "wang", "wanggou", "watch", "watches", "weather", "weatherchannel", "webcam", "weber", "website", "wed", "wedding", "weibo", "weir", "wf", "whoswho", "wien", "wiki", "williamhill", "win", "windows", "wine", "winners", "wme", "woodside", "work", "works", "world", "wow", "ws", "wtc", "wtf", "xbox", "xerox", "xihuan", "xin", "xn--11b4c3d", "xn--1ck2e1b", "xn--1qqw23a", "xn--2scrj9c", "xn--30rr7y", "xn--3bst00m", "xn--3ds443g", "xn--3e0b707e", "xn--3hcrj9c", "xn--3pxu8k", "xn--42c2d9a", "xn--45br5cyl", "xn--45brj9c", "xn--45q11c", "xn--4dbrk0ce", "xn--4gbrim", "xn--54b7fta0cc", "xn--55qw42g", "xn--55qx5d", "xn--5su34j936bgsg", "xn--5tzm5g", "xn--6frz82g", "xn--6qq986b3xl", "xn--80adxhks", "xn--80ao21a", "xn--80aqecdr1a", "xn--80asehdb", "xn--80aswg", "xn--8y0a063a", "xn--90a3ac", "xn--90ae", "xn--90ais", "xn--9dbq2a", "xn--9et52u", "xn--9krt00a", "xn--b4w605ferd", "xn--bck1b9a5dre4c", "xn--c1avg", "xn--c2br7g", "xn--cck2b3b", "xn--cckwcxetd", "xn--cg4bki", "xn--clchc0ea0b2g2a9gcd", "xn--czr694b", "xn--czrs0t", "xn--czru2d", "xn--d1acj3b", "xn--d1alf", "xn--e1a4c", "xn--eckvdtc9d", "xn--efvy88h", "xn--fct429k", "xn--fhbei", "xn--fiq228c5hs", "xn--fiq64b", "xn--fiqs8s", "xn--fiqz9s", "xn--fjq720a", "xn--flw351e", "xn--fpcrj9c3d", "xn--fzc2c9e2c", "xn--fzys8d69uvgm", "xn--g2xx48c", "xn--gckr3f0f", "xn--gecrj9c", "xn--gk3at1e", "xn--h2breg3eve", "xn--h2brj9c", "xn--h2brj9c8c", "xn--hxt814e", "xn--i1b6b1a6a2e", "xn--imr513n", "xn--io0a7i", "xn--j1aef", "xn--j1amh", "xn--j6w193g", "xn--jlq480n2rg", "xn--jvr189m", "xn--kcrx77d1x4a", "xn--kprw13d", "xn--kpry57d", "xn--kput3i", "xn--l1acc", "xn--lgbbat1ad8j", "xn--mgb9awbf", "xn--mgba3a3ejt", "xn--mgba3a4f16a", "xn--mgba7c0bbn0a", "xn--mgbaam7a8h", "xn--mgbab2bd", "xn--mgbah1a3hjkrd", "xn--mgbai9azgqp6j", "xn--mgbayh7gpa", "xn--mgbbh1a", "xn--mgbbh1a71e", "xn--mgbc0a9azcg", "xn--mgbca7dzdo", "xn--mgbcpq6gpa1a", "xn--mgberp4a5d4ar", "xn--mgbgu82a", "xn--mgbi4ecexp", "xn--mgbpl2fh", "xn--mgbt3dhd", "xn--mgbtx2b", "xn--mgbx4cd0ab", "xn--mix891f", "xn--mk1bu44c", "xn--mxtq1m", "xn--ngbc5azd", "xn--ngbe9e0a", "xn--ngbrx", "xn--node", "xn--nqv7f", "xn--nqv7fs00ema", "xn--nyqy26a", "xn--o3cw4h", "xn--ogbpf8fl", "xn--otu796d", "xn--p1acf", "xn--p1ai", "xn--pgbs0dh", "xn--pssy2u", "xn--q7ce6a", "xn--q9jyb4c", "xn--qcka1pmc", "xn--qxa6a", "xn--qxam", "xn--rhqv96g", "xn--rovu88b", "xn--rvc1e0am3e", "xn--s9brj9c", "xn--ses554g", "xn--t60b56a", "xn--tckwe", "xn--tiq49xqyj", "xn--unup4y", "xn--vermgensberater-ctb", "xn--vermgensberatung-pwb", "xn--vhquv", "xn--vuq861b", "xn--w4r85el8fhu5dnra", "xn--w4rs40l", "xn--wgbh1c", "xn--wgbl6a", "xn--xhq521b", "xn--xkc2al3hye2a", "xn--xkc2dl3a5ee0h", "xn--y9a3aq", "xn--yfro4i67o", "xn--ygbi2ammx", "xn--zfr164b", "xxx", "xyz", "yachts", "yahoo", "yamaxun", "yandex", "ye", "yodobashi", "yoga", "yokohama", "you", "youtube", "yt", "yun", "za", "zappos", "zara", "zero", "zip", "zm", "zone", "zuerich", "zw"];
423
+
424
+ // ../network/dist/tld.js
425
+ var SET = new Set(tlds_default.map((t) => t.toLowerCase()));
426
+ function isKnownTld(tld) {
427
+ return SET.has(tld.toLowerCase().replace(/^\./, ""));
428
+ }
429
+ function knownTldCount() {
430
+ return SET.size;
431
+ }
432
+ function validateTld(input) {
433
+ const tld = input.trim().toLowerCase().replace(/^\./, "");
434
+ const valid = SET.has(tld);
435
+ return {
436
+ input,
437
+ tld,
438
+ valid,
439
+ isIdn: tld.startsWith("xn--"),
440
+ errors: valid ? [] : [`".${tld}" is not a delegated TLD in the IANA root zone.`]
441
+ };
442
+ }
443
+
444
+ // ../network/dist/domain.js
445
+ var LABEL_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
446
+ function validateDomain(input) {
447
+ const normalized = input.trim().toLowerCase().replace(/\.$/, "");
448
+ const labels = normalized.split(".");
449
+ const errors = [];
450
+ if (labels.length < 2)
451
+ errors.push("A domain needs at least a name and a TLD.");
452
+ if (!labels.every((l) => LABEL_RE.test(l)))
453
+ errors.push("Contains an invalid label.");
454
+ if (normalized.length > 253)
455
+ errors.push("Domain exceeds 253 characters.");
456
+ const tld = labels.length >= 2 ? labels[labels.length - 1] : null;
457
+ const tldKnown = tld ? isKnownTld(tld) : false;
458
+ if (tld && !tldKnown)
459
+ errors.push(`TLD ".${tld}" is not in the IANA root zone.`);
460
+ return { input, normalized, valid: errors.length === 0, tld, tldKnown, errors };
461
+ }
462
+
463
+ // ../network/dist/ip.js
464
+ function isIPv4(s) {
465
+ const parts = s.split(".");
466
+ if (parts.length !== 4)
467
+ return false;
468
+ for (const p of parts) {
469
+ if (!/^\d{1,3}$/.test(p))
470
+ return false;
471
+ if (p.length > 1 && p[0] === "0")
472
+ return false;
473
+ if (Number(p) > 255)
474
+ return false;
475
+ }
476
+ return true;
477
+ }
478
+ function isIPv6(s) {
479
+ if (s.includes("%"))
480
+ return false;
481
+ const parts = s.split("::");
482
+ if (parts.length > 2)
483
+ return false;
484
+ const groups = (seg) => seg === "" ? [] : seg.split(":");
485
+ const all = parts.length === 2 ? [...groups(parts[0]), ...groups(parts[1])] : groups(parts[0]);
486
+ let count = 0;
487
+ for (let i = 0; i < all.length; i++) {
488
+ const g = all[i];
489
+ if (g === "")
490
+ return false;
491
+ const isFinal = i === all.length - 1;
492
+ if (isFinal && g.includes(".")) {
493
+ if (!isIPv4(g))
494
+ return false;
495
+ count += 2;
496
+ } else {
497
+ if (!/^[0-9a-fA-F]{1,4}$/.test(g))
498
+ return false;
499
+ count += 1;
500
+ }
501
+ }
502
+ return parts.length === 2 ? count <= 7 : count === 8;
503
+ }
504
+ function validateIp(input) {
505
+ const s = input.trim();
506
+ if (isIPv4(s))
507
+ return { input, valid: true, version: 4, errors: [] };
508
+ if (isIPv6(s))
509
+ return { input, valid: true, version: 6, errors: [] };
510
+ return { input, valid: false, version: null, errors: ["Not a valid IPv4 or IPv6 address."] };
511
+ }
512
+
513
+ // ../network/dist/uuid.js
514
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
515
+ var NIL_RE = /^0{8}-0{4}-0{4}-0{4}-0{12}$/;
516
+ function validateUuid(input) {
517
+ const s = input.trim();
518
+ if (!UUID_RE.test(s)) {
519
+ return {
520
+ input,
521
+ valid: false,
522
+ version: null,
523
+ variant: null,
524
+ isNil: false,
525
+ errors: ["Not a canonical UUID (expected 8-4-4-4-12 hex digits)."]
526
+ };
527
+ }
528
+ const isNil = NIL_RE.test(s);
529
+ const version = isNil ? 0 : parseInt(s[14], 16);
530
+ const v = parseInt(s[19], 16);
531
+ let variant = "reserved";
532
+ if ((v & 8) === 0)
533
+ variant = "NCS (legacy)";
534
+ else if ((v & 12) === 8)
535
+ variant = "RFC 4122";
536
+ else if ((v & 14) === 12)
537
+ variant = "Microsoft (legacy)";
538
+ return { input, valid: true, version, variant, isNil, errors: [] };
539
+ }
540
+
541
+ // ../network/dist/url.js
542
+ function validateUrl(input) {
543
+ const s = input.trim();
544
+ let u;
545
+ try {
546
+ u = new URL(s);
547
+ } catch {
548
+ return {
549
+ input,
550
+ valid: false,
551
+ protocol: null,
552
+ hostname: null,
553
+ tld: null,
554
+ tldKnown: false,
555
+ port: null,
556
+ path: null,
557
+ errors: ["Not a parseable absolute URL (needs a scheme, e.g. https://\u2026)."]
558
+ };
559
+ }
560
+ const host = u.hostname;
561
+ const tld = host.includes(".") ? host.split(".").pop().toLowerCase() : null;
562
+ const tldKnown = tld ? isKnownTld(tld) : false;
563
+ const errors = [];
564
+ if (tld && !tldKnown)
565
+ errors.push(`Host TLD ".${tld}" is not in the IANA root zone.`);
566
+ return {
567
+ input,
568
+ valid: true,
569
+ protocol: u.protocol.replace(":", ""),
570
+ hostname: host,
571
+ tld,
572
+ tldKnown,
573
+ port: u.port || null,
574
+ path: u.pathname || null,
575
+ errors
576
+ };
577
+ }
578
+
579
+ // ../network/dist/email.js
580
+ var LOCAL_RE = /^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*$/;
581
+ var LABEL_RE2 = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/;
582
+ function validateEmail(input) {
583
+ const s = input.trim();
584
+ const at = s.lastIndexOf("@");
585
+ const errors = [];
586
+ if (at <= 0 || at === s.length - 1) {
587
+ return { input, valid: false, local: null, domain: null, tld: null, tldKnown: false, errors: ["Missing local part or domain around '@'."] };
588
+ }
589
+ const local = s.slice(0, at);
590
+ const domain = s.slice(at + 1);
591
+ if (local.length > 64 || !LOCAL_RE.test(local))
592
+ errors.push("Invalid local part (before '@').");
593
+ const labels = domain.split(".");
594
+ if (labels.length < 2)
595
+ errors.push("Domain must have at least one dot.");
596
+ if (!labels.every((l) => LABEL_RE2.test(l)))
597
+ errors.push("Invalid domain label.");
598
+ if (domain.length > 253)
599
+ errors.push("Domain exceeds 253 characters.");
600
+ const tld = labels.length >= 2 ? labels[labels.length - 1].toLowerCase() : null;
601
+ const tldKnown = tld ? isKnownTld(tld) : false;
602
+ if (tld && !tldKnown)
603
+ errors.push(`Domain TLD ".${tld}" is not in the IANA root zone.`);
604
+ return { input, valid: errors.length === 0, local, domain, tld, tldKnown, errors };
605
+ }
606
+
607
+ // ../finance/dist/isin.js
608
+ function expand(body) {
609
+ let out = "";
610
+ for (const c of body) {
611
+ out += /[0-9]/.test(c) ? c : (c.charCodeAt(0) - 55).toString();
612
+ }
613
+ return out;
614
+ }
615
+ function luhnCheckDigit2(digits2) {
616
+ let sum = 0;
617
+ let dbl = true;
618
+ for (let i = digits2.length - 1; i >= 0; i--) {
619
+ let d = digits2.charCodeAt(i) - 48;
620
+ if (dbl) {
621
+ d *= 2;
622
+ if (d > 9)
623
+ d -= 9;
624
+ }
625
+ sum += d;
626
+ dbl = !dbl;
627
+ }
628
+ return (10 - sum % 10) % 10;
629
+ }
630
+ function validateIsin(input) {
631
+ const s = input.trim().toUpperCase().replace(/\s/g, "");
632
+ const base = {
633
+ input,
634
+ normalized: s,
635
+ valid: false,
636
+ countryCode: null,
637
+ checkDigit: null,
638
+ expectedCheckDigit: null,
639
+ errors: []
640
+ };
641
+ if (!/^[A-Z]{2}[A-Z0-9]{9}[0-9]$/.test(s)) {
642
+ return { ...base, errors: ["Not an ISIN (expected 2-letter country + 9 alphanumeric + 1 check digit)."] };
643
+ }
644
+ const expected = luhnCheckDigit2(expand(s.slice(0, 11)));
645
+ const actual = Number(s[11]);
646
+ return {
647
+ ...base,
648
+ valid: expected === actual,
649
+ countryCode: s.slice(0, 2),
650
+ checkDigit: s[11],
651
+ expectedCheckDigit: String(expected),
652
+ errors: expected === actual ? [] : [`Checksum failed (ISO 6166 Luhn): expected check digit ${expected}.`]
653
+ };
654
+ }
655
+
656
+ // ../finance/dist/cusip.js
657
+ function charValue(c) {
658
+ if (/[0-9]/.test(c))
659
+ return Number(c);
660
+ if (/[A-Z]/.test(c))
661
+ return c.charCodeAt(0) - 55;
662
+ return { "*": 36, "@": 37, "#": 38 }[c] ?? -1;
663
+ }
664
+ function validateCusip(input) {
665
+ const s = input.trim().toUpperCase().replace(/\s/g, "");
666
+ const base = {
667
+ input,
668
+ normalized: s,
669
+ valid: false,
670
+ checkDigit: null,
671
+ expectedCheckDigit: null,
672
+ errors: []
673
+ };
674
+ if (!/^[0-9A-Z*@#]{8}[0-9]$/.test(s)) {
675
+ return { ...base, errors: ["Not a CUSIP (expected 8 alphanumeric chars + 1 check digit)."] };
676
+ }
677
+ let sum = 0;
678
+ for (let i = 0; i < 8; i++) {
679
+ let v = charValue(s[i]);
680
+ if (i % 2 === 1)
681
+ v *= 2;
682
+ sum += Math.floor(v / 10) + v % 10;
683
+ }
684
+ const expected = (10 - sum % 10) % 10;
685
+ const actual = Number(s[8]);
686
+ return {
687
+ ...base,
688
+ valid: expected === actual,
689
+ checkDigit: s[8],
690
+ expectedCheckDigit: String(expected),
691
+ errors: expected === actual ? [] : [`Checksum failed (CUSIP mod-10): expected check digit ${expected}.`]
692
+ };
693
+ }
694
+
695
+ // ../finance/dist/sedol.js
696
+ var WEIGHTS2 = [1, 3, 1, 7, 3, 9];
697
+ function charValue2(c) {
698
+ return /[0-9]/.test(c) ? Number(c) : c.charCodeAt(0) - 55;
699
+ }
700
+ function validateSedol(input) {
701
+ const s = input.trim().toUpperCase().replace(/\s/g, "");
702
+ const base = {
703
+ input,
704
+ normalized: s,
705
+ valid: false,
706
+ checkDigit: null,
707
+ expectedCheckDigit: null,
708
+ errors: []
709
+ };
710
+ if (!/^[0-9BCDFGHJKLMNPQRSTVWXYZ]{6}[0-9]$/.test(s)) {
711
+ return { ...base, errors: ["Not a SEDOL (expected 6 alphanumeric chars, no vowels, + 1 check digit)."] };
712
+ }
713
+ let sum = 0;
714
+ for (let i = 0; i < 6; i++)
715
+ sum += charValue2(s[i]) * WEIGHTS2[i];
716
+ const expected = (10 - sum % 10) % 10;
717
+ const actual = Number(s[6]);
718
+ return {
719
+ ...base,
720
+ valid: expected === actual,
721
+ checkDigit: s[6],
722
+ expectedCheckDigit: String(expected),
723
+ errors: expected === actual ? [] : [`Checksum failed (SEDOL weighted mod-10): expected check digit ${expected}.`]
724
+ };
725
+ }
726
+
727
+ // ../finance/dist/lei.js
728
+ function validateLei(input) {
729
+ const s = input.trim().toUpperCase().replace(/\s/g, "");
730
+ if (!/^[0-9A-Z]{18}[0-9]{2}$/.test(s)) {
731
+ return { input, normalized: s, valid: false, errors: ["Not an LEI (expected 20 chars: 18 alphanumeric + 2 check digits)."] };
732
+ }
733
+ let rem = 0;
734
+ for (const c of s) {
735
+ const part = /[0-9]/.test(c) ? c : (c.charCodeAt(0) - 55).toString();
736
+ for (const d of part)
737
+ rem = (rem * 10 + (d.charCodeAt(0) - 48)) % 97;
738
+ }
739
+ const valid = rem === 1;
740
+ return {
741
+ input,
742
+ normalized: s,
743
+ valid,
744
+ errors: valid ? [] : ["Checksum failed (ISO 7064 MOD 97-10): the LEI is not valid."]
745
+ };
746
+ }
747
+
748
+ // ../finance/dist/aba.js
749
+ function validateAba(input) {
750
+ const s = input.trim().replace(/\s|-/g, "");
751
+ if (!/^\d{9}$/.test(s)) {
752
+ return { input, normalized: s, valid: false, errors: ["Not a routing number (expected 9 digits)."] };
753
+ }
754
+ const d = s.split("").map(Number);
755
+ const sum = 3 * (d[0] + d[3] + d[6]) + 7 * (d[1] + d[4] + d[7]) + 1 * (d[2] + d[5] + d[8]);
756
+ const valid = sum % 10 === 0;
757
+ return {
758
+ input,
759
+ normalized: s,
760
+ valid,
761
+ errors: valid ? [] : ["Checksum failed (ABA weighted mod-10): the routing number is not valid."]
762
+ };
763
+ }
764
+
765
+ // ../crypto/dist/eth.js
766
+ import { keccak_256 } from "@noble/hashes/sha3";
767
+ function toEip55(hexLower) {
768
+ const hash = keccak_256(new TextEncoder().encode(hexLower));
769
+ let out = "";
770
+ for (let i = 0; i < hexLower.length; i++) {
771
+ const c = hexLower[i];
772
+ if (c >= "0" && c <= "9") {
773
+ out += c;
774
+ } else {
775
+ const byte = hash[i >> 1];
776
+ const nibble = i % 2 === 0 ? byte >> 4 : byte & 15;
777
+ out += nibble >= 8 ? c.toUpperCase() : c;
778
+ }
779
+ }
780
+ return out;
781
+ }
782
+ function validateEthAddress(input) {
783
+ const s = input.trim();
784
+ if (!/^0x[0-9a-fA-F]{40}$/.test(s)) {
785
+ return { input, valid: false, checksumStatus: "none", expected: null, errors: ["Not an Ethereum address (expected 0x followed by 40 hex chars)."] };
786
+ }
787
+ const body = s.slice(2);
788
+ const allLower = body === body.toLowerCase();
789
+ const allUpper = body === body.toUpperCase();
790
+ if (allLower || allUpper) {
791
+ return { input, valid: true, checksumStatus: "none", expected: "0x" + toEip55(body.toLowerCase()), errors: [] };
792
+ }
793
+ const expected = "0x" + toEip55(body.toLowerCase());
794
+ const ok = s === expected;
795
+ return {
796
+ input,
797
+ valid: ok,
798
+ checksumStatus: ok ? "valid" : "invalid",
799
+ expected,
800
+ errors: ok ? [] : ["EIP-55 checksum failed: the mixed-case capitalisation does not match (likely a typo)."]
801
+ };
802
+ }
803
+
804
+ // ../crypto/dist/btc.js
805
+ import { base58check, bech32, bech32m } from "@scure/base";
806
+ import { sha256 } from "@noble/hashes/sha256";
807
+ var b58c = base58check(sha256);
808
+ var BASE58_VERSIONS = {
809
+ 0: { type: "P2PKH", network: "mainnet" },
810
+ 5: { type: "P2SH", network: "mainnet" },
811
+ 111: { type: "P2PKH", network: "testnet" },
812
+ 196: { type: "P2SH", network: "testnet" }
813
+ };
814
+ var HRP = { bc: "mainnet", tb: "testnet", bcrt: "regtest" };
815
+ function validateBtcAddress(input) {
816
+ const s = input.trim();
817
+ const fail = (msg) => ({ input, valid: false, type: null, network: null, errors: [msg] });
818
+ if (/^(bc1|tb1|bcrt1)/i.test(s)) {
819
+ const lower = s.toLowerCase();
820
+ const hrp = lower.startsWith("bcrt1") ? "bcrt" : lower.slice(0, 2);
821
+ for (const [coder, isM] of [[bech32, false], [bech32m, true]]) {
822
+ try {
823
+ const dec = coder.decode(lower, 90);
824
+ const version = dec.words[0];
825
+ if (version === 0 === isM)
826
+ continue;
827
+ const type = version === 0 ? "SegWit v0 (P2WPKH/P2WSH)" : version === 1 ? "Taproot (P2TR)" : `SegWit v${version}`;
828
+ return { input, valid: true, type, network: HRP[hrp] ?? null, errors: [] };
829
+ } catch {
830
+ }
831
+ }
832
+ return fail("Invalid SegWit (bech32/bech32m) address \u2014 checksum or format failed.");
833
+ }
834
+ try {
835
+ const data = b58c.decode(s);
836
+ const version = data[0];
837
+ const info = BASE58_VERSIONS[version];
838
+ if (!info)
839
+ return fail(`Valid Base58Check, but unknown version byte 0x${version.toString(16)}.`);
840
+ return { input, valid: true, type: info.type, network: info.network, errors: [] };
841
+ } catch {
842
+ return fail("Not a valid Bitcoin address (Base58Check checksum or format failed).");
843
+ }
844
+ }
845
+
846
+ // ../national-id/dist/brazil.js
847
+ function digits(s) {
848
+ return s.replace(/\D/g, "").split("").map(Number);
849
+ }
850
+ function validateCpf(input) {
851
+ const clean = input.replace(/\D/g, "");
852
+ const base = { input, normalized: clean, valid: false, errors: [] };
853
+ if (clean.length !== 11)
854
+ return { ...base, errors: ["CPF must have 11 digits."] };
855
+ if (/^(\d)\1{10}$/.test(clean))
856
+ return { ...base, errors: ["Invalid CPF (all digits identical)."] };
857
+ const d = digits(clean);
858
+ const check = (len) => {
859
+ let sum = 0;
860
+ for (let i = 0; i < len; i++)
861
+ sum += d[i] * (len + 1 - i);
862
+ const r = sum * 10 % 11;
863
+ return r === 10 ? 0 : r;
864
+ };
865
+ const ok = check(9) === d[9] && check(10) === d[10];
866
+ return { ...base, valid: ok, errors: ok ? [] : ["Checksum failed (CPF mod-11)."] };
867
+ }
868
+ function validateCnpj(input) {
869
+ const clean = input.replace(/\D/g, "");
870
+ const base = { input, normalized: clean, valid: false, errors: [] };
871
+ if (clean.length !== 14)
872
+ return { ...base, errors: ["CNPJ must have 14 digits."] };
873
+ if (/^(\d)\1{13}$/.test(clean))
874
+ return { ...base, errors: ["Invalid CNPJ (all digits identical)."] };
875
+ const d = digits(clean);
876
+ const check = (len) => {
877
+ const weights = len === 12 ? [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] : [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
878
+ let sum = 0;
879
+ for (let i = 0; i < len; i++)
880
+ sum += d[i] * weights[i];
881
+ const r = sum % 11;
882
+ return r < 2 ? 0 : 11 - r;
883
+ };
884
+ const ok = check(12) === d[12] && check(13) === d[13];
885
+ return { ...base, valid: ok, errors: ok ? [] : ["Checksum failed (CNPJ mod-11)."] };
886
+ }
887
+
888
+ // ../national-id/dist/saId.js
889
+ function luhnValid2(num) {
890
+ let sum = 0;
891
+ let dbl = false;
892
+ for (let i = num.length - 1; i >= 0; i--) {
893
+ let d = num.charCodeAt(i) - 48;
894
+ if (dbl) {
895
+ d *= 2;
896
+ if (d > 9)
897
+ d -= 9;
898
+ }
899
+ sum += d;
900
+ dbl = !dbl;
901
+ }
902
+ return sum % 10 === 0;
903
+ }
904
+ function validateSaId(input) {
905
+ const clean = input.replace(/\s/g, "");
906
+ const base = {
907
+ input,
908
+ normalized: clean,
909
+ valid: false,
910
+ dateOfBirth: null,
911
+ gender: null,
912
+ citizenship: null,
913
+ errors: []
914
+ };
915
+ if (!/^\d{13}$/.test(clean))
916
+ return { ...base, errors: ["SA ID must be 13 digits."] };
917
+ const yy = +clean.slice(0, 2);
918
+ const mm = +clean.slice(2, 4);
919
+ const dd = +clean.slice(4, 6);
920
+ const errors = [];
921
+ if (mm < 1 || mm > 12)
922
+ errors.push("Invalid month in date of birth.");
923
+ if (dd < 1 || dd > 31)
924
+ errors.push("Invalid day in date of birth.");
925
+ if (!luhnValid2(clean))
926
+ errors.push("Checksum failed (Luhn).");
927
+ const citizenshipDigit = +clean[10];
928
+ if (citizenshipDigit > 1)
929
+ errors.push("Invalid citizenship digit (must be 0 or 1).");
930
+ if (errors.length)
931
+ return { ...base, errors };
932
+ const century = yy <= 25 ? "20" : "19";
933
+ const dob = `${century}${String(yy).padStart(2, "0")}-${String(mm).padStart(2, "0")}-${String(dd).padStart(2, "0")}`;
934
+ const gender = +clean.slice(6, 10) >= 5e3 ? "male" : "female";
935
+ const citizenship = citizenshipDigit === 0 ? "citizen" : "permanent resident";
936
+ return { ...base, valid: true, dateOfBirth: dob, gender, citizenship, errors: [] };
937
+ }
938
+
939
+ // ../national-id/dist/spain.js
940
+ var LETTERS = "TRWAGMYFPDXBNJZSQVHLCKE";
941
+ function validateDni(input) {
942
+ const s = input.toUpperCase().replace(/[\s-]/g, "");
943
+ const base = { input, normalized: s, valid: false, type: null, errors: [] };
944
+ let numberPart;
945
+ let type;
946
+ if (/^[0-9]{8}[A-Z]$/.test(s)) {
947
+ numberPart = s.slice(0, 8);
948
+ type = "DNI";
949
+ } else if (/^[XYZ][0-9]{7}[A-Z]$/.test(s)) {
950
+ const prefix = { X: "0", Y: "1", Z: "2" }[s[0]];
951
+ numberPart = prefix + s.slice(1, 8);
952
+ type = "NIE";
953
+ } else {
954
+ return { ...base, errors: ["Not a valid DNI (8 digits + letter) or NIE (X/Y/Z + 7 digits + letter)."] };
955
+ }
956
+ const expected = LETTERS[Number(numberPart) % 23];
957
+ const ok = s[s.length - 1] === expected;
958
+ return {
959
+ ...base,
960
+ valid: ok,
961
+ type,
962
+ errors: ok ? [] : [`Control letter failed (mod-23): expected '${expected}'.`]
963
+ };
964
+ }
965
+
966
+ // ../national-id/dist/verhoeff.js
967
+ var D = [
968
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
969
+ [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
970
+ [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
971
+ [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
972
+ [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
973
+ [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
974
+ [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
975
+ [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
976
+ [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
977
+ [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
978
+ ];
979
+ var P = [
980
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
981
+ [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
982
+ [5, 8, 0, 9, 1, 4, 2, 6, 7, 3],
983
+ [8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
984
+ [9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
985
+ [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
986
+ [2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
987
+ [7, 0, 4, 6, 9, 1, 3, 2, 5, 8]
988
+ ];
989
+ var INV = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9];
990
+ function verhoeffValid(num) {
991
+ let c = 0;
992
+ const rev = num.split("").reverse();
993
+ for (let i = 0; i < rev.length; i++) {
994
+ c = D[c][P[i % 8][Number(rev[i])]];
995
+ }
996
+ return c === 0;
997
+ }
998
+ function verhoeffGenerate(num) {
999
+ let c = 0;
1000
+ const rev = num.split("").reverse();
1001
+ for (let i = 0; i < rev.length; i++) {
1002
+ c = D[c][P[(i + 1) % 8][Number(rev[i])]];
1003
+ }
1004
+ return INV[c];
1005
+ }
1006
+
1007
+ // ../national-id/dist/aadhaar.js
1008
+ function validateAadhaar(input) {
1009
+ const clean = input.replace(/\s/g, "");
1010
+ const base = { input, normalized: clean, valid: false, errors: [] };
1011
+ if (!/^\d{12}$/.test(clean))
1012
+ return { ...base, errors: ["Aadhaar must be 12 digits."] };
1013
+ if (clean[0] === "0" || clean[0] === "1")
1014
+ return { ...base, errors: ["Aadhaar cannot start with 0 or 1."] };
1015
+ const ok = verhoeffValid(clean);
1016
+ return { ...base, valid: ok, errors: ok ? [] : ["Checksum failed (Verhoeff)."] };
1017
+ }
1018
+
1019
+ // ../academic/dist/isbn10.js
1020
+ function validateIsbn10(input) {
1021
+ const s = input.replace(/[\s-]/g, "").toUpperCase();
1022
+ const base = { input, normalized: s, valid: false, expectedCheck: null, errors: [] };
1023
+ if (!/^\d{9}[\dX]$/.test(s))
1024
+ return { ...base, errors: ["Not an ISBN-10 (9 digits + check digit 0-9 or X)."] };
1025
+ let sum = 0;
1026
+ for (let i = 0; i < 9; i++)
1027
+ sum += Number(s[i]) * (10 - i);
1028
+ const r = (11 - sum % 11) % 11;
1029
+ const expected = r === 10 ? "X" : String(r);
1030
+ const ok = expected === s[9];
1031
+ return { ...base, valid: ok, expectedCheck: expected, errors: ok ? [] : [`Checksum failed (ISBN-10 mod-11): expected '${expected}'.`] };
1032
+ }
1033
+
1034
+ // ../academic/dist/issn.js
1035
+ function validateIssn(input) {
1036
+ const s = input.replace(/[\s-]/g, "").toUpperCase();
1037
+ const base = { input, normalized: s, valid: false, expectedCheck: null, errors: [] };
1038
+ if (!/^\d{7}[\dX]$/.test(s))
1039
+ return { ...base, errors: ["Not an ISSN (7 digits + check digit 0-9 or X)."] };
1040
+ let sum = 0;
1041
+ for (let i = 0; i < 7; i++)
1042
+ sum += Number(s[i]) * (8 - i);
1043
+ const r = (11 - sum % 11) % 11;
1044
+ const expected = r === 10 ? "X" : String(r);
1045
+ const ok = expected === s[7];
1046
+ return { ...base, valid: ok, expectedCheck: expected, errors: ok ? [] : [`Checksum failed (ISSN mod-11): expected '${expected}'.`] };
1047
+ }
1048
+
1049
+ // ../academic/dist/orcid.js
1050
+ function mod11_2(first15) {
1051
+ let total = 0;
1052
+ for (const c of first15)
1053
+ total = (total + Number(c)) * 2 % 11;
1054
+ const result = (12 - total % 11) % 11;
1055
+ return result === 10 ? "X" : String(result);
1056
+ }
1057
+ function validateOrcid(input) {
1058
+ const s = input.replace(/[\s-]/g, "").replace(/^https?:\/\/orcid\.org\//i, "").toUpperCase();
1059
+ const base = { input, normalized: s, valid: false, expectedCheck: null, errors: [] };
1060
+ if (!/^\d{15}[\dX]$/.test(s))
1061
+ return { ...base, errors: ["Not an ORCID (16 chars: 15 digits + check digit 0-9 or X)."] };
1062
+ const expected = mod11_2(s.slice(0, 15));
1063
+ const ok = expected === s[15];
1064
+ return { ...base, valid: ok, expectedCheck: expected, errors: ok ? [] : [`Checksum failed (ISO 7064 MOD 11-2): expected '${expected}'.`] };
1065
+ }
1066
+
1067
+ // ../locale/dist/dates.js
1068
+ var DAYFIRST = { "en-GB": true, "en-US": false };
1069
+ var ISO = /^\d{4}-\d{2}-\d{2}$/;
1070
+ var NUMERIC = /^(\d{1,2})[/.\-](\d{1,2})[/.\-](\d{4})$/;
1071
+ function pad(n) {
1072
+ return String(n).padStart(2, "0");
1073
+ }
1074
+ function buildIso(y, m, d) {
1075
+ const dt = new Date(Date.UTC(y, m - 1, d));
1076
+ if (dt.getUTCFullYear() === y && dt.getUTCMonth() === m - 1 && dt.getUTCDate() === d) {
1077
+ return `${y}-${pad(m)}-${pad(d)}`;
1078
+ }
1079
+ return null;
1080
+ }
1081
+ function parseDate(text, locale = "en-GB") {
1082
+ locale = locale || "en-GB";
1083
+ let dayfirst = DAYFIRST[locale] ?? true;
1084
+ const s = (text ?? "").trim();
1085
+ if (!s)
1086
+ return { valid: false, reason: "empty input", locale };
1087
+ if (ISO.test(s)) {
1088
+ const [y, m, d] = s.split("-").map(Number);
1089
+ const iso = buildIso(y, m, d);
1090
+ return iso ? { valid: true, iso, locale, dayfirst: false, input: text } : { valid: false, reason: "impossible date", locale, input: text };
1091
+ }
1092
+ const num = s.match(NUMERIC);
1093
+ if (num) {
1094
+ const a = Number(num[1]);
1095
+ const b = Number(num[2]);
1096
+ const y = Number(num[3]);
1097
+ const day = dayfirst ? a : b;
1098
+ const month = dayfirst ? b : a;
1099
+ const iso = buildIso(y, month, day);
1100
+ return iso ? { valid: true, iso, locale, dayfirst, input: text } : { valid: false, reason: "unparseable or impossible date", locale, input: text };
1101
+ }
1102
+ const cleaned = s.replace(/(\d+)(st|nd|rd|th)\b/gi, "$1");
1103
+ const parsed = new Date(cleaned);
1104
+ if (!isNaN(parsed.getTime())) {
1105
+ const iso = buildIso(parsed.getFullYear(), parsed.getMonth() + 1, parsed.getDate());
1106
+ if (iso)
1107
+ return { valid: true, iso, locale, dayfirst, input: text };
1108
+ }
1109
+ return { valid: false, reason: "unparseable or impossible date", locale, input: text };
1110
+ }
1111
+
1112
+ // ../locale/dist/phone.js
1113
+ import { parsePhoneNumberFromString } from "libphonenumber-js";
1114
+ function validatePhone(number, region = "GB") {
1115
+ region = (region || "GB").toUpperCase();
1116
+ let parsed;
1117
+ try {
1118
+ parsed = parsePhoneNumberFromString(String(number), region);
1119
+ } catch {
1120
+ parsed = void 0;
1121
+ }
1122
+ if (!parsed || !parsed.isValid()) {
1123
+ return { valid: false, reason: "not a valid number for region", region, input: number };
1124
+ }
1125
+ return {
1126
+ valid: true,
1127
+ e164: parsed.number,
1128
+ national: parsed.formatNational(),
1129
+ international: parsed.formatInternational(),
1130
+ type: parsed.getType() ?? "unknown",
1131
+ region,
1132
+ input: number
1133
+ };
1134
+ }
1135
+
1136
+ // ../locale/dist/currency.js
1137
+ var DEFAULT_LOCALE = { GBP: "en-GB", USD: "en-US" };
1138
+ function formatMoney(amount, currency = "GBP", locale) {
1139
+ currency = (currency || "GBP").toUpperCase();
1140
+ const loc = locale || DEFAULT_LOCALE[currency] || "en-US";
1141
+ try {
1142
+ const formatted = new Intl.NumberFormat(loc, { style: "currency", currency }).format(amount);
1143
+ return { ok: true, formatted, amount, currency, locale: loc };
1144
+ } catch (e) {
1145
+ return { ok: false, reason: String(e), amount, currency, locale: loc };
1146
+ }
1147
+ }
1148
+
1149
+ // ../locale/dist/holidays.js
1150
+ import Holidays from "date-holidays";
1151
+ var COUNTRIES = new Holidays().getCountries();
1152
+ function supported(country) {
1153
+ return Object.prototype.hasOwnProperty.call(COUNTRIES, country);
1154
+ }
1155
+ var DEFAULT_SUBDIV = { GB: "ENG" };
1156
+ var COUNT_TYPES = /* @__PURE__ */ new Set(["public", "bank"]);
1157
+ function makeCal(country, subdiv) {
1158
+ if (subdiv) {
1159
+ try {
1160
+ return new Holidays(country, subdiv);
1161
+ } catch {
1162
+ }
1163
+ }
1164
+ return new Holidays(country);
1165
+ }
1166
+ function isoOf(h) {
1167
+ return h.date.slice(0, 10);
1168
+ }
1169
+ function isHoliday(date, country = "GB", subdiv) {
1170
+ country = (country || "GB").toUpperCase();
1171
+ if (!supported(country))
1172
+ return { ok: false, reason: `country ${country} not supported` };
1173
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(date))
1174
+ return { ok: false, reason: `bad date: ${date}` };
1175
+ const used = subdiv || DEFAULT_SUBDIV[country] || void 0;
1176
+ const cal = makeCal(country, used);
1177
+ const res = cal.isHoliday(/* @__PURE__ */ new Date(`${date}T12:00:00`));
1178
+ const matches = Array.isArray(res) ? res.filter((h) => COUNT_TYPES.has(h.type)) : [];
1179
+ const hit = matches[0];
1180
+ return {
1181
+ ok: true,
1182
+ date,
1183
+ country,
1184
+ subdiv: used ?? null,
1185
+ is_holiday: matches.length > 0,
1186
+ name: hit ? hit.name : null
1187
+ };
1188
+ }
1189
+ function nextHoliday(country = "GB", after, subdiv) {
1190
+ country = (country || "GB").toUpperCase();
1191
+ if (!supported(country))
1192
+ return { ok: false, reason: `country ${country} not supported` };
1193
+ const start = after && /^\d{4}-\d{2}-\d{2}$/.test(after) ? after : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1194
+ const used = subdiv || DEFAULT_SUBDIV[country] || void 0;
1195
+ const cal = makeCal(country, used);
1196
+ const startYear = Number(start.slice(0, 4));
1197
+ for (const yr of [startYear, startYear + 1]) {
1198
+ const all = (cal.getHolidays(yr) || []).filter((h) => COUNT_TYPES.has(h.type)).map((h) => ({ iso: isoOf(h), name: h.name })).filter((h) => h.iso >= start).sort((a, b) => a.iso.localeCompare(b.iso));
1199
+ if (all.length) {
1200
+ return { ok: true, country, subdiv: used ?? null, date: all[0].iso, name: all[0].name };
1201
+ }
1202
+ }
1203
+ return { ok: false, reason: "none found in window" };
1204
+ }
1205
+
1206
+ // ../locale/dist/data/uk_vat.json
1207
+ var uk_vat_default = {
1208
+ country: "GB",
1209
+ tax_name: "VAT",
1210
+ source: "HM Revenue & Customs standard-rate history (public record)",
1211
+ last_verified: "2026-06-14",
1212
+ note: "Standard rate only. Reduced (5%) and zero rates are separate and not modelled here. Ranges are inclusive of start, exclusive of next start. Verify pre-1991 values before production use.",
1213
+ standard_rate_history: [
1214
+ { from: "1979-06-18", rate: 15 },
1215
+ { from: "1991-04-01", rate: 17.5 },
1216
+ { from: "2008-12-01", rate: 15, note: "Temporary cut (financial crisis)" },
1217
+ { from: "2010-01-01", rate: 17.5, note: "Reverted" },
1218
+ { from: "2011-01-04", rate: 20, note: "Current standard rate" }
1219
+ ],
1220
+ reduced_rate: 5,
1221
+ zero_rate: 0
1222
+ };
1223
+
1224
+ // ../locale/dist/data/us_sales_tax.json
1225
+ var us_sales_tax_default = {
1226
+ country: "US",
1227
+ tax_name: "Sales tax (state base rate)",
1228
+ national_vat: 0,
1229
+ source: "Commonly published 2025 state base sales-tax rates (e.g. Tax Foundation). VERIFY against an authoritative feed before production billing.",
1230
+ last_verified: "2026-06-14",
1231
+ note: "The US has NO national VAT. These are STATE BASE rates only; local (county/city/district) rates add on top and can add several percent. Five states (AK, DE, MT, NH, OR) levy no state sales tax (Alaska allows local). HI and NM are gross-receipts taxes shown as equivalents. Treat as indicative, not authoritative for invoicing.",
1232
+ no_state_sales_tax: ["AK", "DE", "MT", "NH", "OR"],
1233
+ state_base_rates: {
1234
+ AL: 4,
1235
+ AK: 0,
1236
+ AZ: 5.6,
1237
+ AR: 6.5,
1238
+ CA: 7.25,
1239
+ CO: 2.9,
1240
+ CT: 6.35,
1241
+ DE: 0,
1242
+ FL: 6,
1243
+ GA: 4,
1244
+ HI: 4,
1245
+ ID: 6,
1246
+ IL: 6.25,
1247
+ IN: 7,
1248
+ IA: 6,
1249
+ KS: 6.5,
1250
+ KY: 6,
1251
+ LA: 5,
1252
+ ME: 5.5,
1253
+ MD: 6,
1254
+ MA: 6.25,
1255
+ MI: 6,
1256
+ MN: 6.875,
1257
+ MS: 7,
1258
+ MO: 4.225,
1259
+ MT: 0,
1260
+ NE: 5.5,
1261
+ NV: 6.85,
1262
+ NH: 0,
1263
+ NJ: 6.625,
1264
+ NM: 4.875,
1265
+ NY: 4,
1266
+ NC: 4.75,
1267
+ ND: 5,
1268
+ OH: 5.75,
1269
+ OK: 4.5,
1270
+ OR: 0,
1271
+ PA: 6,
1272
+ RI: 7,
1273
+ SC: 6,
1274
+ SD: 4.2,
1275
+ TN: 7,
1276
+ TX: 6.25,
1277
+ UT: 6.1,
1278
+ VT: 6,
1279
+ VA: 5.3,
1280
+ WA: 6.5,
1281
+ WV: 6,
1282
+ WI: 5,
1283
+ WY: 4,
1284
+ DC: 6
1285
+ },
1286
+ rate_notes: {
1287
+ LA: "Raised to 5.0% effective 2025-01-01 (was 4.45%).",
1288
+ SD: "Reduced to 4.2% (2023), scheduled to revert to 4.5% in 2027.",
1289
+ NM: "Gross-receipts tax; state portion ~4.875%.",
1290
+ HI: "General excise tax (GET) 4.0%, economically broader than a sales tax.",
1291
+ UT: "Includes mandatory statewide local add-ons (state base 4.85% + 1.25%).",
1292
+ VA: "Includes 1% mandatory local add-on (4.3% state + 1.0%)."
1293
+ }
1294
+ };
1295
+
1296
+ // ../locale/dist/vat.js
1297
+ var UK = uk_vat_default;
1298
+ var US = us_sales_tax_default;
1299
+ function ukVat(date) {
1300
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(date))
1301
+ return { ok: false, reason: `bad date: ${date}` };
1302
+ let applicable = null;
1303
+ for (const band of UK.standard_rate_history) {
1304
+ if (band.from <= date)
1305
+ applicable = band;
1306
+ else
1307
+ break;
1308
+ }
1309
+ if (!applicable) {
1310
+ return { ok: false, reason: `no UK VAT rate recorded for ${date}` };
1311
+ }
1312
+ return {
1313
+ ok: true,
1314
+ country: "GB",
1315
+ tax_name: "VAT",
1316
+ rate: applicable.rate,
1317
+ date,
1318
+ effective_from: applicable.from,
1319
+ kind: "standard",
1320
+ note: applicable.note ?? null,
1321
+ source: UK.source,
1322
+ last_verified: UK.last_verified
1323
+ };
1324
+ }
1325
+ function usTax(date, state) {
1326
+ const base = {
1327
+ ok: true,
1328
+ country: "US",
1329
+ tax_name: "Sales tax",
1330
+ national_vat: 0,
1331
+ date,
1332
+ note: US.note,
1333
+ source: US.source,
1334
+ last_verified: US.last_verified
1335
+ };
1336
+ if (!state) {
1337
+ return { ...base, rate: 0, scope: "national (US has no VAT; supply a state for sales tax)" };
1338
+ }
1339
+ const st = state.toUpperCase();
1340
+ const rate = US.state_base_rates[st];
1341
+ if (rate === void 0)
1342
+ return { ok: false, reason: `unknown US state code: ${st}` };
1343
+ return {
1344
+ ...base,
1345
+ rate,
1346
+ scope: "state base rate (local taxes add on top)",
1347
+ state: st,
1348
+ state_note: US.rate_notes[st]
1349
+ };
1350
+ }
1351
+ function vatRate(country = "GB", date, state) {
1352
+ country = (country || "GB").toUpperCase();
1353
+ const d = date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1354
+ if (country === "GB")
1355
+ return ukVat(d);
1356
+ if (country === "US")
1357
+ return usTax(d, state);
1358
+ return { ok: false, reason: `country ${country} not supported in v1 (GB, US only)` };
1359
+ }
1360
+
1361
+ // ../locale/dist/address.js
1362
+ var UK_POSTCODE = /\b(GIR ?0AA|[A-PR-UWYZ][A-HK-Y]?[0-9][0-9A-HJKPS-UW]? ?[0-9][ABD-HJLNP-UW-Z]{2})\b/i;
1363
+ var US_STATE_ZIP = /\b([A-Z]{2})\s+(\d{5}(?:-\d{4})?)\b/;
1364
+ var UK_HINT = /\b(United Kingdom|U\.?K\.?|England|Scotland|Wales|Northern Ireland)\b/i;
1365
+ var US_HINT = /\b(United States|U\.?S\.?A?\.?)\b/i;
1366
+ var US_STATES = /* @__PURE__ */ new Set([
1367
+ "AL",
1368
+ "AK",
1369
+ "AZ",
1370
+ "AR",
1371
+ "CA",
1372
+ "CO",
1373
+ "CT",
1374
+ "DE",
1375
+ "FL",
1376
+ "GA",
1377
+ "HI",
1378
+ "ID",
1379
+ "IL",
1380
+ "IN",
1381
+ "IA",
1382
+ "KS",
1383
+ "KY",
1384
+ "LA",
1385
+ "ME",
1386
+ "MD",
1387
+ "MA",
1388
+ "MI",
1389
+ "MN",
1390
+ "MS",
1391
+ "MO",
1392
+ "MT",
1393
+ "NE",
1394
+ "NV",
1395
+ "NH",
1396
+ "NJ",
1397
+ "NM",
1398
+ "NY",
1399
+ "NC",
1400
+ "ND",
1401
+ "OH",
1402
+ "OK",
1403
+ "OR",
1404
+ "PA",
1405
+ "RI",
1406
+ "SC",
1407
+ "SD",
1408
+ "TN",
1409
+ "TX",
1410
+ "UT",
1411
+ "VT",
1412
+ "VA",
1413
+ "WA",
1414
+ "WV",
1415
+ "WI",
1416
+ "WY",
1417
+ "DC"
1418
+ ]);
1419
+ function normaliseUk(pc) {
1420
+ const c = pc.replace(/\s+/g, "").toUpperCase();
1421
+ return c.slice(0, -3) + " " + c.slice(-3);
1422
+ }
1423
+ function guessCity(raw, country, postcode, state) {
1424
+ const parts = raw.split(",").map((p) => p.trim()).filter(Boolean);
1425
+ if (!parts.length)
1426
+ return null;
1427
+ if (country === "US" && state && postcode) {
1428
+ for (let i = 0; i < parts.length; i++) {
1429
+ if (parts[i].includes(state) && parts[i].includes(postcode) && i > 0)
1430
+ return parts[i - 1];
1431
+ }
1432
+ }
1433
+ if (country === "GB" && postcode) {
1434
+ const pcKey = postcode.replace(/\s+/g, "").toUpperCase();
1435
+ for (const p of parts) {
1436
+ if (p.replace(/\s+/g, "").toUpperCase().includes(pcKey)) {
1437
+ const cleaned = p.replace(UK_POSTCODE, "").replace(/[,\s]+$/, "").trim();
1438
+ if (cleaned)
1439
+ return cleaned;
1440
+ }
1441
+ }
1442
+ return parts.length >= 2 ? parts[parts.length - 2] : null;
1443
+ }
1444
+ return null;
1445
+ }
1446
+ function confidence(country, postcode, city) {
1447
+ const score = (country ? 1 : 0) + (postcode ? 1 : 0) + (city ? 1 : 0);
1448
+ return ["none", "low", "medium", "high"][score];
1449
+ }
1450
+ function parseAddress(text) {
1451
+ const raw = (text ?? "").trim();
1452
+ if (!raw)
1453
+ return { ok: false, reason: "empty input" };
1454
+ const ukPc = raw.match(UK_POSTCODE);
1455
+ const usSz = raw.match(US_STATE_ZIP);
1456
+ const usValid = !!(usSz && US_STATES.has(usSz[1].toUpperCase()));
1457
+ let country = null;
1458
+ let postcode = null;
1459
+ let state = null;
1460
+ if (ukPc && !usValid) {
1461
+ country = "GB";
1462
+ postcode = normaliseUk(ukPc[1]);
1463
+ } else if (usValid && usSz) {
1464
+ country = "US";
1465
+ state = usSz[1].toUpperCase();
1466
+ postcode = usSz[2];
1467
+ } else if (UK_HINT.test(raw)) {
1468
+ country = "GB";
1469
+ postcode = ukPc ? normaliseUk(ukPc[1]) : null;
1470
+ } else if (US_HINT.test(raw)) {
1471
+ country = "US";
1472
+ }
1473
+ const city = guessCity(raw, country, postcode, state);
1474
+ return { ok: true, input: raw, country, postcode, state, city, confidence: confidence(country, postcode, city) };
1475
+ }
1476
+
1477
+ // ../vat/dist/vat.js
1478
+ import { checkVAT, countries } from "jsvat-next";
1479
+ function validateVat(input, country) {
1480
+ let raw = String(input ?? "").toUpperCase().replace(/[\s.\-]/g, "");
1481
+ if (country && !/^[A-Z]{2}/.test(raw)) {
1482
+ raw = country.toUpperCase().slice(0, 2) + raw;
1483
+ }
1484
+ const r = checkVAT(raw, countries);
1485
+ const cc = r.country?.isoCode?.short ?? null;
1486
+ return {
1487
+ input,
1488
+ normalized: raw,
1489
+ valid: !!r.isValid,
1490
+ country: r.country?.name ?? null,
1491
+ countryCode: cc,
1492
+ errors: r.isValid ? [] : ["Invalid VAT number \u2014 format or checksum failed (NOTE: this does not check VIES registration)."]
1493
+ };
1494
+ }
1495
+
1496
+ // src/branding.ts
1497
+ var PUBLIC_BASE = "https://qiniso.qinisolabs.workers.dev";
1498
+ var LOGO_SVG = `<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Qiniso">
1499
+ <defs>
1500
+ <linearGradient id="emerald" x1="0" y1="0" x2="1" y2="1">
1501
+ <stop offset="0" stop-color="#10b981"/>
1502
+ <stop offset="1" stop-color="#047857"/>
1503
+ </linearGradient>
1504
+ </defs>
1505
+ <rect x="16" y="16" width="480" height="480" rx="112" fill="url(#emerald)"/>
1506
+ <circle cx="236" cy="252" r="120" fill="none" stroke="#ffffff" stroke-width="42"/>
1507
+ <path d="M238 300 L300 366 L420 190" fill="none" stroke="#ffffff" stroke-width="42" stroke-linecap="round" stroke-linejoin="round"/>
1508
+ </svg>`;
1509
+ var ICONS = [
1510
+ { src: `${PUBLIC_BASE}/icon.svg`, mimeType: "image/svg+xml", sizes: ["any"] }
1511
+ ];
1512
+
1513
+ // src/core.ts
1514
+ var TOOLS = [
1515
+ {
1516
+ name: "validate_iban",
1517
+ description: "USE THIS to verify an IBAN (international bank account number) before relying on it \u2014 instead of guessing whether it looks right. Checks the country, the country-specific length, and the ISO 7064 mod-97 checksum, and returns the country, check digits and BBAN. Call this whenever a user supplies a bank account for a payment, payout or invoice.",
1518
+ argName: "iban",
1519
+ argDescription: "The IBAN to validate; spaces are ignored.",
1520
+ run: (v) => validateIban(v)
1521
+ },
1522
+ {
1523
+ name: "validate_card",
1524
+ description: "USE THIS to check a payment card number's structure before using it \u2014 never assume a card number is valid or guess its brand. Verifies the Luhn checksum, detects the brand (Visa, Mastercard, Amex, Discover, Diners, JCB, UnionPay) from its BIN, and checks the length. Does NOT check whether the card is real, active or has funds.",
1525
+ argName: "number",
1526
+ argDescription: "The card number; spaces and dashes are ignored.",
1527
+ run: (v) => validateCard(v)
1528
+ },
1529
+ {
1530
+ name: "validate_isbn",
1531
+ description: "USE THIS to verify an ISBN-13 book identifier instead of trusting that 13 digits are correct. Checks the 978/979 prefix and the mod-10 weighted check digit, and returns the expected check digit when it fails.",
1532
+ argName: "isbn",
1533
+ argDescription: "The ISBN-13; hyphens and spaces are ignored.",
1534
+ run: (v) => validateIsbn13(v)
1535
+ },
1536
+ {
1537
+ name: "validate_vin",
1538
+ description: "USE THIS to verify a vehicle VIN before acting on it \u2014 do not assume a 17-character string is a valid VIN. Checks the allowed alphabet (no I/O/Q) and the ISO 3779 transliteration check digit in position 9, and returns the expected check digit when it fails.",
1539
+ argName: "vin",
1540
+ argDescription: "The 17-character VIN to validate.",
1541
+ run: (v) => validateVin(v)
1542
+ },
1543
+ {
1544
+ name: "validate_tld",
1545
+ description: "USE THIS to check whether a top-level domain is real before trusting a domain or link \u2014 do NOT guess whether a TLD like .zip, .corp, .crypto or .web exists. Checks the suffix against the authoritative IANA root-zone list (kept current). Returns valid:false for TLDs that are not actually delegated.",
1546
+ argName: "tld",
1547
+ argDescription: "The TLD to check, with or without a leading dot (e.g. 'zip' or '.zip').",
1548
+ run: (v) => validateTld(v)
1549
+ },
1550
+ {
1551
+ name: "validate_domain",
1552
+ description: "USE THIS to verify a domain name's structure AND that its TLD is a real IANA-delegated suffix \u2014 instead of assuming a domain is legitimate. Catches invalid labels and made-up TLDs (e.g. example.corp). Returns the TLD and whether it is known.",
1553
+ argName: "domain",
1554
+ argDescription: "The domain name, e.g. 'example.com'.",
1555
+ run: (v) => validateDomain(v)
1556
+ },
1557
+ {
1558
+ name: "validate_ip",
1559
+ description: "USE THIS to verify an IP address before relying on it \u2014 do not assume a dotted or colon string is valid. Strictly checks IPv4 (RFC 791, rejects leading zeros / out-of-range octets) and IPv6 (RFC 4291, including '::' compression and embedded IPv4), and returns the version.",
1560
+ argName: "ip",
1561
+ argDescription: "The IPv4 or IPv6 address to validate.",
1562
+ run: (v) => validateIp(v)
1563
+ },
1564
+ {
1565
+ name: "validate_uuid",
1566
+ description: "USE THIS to verify a UUID and read its version/variant instead of guessing \u2014 e.g. to tell a v4 (random) from a v7 (time-ordered) UUID. Checks the canonical 8-4-4-4-12 form and returns version, variant and whether it is the nil UUID.",
1567
+ argName: "uuid",
1568
+ argDescription: "The UUID string to validate.",
1569
+ run: (v) => validateUuid(v)
1570
+ },
1571
+ {
1572
+ name: "validate_url",
1573
+ description: "USE THIS to verify a URL before fetching or storing it \u2014 parses it with the WHATWG URL standard and additionally checks that the host's TLD is a real IANA suffix. Returns protocol, hostname, port, path and whether the TLD is known.",
1574
+ argName: "url",
1575
+ argDescription: "The absolute URL to validate (e.g. https://example.com/path).",
1576
+ run: (v) => validateUrl(v)
1577
+ },
1578
+ {
1579
+ name: "validate_email",
1580
+ description: "USE THIS to check an email address's syntax AND that its domain TLD is real, before saving or sending \u2014 instead of trusting raw input. Validates the local part and domain (RFC 5321/5322 subset) and flags made-up TLDs. Does NOT check deliverability.",
1581
+ argName: "email",
1582
+ argDescription: "The email address to validate.",
1583
+ run: (v) => validateEmail(v)
1584
+ },
1585
+ {
1586
+ name: "validate_isin",
1587
+ description: "USE THIS to verify an ISIN (international securities identifier) before relying on it \u2014 never assume a 12-character code is valid. Checks the format and the ISO 6166 Luhn check digit, and returns the country code. Call this when a user supplies a security/instrument identifier.",
1588
+ argName: "isin",
1589
+ argDescription: "The 12-character ISIN, e.g. US0378331005.",
1590
+ run: (v) => validateIsin(v)
1591
+ },
1592
+ {
1593
+ name: "validate_cusip",
1594
+ description: "USE THIS to verify a CUSIP (North American securities identifier) instead of trusting 9 characters. Checks the CUSIP mod-10 check digit and returns the expected digit when it fails.",
1595
+ argName: "cusip",
1596
+ argDescription: "The 9-character CUSIP, e.g. 037833100.",
1597
+ run: (v) => validateCusip(v)
1598
+ },
1599
+ {
1600
+ name: "validate_sedol",
1601
+ description: "USE THIS to verify a SEDOL (LSE securities identifier) before relying on it. Checks the no-vowels alphabet and the weighted mod-10 check digit.",
1602
+ argName: "sedol",
1603
+ argDescription: "The 7-character SEDOL, e.g. B0YBKJ7.",
1604
+ run: (v) => validateSedol(v)
1605
+ },
1606
+ {
1607
+ name: "validate_lei",
1608
+ description: "USE THIS to verify a Legal Entity Identifier (LEI) before relying on it \u2014 do not assume a 20-character code is valid. Checks the ISO 17442 / ISO 7064 MOD 97-10 check digits.",
1609
+ argName: "lei",
1610
+ argDescription: "The 20-character LEI, e.g. 5493001KJTIIGC8Y1R12.",
1611
+ run: (v) => validateLei(v)
1612
+ },
1613
+ {
1614
+ name: "validate_routing",
1615
+ description: "USE THIS to verify a US bank routing / ABA transit number before relying on it for a payment or direct deposit. Checks the 9-digit weighted (3,7,1) mod-10 checksum. Does NOT check whether the bank or account is real.",
1616
+ argName: "routing",
1617
+ argDescription: "The 9-digit ABA routing number.",
1618
+ run: (v) => validateAba(v)
1619
+ },
1620
+ {
1621
+ name: "validate_eth_address",
1622
+ description: "USE THIS to verify an Ethereum address before sending funds or storing it \u2014 never trust that a 0x\u2026 string is correct. Validates the format and the EIP-55 mixed-case checksum (catches typos), and returns the correctly-checksummed form. A wrong character makes a different address \u2014 funds sent there are lost.",
1623
+ argName: "address",
1624
+ argDescription: "The Ethereum address (0x + 40 hex chars).",
1625
+ run: (v) => validateEthAddress(v)
1626
+ },
1627
+ {
1628
+ name: "validate_btc_address",
1629
+ description: "USE THIS to verify a Bitcoin address before sending funds or storing it \u2014 do not assume it is valid. Checks Base58Check (P2PKH/P2SH, double-SHA256 checksum) and Bech32/Bech32m SegWit (bc1\u2026, incl. Taproot), and returns the address type and network. A bad checksum means a mistyped address.",
1630
+ argName: "address",
1631
+ argDescription: "The Bitcoin address (legacy 1\u2026/3\u2026 or bech32 bc1\u2026).",
1632
+ run: (v) => validateBtcAddress(v)
1633
+ },
1634
+ {
1635
+ name: "validate_cpf",
1636
+ description: "USE THIS to verify a Brazilian CPF (individual taxpayer ID) before relying on it \u2014 never assume 11 digits are valid. Checks the two mod-11 check digits and rejects all-identical sentinels. Call this for KYC/onboarding of Brazilian individuals.",
1637
+ argName: "cpf",
1638
+ argDescription: "The CPF (11 digits; dots and dash are ignored).",
1639
+ run: (v) => validateCpf(v)
1640
+ },
1641
+ {
1642
+ name: "validate_cnpj",
1643
+ description: "USE THIS to verify a Brazilian CNPJ (company registration number) instead of trusting 14 digits. Checks the two mod-11 check digits. Call this for onboarding Brazilian businesses.",
1644
+ argName: "cnpj",
1645
+ argDescription: "The CNPJ (14 digits; punctuation is ignored).",
1646
+ run: (v) => validateCnpj(v)
1647
+ },
1648
+ {
1649
+ name: "validate_sa_id",
1650
+ description: "USE THIS to verify a South African ID number before relying on it. Checks the Luhn check digit and date-of-birth validity, and returns the date of birth, gender and citizenship status encoded in the number.",
1651
+ argName: "id",
1652
+ argDescription: "The 13-digit South African ID number.",
1653
+ run: (v) => validateSaId(v)
1654
+ },
1655
+ {
1656
+ name: "validate_dni",
1657
+ description: "USE THIS to verify a Spanish DNI or NIE before relying on it \u2014 do not guess the control letter. Checks the mod-23 control letter and returns whether it is a DNI or NIE.",
1658
+ argName: "id",
1659
+ argDescription: "The Spanish DNI (8 digits + letter) or NIE (X/Y/Z + 7 digits + letter).",
1660
+ run: (v) => validateDni(v)
1661
+ },
1662
+ {
1663
+ name: "validate_aadhaar",
1664
+ description: "USE THIS to verify the format and checksum of an Indian Aadhaar number \u2014 never assume 12 digits are valid. Checks the Verhoeff check digit and the leading-digit rule. Validates structure only; does NOT look the number up.",
1665
+ argName: "aadhaar",
1666
+ argDescription: "The 12-digit Aadhaar number (spaces ignored).",
1667
+ run: (v) => validateAadhaar(v)
1668
+ },
1669
+ {
1670
+ name: "validate_isbn10",
1671
+ description: "USE THIS to verify an ISBN-10 (older book identifier) instead of trusting 10 characters. Checks the mod-11 check digit (which may be 'X'). For 13-digit ISBNs use validate_isbn.",
1672
+ argName: "isbn",
1673
+ argDescription: "The ISBN-10 (hyphens/spaces ignored).",
1674
+ run: (v) => validateIsbn10(v)
1675
+ },
1676
+ {
1677
+ name: "validate_issn",
1678
+ description: "USE THIS to verify an ISSN (serial/journal identifier) before relying on it. Checks the mod-11 check digit (which may be 'X') and returns the expected digit when it fails.",
1679
+ argName: "issn",
1680
+ argDescription: "The ISSN (8 chars; hyphen ignored).",
1681
+ run: (v) => validateIssn(v)
1682
+ },
1683
+ {
1684
+ name: "validate_orcid",
1685
+ description: "USE THIS to verify an ORCID researcher identifier instead of trusting 16 digits. Checks the ISO 7064 MOD 11-2 check digit (which may be 'X'); accepts the bare ID or an orcid.org URL.",
1686
+ argName: "orcid",
1687
+ argDescription: "The ORCID (e.g. 0000-0002-1825-0097, or an orcid.org URL).",
1688
+ run: (v) => validateOrcid(v)
1689
+ },
1690
+ {
1691
+ name: "validate_phone",
1692
+ description: "USE THIS to check a phone number is correctly formatted for its country and normalise it to E.164 before saving, dialling or texting. You MUST pass the ISO country the number ACTUALLY belongs to (e.g. GB, US, ZA) \u2014 the result depends on it, so don't reuse an unrelated country field. 'valid' means it conforms to that country's numbering plan (plausible, well-formed), NOT that the line is live or reachable. Returns E.164, national/international formats and line type.",
1693
+ args: [
1694
+ { name: "number", description: "The phone number to validate." },
1695
+ { name: "region", description: "ISO country code the number belongs to (default GB).", optional: true }
1696
+ ],
1697
+ runArgs: (a) => validatePhone(a.number ?? "", a.region ?? "GB")
1698
+ },
1699
+ {
1700
+ name: "parse_date",
1701
+ description: "USE THIS to interpret a human-written date into ISO 8601 (YYYY-MM-DD), especially ambiguous numeric dates like 03/04/2025 which mean different things in the UK (day-first) vs US (month-first). Pass locale 'en-GB' or 'en-US'. Returns valid:false for impossible dates.",
1702
+ args: [
1703
+ { name: "input", description: "The date text to parse." },
1704
+ { name: "locale", description: "'en-GB' (day-first) or 'en-US' (month-first); default en-GB.", optional: true }
1705
+ ],
1706
+ runArgs: (a) => parseDate(a.input ?? "", a.locale ?? "en-GB")
1707
+ },
1708
+ {
1709
+ name: "format_currency",
1710
+ description: "USE THIS to format a money amount the way a reader in a locale expects (symbol position, separators) before showing it in a price, invoice or email. e.g. 1234.5 GBP en-GB \u2192 '\xA31,234.50'.",
1711
+ args: [
1712
+ { name: "amount", description: "The numeric amount." },
1713
+ { name: "currency", description: "ISO 4217 currency code (default GBP).", optional: true },
1714
+ { name: "locale", description: "BCP-47 locale (e.g. en-GB). Defaults from the currency.", optional: true }
1715
+ ],
1716
+ runArgs: (a) => formatMoney(Number(a.amount), a.currency ?? "GBP", a.locale)
1717
+ },
1718
+ {
1719
+ name: "is_holiday",
1720
+ description: "USE THIS to check whether a date is a public/bank holiday when computing business-day deadlines, delivery SLAs or 'next working day'. Supports ~200 countries (ISO code, e.g. GB, US, ZA, DE, IN); GB defaults to England \u2014 pass a subdivision ('SCT'/'WLS'/'NIR', or a US state) to narrow.",
1721
+ args: [
1722
+ { name: "date", description: "The date (YYYY-MM-DD)." },
1723
+ { name: "country", description: "ISO country code (default GB), e.g. GB, US, ZA, DE.", optional: true },
1724
+ { name: "subdiv", description: "Subdivision code (e.g. UK nation SCT/WLS/NIR, or a US state).", optional: true }
1725
+ ],
1726
+ runArgs: (a) => isHoliday(a.date ?? "", a.country ?? "GB", a.subdiv)
1727
+ },
1728
+ {
1729
+ name: "next_holiday",
1730
+ description: "USE THIS to find the next public/bank holiday on or after a date (default today) \u2014 e.g. to find the next working day. Supports ~200 countries (ISO code, e.g. GB, US, ZA, DE); subdivision narrows to a region.",
1731
+ args: [
1732
+ { name: "country", description: "ISO country code (default GB), e.g. GB, US, ZA, DE.", optional: true },
1733
+ { name: "after", description: "Find the next holiday on/after this date (YYYY-MM-DD); default today.", optional: true },
1734
+ { name: "subdiv", description: "Subdivision code (e.g. UK nation SCT/WLS/NIR, or a US state).", optional: true }
1735
+ ],
1736
+ runArgs: (a) => nextHoliday(a.country ?? "GB", a.after, a.subdiv)
1737
+ },
1738
+ {
1739
+ name: "tax_rate",
1740
+ description: "USE THIS before calculating VAT or sales tax on an invoice/quote \u2014 never recall the rate from memory, it is DATE-SENSITIVE. GB returns the UK standard VAT rate that applied on the given date (handles historical/temporary changes). US has no national VAT (returns 0); pass a state code for the state base sales-tax rate. Always pass the invoice date for GB.",
1741
+ args: [
1742
+ { name: "country", description: "GB or US (default GB).", optional: true },
1743
+ { name: "date", description: "The invoice date (YYYY-MM-DD); default today.", optional: true },
1744
+ { name: "state", description: "US state code (e.g. CA) for sales tax.", optional: true }
1745
+ ],
1746
+ runArgs: (a) => vatRate(a.country ?? "GB", a.date, a.state)
1747
+ },
1748
+ {
1749
+ name: "parse_address",
1750
+ description: "USE THIS to extract structured {country, postcode, city, state} from a free-text UK or US address \u2014 when onboarding a user, running a KYC/fraud check, or storing an address \u2014 instead of splitting the string yourself. Returns a confidence flag.",
1751
+ args: [{ name: "input", description: "The free-text address." }],
1752
+ runArgs: (a) => parseAddress(a.input ?? "")
1753
+ },
1754
+ {
1755
+ name: "validate_vat",
1756
+ description: "USE THIS to verify an EU/EFTA VAT registration number's format and checksum before invoicing or onboarding a business \u2014 instead of trusting it looks right. Covers all EU members plus UK/EFTA. Pass the full number incl. country prefix (e.g. DE136695976) or the digits plus a country code. NOTE: checks format+checksum only; does NOT confirm the number is live-registered (that is a VIES lookup).",
1757
+ args: [
1758
+ { name: "vat", description: "The VAT number, ideally with its country prefix (e.g. DE136695976)." },
1759
+ { name: "country", description: "ISO country code, if the number has no prefix (e.g. DE).", optional: true }
1760
+ ],
1761
+ runArgs: (a) => validateVat(a.vat ?? "", a.country)
1762
+ }
1763
+ ];
1764
+ var SERVER_INFO = { name: "qiniso", version: "0.1.0" };
1765
+ var DEFAULT_PROTOCOL = "2025-06-18";
1766
+ function argList(t) {
1767
+ return t.args ?? [{ name: t.argName, description: t.argDescription }];
1768
+ }
1769
+ function inputSchema(t) {
1770
+ const properties = {};
1771
+ const required = [];
1772
+ for (const a of argList(t)) {
1773
+ properties[a.name] = { type: "string", description: a.description };
1774
+ if (!a.optional) required.push(a.name);
1775
+ }
1776
+ return { type: "object", properties, required, additionalProperties: false };
1777
+ }
1778
+ function listTools() {
1779
+ return TOOLS.map((t) => ({
1780
+ name: t.name,
1781
+ description: t.description,
1782
+ inputSchema: inputSchema(t)
1783
+ }));
1784
+ }
1785
+ function callTool(name, args) {
1786
+ const t = TOOLS.find((x) => x.name === name);
1787
+ if (!t) {
1788
+ const e = new Error(`Unknown tool: ${name}`);
1789
+ e.code = -32602;
1790
+ throw e;
1791
+ }
1792
+ let result;
1793
+ if (t.args) {
1794
+ const a = {};
1795
+ for (const arg of t.args) {
1796
+ const v = args?.[arg.name];
1797
+ a[arg.name] = v === void 0 || v === null ? void 0 : String(v);
1798
+ }
1799
+ result = t.runArgs(a);
1800
+ } else {
1801
+ result = t.run(String(args?.[t.argName] ?? ""));
1802
+ }
1803
+ return {
1804
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
1805
+ };
1806
+ }
1807
+ function handleRpc(msg) {
1808
+ const { id, method, params } = msg;
1809
+ if (id === void 0 || method === "notifications/initialized") return null;
1810
+ try {
1811
+ let result;
1812
+ switch (method) {
1813
+ case "initialize":
1814
+ result = {
1815
+ protocolVersion: params?.protocolVersion ?? DEFAULT_PROTOCOL,
1816
+ capabilities: { tools: {} },
1817
+ serverInfo: { ...SERVER_INFO, websiteUrl: PUBLIC_BASE, icons: ICONS }
1818
+ };
1819
+ break;
1820
+ case "tools/list":
1821
+ result = { tools: listTools() };
1822
+ break;
1823
+ case "tools/call":
1824
+ result = callTool(params?.name, params?.arguments);
1825
+ break;
1826
+ case "ping":
1827
+ result = {};
1828
+ break;
1829
+ default:
1830
+ return { jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${method}` } };
1831
+ }
1832
+ return { jsonrpc: "2.0", id, result };
1833
+ } catch (err) {
1834
+ return { jsonrpc: "2.0", id, error: { code: err?.code ?? -32603, message: err?.message ?? String(err) } };
1835
+ }
1836
+ }
1837
+
1838
+ export {
1839
+ normalizeIban,
1840
+ validateIban,
1841
+ ibanCheckDigits,
1842
+ supportedIbanCountries,
1843
+ luhnValid,
1844
+ luhnCheckDigit,
1845
+ detectBrand,
1846
+ validateCard,
1847
+ isbn13CheckDigit,
1848
+ validateIsbn13,
1849
+ vinCheckDigit,
1850
+ validateVin,
1851
+ isKnownTld,
1852
+ knownTldCount,
1853
+ validateTld,
1854
+ validateDomain,
1855
+ isIPv4,
1856
+ isIPv6,
1857
+ validateIp,
1858
+ validateUuid,
1859
+ validateUrl,
1860
+ validateEmail,
1861
+ validateIsin,
1862
+ validateCusip,
1863
+ validateSedol,
1864
+ validateLei,
1865
+ validateAba,
1866
+ validateEthAddress,
1867
+ validateBtcAddress,
1868
+ validateCpf,
1869
+ validateCnpj,
1870
+ validateSaId,
1871
+ validateDni,
1872
+ verhoeffValid,
1873
+ verhoeffGenerate,
1874
+ validateAadhaar,
1875
+ validateIsbn10,
1876
+ validateIssn,
1877
+ validateOrcid,
1878
+ parseDate,
1879
+ validatePhone,
1880
+ formatMoney,
1881
+ isHoliday,
1882
+ nextHoliday,
1883
+ vatRate,
1884
+ parseAddress,
1885
+ validateVat,
1886
+ LOGO_SVG,
1887
+ TOOLS,
1888
+ SERVER_INFO,
1889
+ listTools,
1890
+ callTool,
1891
+ handleRpc
1892
+ };