kassza 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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -0
  3. package/agents/README.md +59 -0
  4. package/agents/api.md +206 -0
  5. package/agents/pitfalls.md +52 -0
  6. package/agents/recipes.md +217 -0
  7. package/agents/skills/kassza/SKILL.md +29 -0
  8. package/assets/logo-wordmark.svg +25 -0
  9. package/assets/logo.svg +19 -0
  10. package/dist/binary-B89HM03_.cjs +63 -0
  11. package/dist/binary-D0M9U2MD.js +34 -0
  12. package/dist/client-B9kbKKBf.d.cts +748 -0
  13. package/dist/client-zv4enyq7.d.ts +748 -0
  14. package/dist/cookie-stores/index.cjs +153 -0
  15. package/dist/cookie-stores/index.d.cts +63 -0
  16. package/dist/cookie-stores/index.d.ts +63 -0
  17. package/dist/cookie-stores/index.js +144 -0
  18. package/dist/create-BZd6CUO7.cjs +1160 -0
  19. package/dist/create-DDZaNlOz.js +939 -0
  20. package/dist/dates-D2XebOIg.js +34 -0
  21. package/dist/dates-DtHzwNU6.d.cts +7 -0
  22. package/dist/dates-DtHzwNU6.d.ts +7 -0
  23. package/dist/dates-PPxH0n5H.cjs +57 -0
  24. package/dist/errors-B0QJhUW1.cjs +302 -0
  25. package/dist/errors-DapdV5DK.js +273 -0
  26. package/dist/index-CYAGCdKJ.d.cts +56 -0
  27. package/dist/index-CYAGCdKJ.d.ts +56 -0
  28. package/dist/index.cjs +1406 -0
  29. package/dist/index.d.cts +8 -0
  30. package/dist/index.d.ts +8 -0
  31. package/dist/index.js +1394 -0
  32. package/dist/ipn/index.cjs +108 -0
  33. package/dist/ipn/index.d.cts +32 -0
  34. package/dist/ipn/index.d.ts +32 -0
  35. package/dist/ipn/index.js +101 -0
  36. package/dist/money/index.cjs +15 -0
  37. package/dist/money/index.d.cts +2 -0
  38. package/dist/money/index.d.ts +2 -0
  39. package/dist/money/index.js +2 -0
  40. package/dist/money-C9j7nBNP.js +258 -0
  41. package/dist/money-DkiGukAw.cjs +335 -0
  42. package/dist/session-CRGOuz-R.cjs +100 -0
  43. package/dist/session-Dm_45x8K.d.cts +11 -0
  44. package/dist/session-Dm_45x8K.d.ts +11 -0
  45. package/dist/session-OJMLGufT.js +77 -0
  46. package/dist/shared-CrxlxI8n.cjs +207 -0
  47. package/dist/shared-D6DLa4b7.js +100 -0
  48. package/dist/storage/fs.cjs +88 -0
  49. package/dist/storage/fs.d.cts +13 -0
  50. package/dist/storage/fs.d.ts +13 -0
  51. package/dist/storage/fs.js +87 -0
  52. package/dist/storage/index.cjs +643 -0
  53. package/dist/storage/index.d.cts +288 -0
  54. package/dist/storage/index.d.ts +288 -0
  55. package/dist/storage/index.js +619 -0
  56. package/dist/testing/index.cjs +409 -0
  57. package/dist/testing/index.d.cts +35 -0
  58. package/dist/testing/index.d.ts +35 -0
  59. package/dist/testing/index.js +407 -0
  60. package/dist/types-DVRgwcqg.d.cts +26 -0
  61. package/dist/types-DVRgwcqg.d.ts +26 -0
  62. package/dist/validators/index.cjs +296 -0
  63. package/dist/validators/index.d.cts +51 -0
  64. package/dist/validators/index.d.ts +51 -0
  65. package/dist/validators/index.js +278 -0
  66. package/llms.txt +16 -0
  67. package/package.json +151 -0
@@ -0,0 +1,296 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/validators/address.ts
3
+ const MIN_ZIP = 1e3;
4
+ const MAX_ZIP = 9999;
5
+ const MAX_BUDAPEST_DISTRICT = 23;
6
+ const ROMAN_VALUES = {
7
+ I: 1,
8
+ V: 5,
9
+ X: 10
10
+ };
11
+ function isValidHungarianZipCode(value) {
12
+ const trimmed = (typeof value === "number" ? String(value) : typeof value === "string" ? value : "").trim();
13
+ if (!/^\d{4}$/.test(trimmed)) return false;
14
+ const zip = Number(trimmed);
15
+ return zip >= MIN_ZIP && zip <= MAX_ZIP;
16
+ }
17
+ function romanToNumber(roman) {
18
+ let total = 0;
19
+ for (let index = 0; index < roman.length; index++) {
20
+ const current = ROMAN_VALUES[roman.charAt(index)] ?? 0;
21
+ const next = ROMAN_VALUES[roman.charAt(index + 1)] ?? 0;
22
+ total += current < next ? -current : current;
23
+ }
24
+ return total;
25
+ }
26
+ const ROMAN_DISTRICT = /^([IVX]{1,5})(?:\.\s*(?:[Kk]er(?:ület)?\.?)?|\s+[Kk]er(?:ület)?\.?)(?=[\s,]|$)/;
27
+ const ARABIC_DISTRICT = /^(\d{1,2})\.?\s*[Kk]er(?:ület)?\.?(?=[\s,]|$)/;
28
+ function matchDistrict(text) {
29
+ const roman = ROMAN_DISTRICT.exec(text);
30
+ const arabic = roman ? void 0 : ARABIC_DISTRICT.exec(text);
31
+ const match = roman ?? arabic;
32
+ if (!match?.[1]) return void 0;
33
+ const district = roman ? romanToNumber(match[1]) : Number(match[1]);
34
+ if (district < 1 || district > MAX_BUDAPEST_DISTRICT) return void 0;
35
+ return {
36
+ district,
37
+ rest: text.slice(match[0].length).replace(/^[\s,]+/, "")
38
+ };
39
+ }
40
+ function normalizeInput(value) {
41
+ return value.replace(/[\r\n]+/g, ", ").replace(/\s+/g, " ").replace(/\s*(?:,\s*)+/g, ", ").replace(/^(?:HU|H)\s*-\s*/i, "").replace(/,?\s*(?:Magyarország|Hungary)\.?$/i, "").replace(/^[\s,]+|[\s,]+$/g, "");
42
+ }
43
+ function isBudapest(city) {
44
+ return city.toLowerCase() === "budapest";
45
+ }
46
+ function splitCityAndAddress(rest) {
47
+ const comma = rest.indexOf(",");
48
+ if (comma !== -1) return {
49
+ city: rest.slice(0, comma).trim(),
50
+ address: rest.slice(comma + 1).trim()
51
+ };
52
+ const space = rest.indexOf(" ");
53
+ if (space === -1) return void 0;
54
+ return {
55
+ city: rest.slice(0, space),
56
+ address: rest.slice(space + 1).trim()
57
+ };
58
+ }
59
+ function buildAddress(zip, cityPart, addressPart) {
60
+ let city = cityPart.trim();
61
+ let address = addressPart.trim();
62
+ let district;
63
+ const cityWithDistrict = /^(\S+)\s+(.+)$/.exec(city);
64
+ if (cityWithDistrict?.[1] && cityWithDistrict[2] && isBudapest(cityWithDistrict[1])) {
65
+ const match = matchDistrict(cityWithDistrict[2]);
66
+ if (match && match.rest === "") {
67
+ city = cityWithDistrict[1];
68
+ district = match.district;
69
+ }
70
+ }
71
+ if (district === void 0 && isBudapest(city)) {
72
+ const match = matchDistrict(address);
73
+ if (match) {
74
+ district = match.district;
75
+ address = match.rest;
76
+ }
77
+ }
78
+ if (!isValidHungarianZipCode(zip) || !/\p{L}/u.test(city) || address === "") return void 0;
79
+ return district === void 0 ? {
80
+ zip,
81
+ city,
82
+ address
83
+ } : {
84
+ zip,
85
+ city,
86
+ address,
87
+ district
88
+ };
89
+ }
90
+ function parseHungarianAddress(value) {
91
+ if (typeof value !== "string") return void 0;
92
+ const text = normalizeInput(value);
93
+ const zipFirst = /^(\d{4}),?\s+(.+)$/.exec(text);
94
+ if (zipFirst?.[1] && zipFirst[2]) {
95
+ const parts = splitCityAndAddress(zipFirst[2]);
96
+ return parts ? buildAddress(zipFirst[1], parts.city, parts.address) : void 0;
97
+ }
98
+ const zipLast = /^(.+?),\s*(\d{4})\s+([^,\d][^,]*)$/.exec(text);
99
+ if (zipLast?.[1] && zipLast[2] && zipLast[3]) return buildAddress(zipLast[2], zipLast[3], zipLast[1]);
100
+ }
101
+ //#endregion
102
+ //#region src/validators/agent-key.ts
103
+ function isValidAgentKey(value) {
104
+ if (typeof value !== "string") return false;
105
+ const key = value.trim();
106
+ return key !== "" && !/\s/.test(key) && key === key.toLowerCase();
107
+ }
108
+ //#endregion
109
+ //#region src/validators/digits.ts
110
+ const SEPARATORS = /[\s-]+/g;
111
+ function stripSeparators(value) {
112
+ return value.replace(SEPARATORS, "");
113
+ }
114
+ function isDigits(value, length) {
115
+ if (!/^\d+$/.test(value)) return false;
116
+ return length === void 0 || value.length === length;
117
+ }
118
+ function weightedDigitSum(digits, weights) {
119
+ return [...digits].reduce((sum, digit, index) => sum + Number(digit) * Number(weights[index % weights.length]), 0);
120
+ }
121
+ //#endregion
122
+ //#region src/validators/bank-account.ts
123
+ const GIRO_WEIGHTS = [
124
+ 9,
125
+ 7,
126
+ 3,
127
+ 1
128
+ ];
129
+ const HU_IBAN_LENGTH = 28;
130
+ function hasValidCheckDigit(block) {
131
+ return weightedDigitSum(block, GIRO_WEIGHTS) % 10 === 0;
132
+ }
133
+ function groupsOfEight(digits) {
134
+ return digits.match(/\d{8}/g)?.join("-") ?? digits;
135
+ }
136
+ function parseHungarianBankAccount(value) {
137
+ if (typeof value !== "string") return void 0;
138
+ const digits = stripSeparators(value);
139
+ if (!isDigits(digits, 16) && !isDigits(digits, 24)) return void 0;
140
+ if (!hasValidCheckDigit(digits.slice(0, 8)) || !hasValidCheckDigit(digits.slice(8))) return;
141
+ return {
142
+ digits,
143
+ bankCode: digits.slice(0, 3),
144
+ branchCode: digits.slice(3, 7),
145
+ formatted: groupsOfEight(digits)
146
+ };
147
+ }
148
+ function isValidHungarianBankAccount(value) {
149
+ return parseHungarianBankAccount(value) !== void 0;
150
+ }
151
+ function formatHungarianBankAccount(value) {
152
+ return parseHungarianBankAccount(value)?.formatted;
153
+ }
154
+ function ibanMod97(iban) {
155
+ const rearranged = `${iban.slice(4)}${iban.slice(0, 4)}`;
156
+ let remainder = 0;
157
+ for (const char of rearranged) {
158
+ const code = char.charCodeAt(0);
159
+ const chunk = code >= 65 ? String(code - 55) : char;
160
+ for (const digit of chunk) remainder = (remainder * 10 + Number(digit)) % 97;
161
+ }
162
+ return remainder;
163
+ }
164
+ function isValidHungarianIban(value) {
165
+ if (typeof value !== "string") return false;
166
+ const iban = stripSeparators(value).toUpperCase();
167
+ if (iban.length !== HU_IBAN_LENGTH || !iban.startsWith("HU")) return false;
168
+ if (!isDigits(iban.slice(2), 26)) return false;
169
+ return ibanMod97(iban) === 1;
170
+ }
171
+ //#endregion
172
+ //#region src/validators/email.ts
173
+ const MAX_EMAIL_LENGTH = 254;
174
+ const EMAIL_PATTERN = /^[^\s@]+@[^\s@.]+(?:\.[^\s@.]+)+$/;
175
+ function normalizeEmail(value) {
176
+ return value.trim().toLowerCase();
177
+ }
178
+ function isValidEmail(value) {
179
+ if (typeof value !== "string") return false;
180
+ const email = value.trim();
181
+ return email.length <= MAX_EMAIL_LENGTH && EMAIL_PATTERN.test(email);
182
+ }
183
+ //#endregion
184
+ //#region src/validators/eu-vat.ts
185
+ const EU_VAT_NUMBER_PATTERNS = {
186
+ AT: /^U\d{8}$/,
187
+ BE: /^[01]\d{9}$/,
188
+ BG: /^\d{9,10}$/,
189
+ CY: /^\d{8}[A-Z]$/,
190
+ CZ: /^\d{8,10}$/,
191
+ DE: /^\d{9}$/,
192
+ DK: /^\d{8}$/,
193
+ EE: /^\d{9}$/,
194
+ EL: /^\d{9}$/,
195
+ ES: /^[A-Z0-9]\d{7}[A-Z0-9]$/,
196
+ FI: /^\d{8}$/,
197
+ FR: /^[A-HJ-NP-Z0-9]{2}\d{9}$/,
198
+ HR: /^\d{11}$/,
199
+ HU: /^\d{8}$/,
200
+ IE: /^(?:\d{7}[A-W][A-I]?|\d[A-Z+*]\d{5}[A-W])$/,
201
+ IT: /^\d{11}$/,
202
+ LT: /^(?:\d{9}|\d{12})$/,
203
+ LU: /^\d{8}$/,
204
+ LV: /^\d{11}$/,
205
+ MT: /^\d{8}$/,
206
+ NL: /^\d{9}B\d{2}$/,
207
+ PL: /^\d{10}$/,
208
+ PT: /^\d{9}$/,
209
+ RO: /^[1-9]\d{1,9}$/,
210
+ SE: /^\d{10}01$/,
211
+ SI: /^\d{8}$/,
212
+ SK: /^\d{10}$/,
213
+ XI: /^(?:\d{9}|\d{12}|GD[0-4]\d{2}|HA[5-9]\d{2})$/
214
+ };
215
+ function isEuVatCountryPrefix(value) {
216
+ return Object.hasOwn(EU_VAT_NUMBER_PATTERNS, value);
217
+ }
218
+ function normalizeEuVatNumber(value) {
219
+ return value.replace(/[\s.-]+/g, "").toUpperCase();
220
+ }
221
+ function isValidEuVatNumber(value) {
222
+ if (typeof value !== "string") return false;
223
+ const normalized = normalizeEuVatNumber(value);
224
+ const prefix = normalized.slice(0, 2);
225
+ if (!isEuVatCountryPrefix(prefix)) return false;
226
+ return EU_VAT_NUMBER_PATTERNS[prefix].test(normalized.slice(2));
227
+ }
228
+ //#endregion
229
+ //#region src/validators/tax-number.ts
230
+ const TAXPAYER_ID_WEIGHTS = [
231
+ 9,
232
+ 7,
233
+ 3,
234
+ 1,
235
+ 9,
236
+ 7,
237
+ 3
238
+ ];
239
+ const HUNGARIAN_TAX_COUNTY_CODES = [
240
+ ...Array.from({ length: 19 }, (_, index) => String(index + 2).padStart(2, "0")),
241
+ ...Array.from({ length: 23 }, (_, index) => String(index + 22)),
242
+ "51"
243
+ ];
244
+ const COUNTY_CODE_SET = new Set(HUNGARIAN_TAX_COUNTY_CODES);
245
+ function isVatCode(value) {
246
+ return value >= "1" && value <= "5" && value.length === 1;
247
+ }
248
+ function isValidHungarianTaxpayerId(value) {
249
+ const digits = stripSeparators(value);
250
+ if (!isDigits(digits, 8)) return false;
251
+ return (10 - weightedDigitSum(digits.slice(0, 7), TAXPAYER_ID_WEIGHTS) % 10) % 10 === Number(digits[7]);
252
+ }
253
+ function parseHungarianTaxNumber(value) {
254
+ if (typeof value !== "string") return void 0;
255
+ const digits = stripSeparators(value);
256
+ if (!isDigits(digits, 11)) return void 0;
257
+ const taxpayerId = digits.slice(0, 8);
258
+ const vatCode = digits.slice(8, 9);
259
+ const countyCode = digits.slice(9, 11);
260
+ if (!isValidHungarianTaxpayerId(taxpayerId)) return void 0;
261
+ if (!isVatCode(vatCode) || !COUNTY_CODE_SET.has(countyCode)) return void 0;
262
+ return {
263
+ taxpayerId,
264
+ vatCode,
265
+ countyCode,
266
+ formatted: `${taxpayerId}-${vatCode}-${countyCode}`
267
+ };
268
+ }
269
+ function isValidHungarianTaxNumber(value) {
270
+ return parseHungarianTaxNumber(value) !== void 0;
271
+ }
272
+ function isValidHungarianGroupTaxNumber(value) {
273
+ return parseHungarianTaxNumber(value)?.vatCode === "5";
274
+ }
275
+ function isHungarianVatGroupMemberTaxNumber(value) {
276
+ return parseHungarianTaxNumber(value)?.vatCode === "4";
277
+ }
278
+ //#endregion
279
+ exports.EU_VAT_NUMBER_PATTERNS = EU_VAT_NUMBER_PATTERNS;
280
+ exports.HUNGARIAN_TAX_COUNTY_CODES = HUNGARIAN_TAX_COUNTY_CODES;
281
+ exports.formatHungarianBankAccount = formatHungarianBankAccount;
282
+ exports.isHungarianVatGroupMemberTaxNumber = isHungarianVatGroupMemberTaxNumber;
283
+ exports.isValidAgentKey = isValidAgentKey;
284
+ exports.isValidEmail = isValidEmail;
285
+ exports.isValidEuVatNumber = isValidEuVatNumber;
286
+ exports.isValidHungarianBankAccount = isValidHungarianBankAccount;
287
+ exports.isValidHungarianGroupTaxNumber = isValidHungarianGroupTaxNumber;
288
+ exports.isValidHungarianIban = isValidHungarianIban;
289
+ exports.isValidHungarianTaxNumber = isValidHungarianTaxNumber;
290
+ exports.isValidHungarianTaxpayerId = isValidHungarianTaxpayerId;
291
+ exports.isValidHungarianZipCode = isValidHungarianZipCode;
292
+ exports.normalizeEmail = normalizeEmail;
293
+ exports.normalizeEuVatNumber = normalizeEuVatNumber;
294
+ exports.parseHungarianAddress = parseHungarianAddress;
295
+ exports.parseHungarianBankAccount = parseHungarianBankAccount;
296
+ exports.parseHungarianTaxNumber = parseHungarianTaxNumber;
@@ -0,0 +1,51 @@
1
+ //#region src/validators/address.d.ts
2
+ interface HungarianAddress {
3
+ readonly zip: string;
4
+ readonly city: string;
5
+ readonly address: string;
6
+ readonly district?: number | undefined;
7
+ }
8
+ export declare function isValidHungarianZipCode(value: string | number): boolean;
9
+ export declare function parseHungarianAddress(value: string): HungarianAddress | undefined;
10
+ //#endregion
11
+ //#region src/validators/agent-key.d.ts
12
+ export declare function isValidAgentKey(value: string): boolean;
13
+ //#endregion
14
+ //#region src/validators/bank-account.d.ts
15
+ interface HungarianBankAccount {
16
+ readonly digits: string;
17
+ readonly bankCode: string;
18
+ readonly branchCode: string;
19
+ readonly formatted: string;
20
+ }
21
+ export declare function parseHungarianBankAccount(value: string): HungarianBankAccount | undefined;
22
+ export declare function isValidHungarianBankAccount(value: string): boolean;
23
+ export declare function formatHungarianBankAccount(value: string): string | undefined;
24
+ export declare function isValidHungarianIban(value: string): boolean;
25
+ //#endregion
26
+ //#region src/validators/email.d.ts
27
+ export declare function normalizeEmail(value: string): string;
28
+ export declare function isValidEmail(value: string): boolean;
29
+ //#endregion
30
+ //#region src/validators/eu-vat.d.ts
31
+ type EuVatCountryPrefix = "AT" | "BE" | "BG" | "CY" | "CZ" | "DE" | "DK" | "EE" | "EL" | "ES" | "FI" | "FR" | "HR" | "HU" | "IE" | "IT" | "LT" | "LU" | "LV" | "MT" | "NL" | "PL" | "PT" | "RO" | "SE" | "SI" | "SK" | "XI";
32
+ export declare const EU_VAT_NUMBER_PATTERNS: Readonly<Record<EuVatCountryPrefix, RegExp>>;
33
+ export declare function normalizeEuVatNumber(value: string): string;
34
+ export declare function isValidEuVatNumber(value: string): boolean;
35
+ //#endregion
36
+ //#region src/validators/tax-number.d.ts
37
+ type HungarianVatCode = "1" | "2" | "3" | "4" | "5";
38
+ interface HungarianTaxNumber {
39
+ readonly taxpayerId: string;
40
+ readonly vatCode: HungarianVatCode;
41
+ readonly countyCode: string;
42
+ readonly formatted: string;
43
+ }
44
+ export declare const HUNGARIAN_TAX_COUNTY_CODES: readonly string[];
45
+ export declare function isValidHungarianTaxpayerId(value: string): boolean;
46
+ export declare function parseHungarianTaxNumber(value: string): HungarianTaxNumber | undefined;
47
+ export declare function isValidHungarianTaxNumber(value: string): boolean;
48
+ export declare function isValidHungarianGroupTaxNumber(value: string): boolean;
49
+ export declare function isHungarianVatGroupMemberTaxNumber(value: string): boolean;
50
+ //#endregion
51
+ export type { EuVatCountryPrefix, HungarianAddress, HungarianBankAccount, HungarianTaxNumber, HungarianVatCode };
@@ -0,0 +1,51 @@
1
+ //#region src/validators/address.d.ts
2
+ interface HungarianAddress {
3
+ readonly zip: string;
4
+ readonly city: string;
5
+ readonly address: string;
6
+ readonly district?: number | undefined;
7
+ }
8
+ export declare function isValidHungarianZipCode(value: string | number): boolean;
9
+ export declare function parseHungarianAddress(value: string): HungarianAddress | undefined;
10
+ //#endregion
11
+ //#region src/validators/agent-key.d.ts
12
+ export declare function isValidAgentKey(value: string): boolean;
13
+ //#endregion
14
+ //#region src/validators/bank-account.d.ts
15
+ interface HungarianBankAccount {
16
+ readonly digits: string;
17
+ readonly bankCode: string;
18
+ readonly branchCode: string;
19
+ readonly formatted: string;
20
+ }
21
+ export declare function parseHungarianBankAccount(value: string): HungarianBankAccount | undefined;
22
+ export declare function isValidHungarianBankAccount(value: string): boolean;
23
+ export declare function formatHungarianBankAccount(value: string): string | undefined;
24
+ export declare function isValidHungarianIban(value: string): boolean;
25
+ //#endregion
26
+ //#region src/validators/email.d.ts
27
+ export declare function normalizeEmail(value: string): string;
28
+ export declare function isValidEmail(value: string): boolean;
29
+ //#endregion
30
+ //#region src/validators/eu-vat.d.ts
31
+ type EuVatCountryPrefix = "AT" | "BE" | "BG" | "CY" | "CZ" | "DE" | "DK" | "EE" | "EL" | "ES" | "FI" | "FR" | "HR" | "HU" | "IE" | "IT" | "LT" | "LU" | "LV" | "MT" | "NL" | "PL" | "PT" | "RO" | "SE" | "SI" | "SK" | "XI";
32
+ export declare const EU_VAT_NUMBER_PATTERNS: Readonly<Record<EuVatCountryPrefix, RegExp>>;
33
+ export declare function normalizeEuVatNumber(value: string): string;
34
+ export declare function isValidEuVatNumber(value: string): boolean;
35
+ //#endregion
36
+ //#region src/validators/tax-number.d.ts
37
+ type HungarianVatCode = "1" | "2" | "3" | "4" | "5";
38
+ interface HungarianTaxNumber {
39
+ readonly taxpayerId: string;
40
+ readonly vatCode: HungarianVatCode;
41
+ readonly countyCode: string;
42
+ readonly formatted: string;
43
+ }
44
+ export declare const HUNGARIAN_TAX_COUNTY_CODES: readonly string[];
45
+ export declare function isValidHungarianTaxpayerId(value: string): boolean;
46
+ export declare function parseHungarianTaxNumber(value: string): HungarianTaxNumber | undefined;
47
+ export declare function isValidHungarianTaxNumber(value: string): boolean;
48
+ export declare function isValidHungarianGroupTaxNumber(value: string): boolean;
49
+ export declare function isHungarianVatGroupMemberTaxNumber(value: string): boolean;
50
+ //#endregion
51
+ export type { EuVatCountryPrefix, HungarianAddress, HungarianBankAccount, HungarianTaxNumber, HungarianVatCode };
@@ -0,0 +1,278 @@
1
+ //#region src/validators/address.ts
2
+ const MIN_ZIP = 1e3;
3
+ const MAX_ZIP = 9999;
4
+ const MAX_BUDAPEST_DISTRICT = 23;
5
+ const ROMAN_VALUES = {
6
+ I: 1,
7
+ V: 5,
8
+ X: 10
9
+ };
10
+ function isValidHungarianZipCode(value) {
11
+ const trimmed = (typeof value === "number" ? String(value) : typeof value === "string" ? value : "").trim();
12
+ if (!/^\d{4}$/.test(trimmed)) return false;
13
+ const zip = Number(trimmed);
14
+ return zip >= MIN_ZIP && zip <= MAX_ZIP;
15
+ }
16
+ function romanToNumber(roman) {
17
+ let total = 0;
18
+ for (let index = 0; index < roman.length; index++) {
19
+ const current = ROMAN_VALUES[roman.charAt(index)] ?? 0;
20
+ const next = ROMAN_VALUES[roman.charAt(index + 1)] ?? 0;
21
+ total += current < next ? -current : current;
22
+ }
23
+ return total;
24
+ }
25
+ const ROMAN_DISTRICT = /^([IVX]{1,5})(?:\.\s*(?:[Kk]er(?:ület)?\.?)?|\s+[Kk]er(?:ület)?\.?)(?=[\s,]|$)/;
26
+ const ARABIC_DISTRICT = /^(\d{1,2})\.?\s*[Kk]er(?:ület)?\.?(?=[\s,]|$)/;
27
+ function matchDistrict(text) {
28
+ const roman = ROMAN_DISTRICT.exec(text);
29
+ const arabic = roman ? void 0 : ARABIC_DISTRICT.exec(text);
30
+ const match = roman ?? arabic;
31
+ if (!match?.[1]) return void 0;
32
+ const district = roman ? romanToNumber(match[1]) : Number(match[1]);
33
+ if (district < 1 || district > MAX_BUDAPEST_DISTRICT) return void 0;
34
+ return {
35
+ district,
36
+ rest: text.slice(match[0].length).replace(/^[\s,]+/, "")
37
+ };
38
+ }
39
+ function normalizeInput(value) {
40
+ return value.replace(/[\r\n]+/g, ", ").replace(/\s+/g, " ").replace(/\s*(?:,\s*)+/g, ", ").replace(/^(?:HU|H)\s*-\s*/i, "").replace(/,?\s*(?:Magyarország|Hungary)\.?$/i, "").replace(/^[\s,]+|[\s,]+$/g, "");
41
+ }
42
+ function isBudapest(city) {
43
+ return city.toLowerCase() === "budapest";
44
+ }
45
+ function splitCityAndAddress(rest) {
46
+ const comma = rest.indexOf(",");
47
+ if (comma !== -1) return {
48
+ city: rest.slice(0, comma).trim(),
49
+ address: rest.slice(comma + 1).trim()
50
+ };
51
+ const space = rest.indexOf(" ");
52
+ if (space === -1) return void 0;
53
+ return {
54
+ city: rest.slice(0, space),
55
+ address: rest.slice(space + 1).trim()
56
+ };
57
+ }
58
+ function buildAddress(zip, cityPart, addressPart) {
59
+ let city = cityPart.trim();
60
+ let address = addressPart.trim();
61
+ let district;
62
+ const cityWithDistrict = /^(\S+)\s+(.+)$/.exec(city);
63
+ if (cityWithDistrict?.[1] && cityWithDistrict[2] && isBudapest(cityWithDistrict[1])) {
64
+ const match = matchDistrict(cityWithDistrict[2]);
65
+ if (match && match.rest === "") {
66
+ city = cityWithDistrict[1];
67
+ district = match.district;
68
+ }
69
+ }
70
+ if (district === void 0 && isBudapest(city)) {
71
+ const match = matchDistrict(address);
72
+ if (match) {
73
+ district = match.district;
74
+ address = match.rest;
75
+ }
76
+ }
77
+ if (!isValidHungarianZipCode(zip) || !/\p{L}/u.test(city) || address === "") return void 0;
78
+ return district === void 0 ? {
79
+ zip,
80
+ city,
81
+ address
82
+ } : {
83
+ zip,
84
+ city,
85
+ address,
86
+ district
87
+ };
88
+ }
89
+ function parseHungarianAddress(value) {
90
+ if (typeof value !== "string") return void 0;
91
+ const text = normalizeInput(value);
92
+ const zipFirst = /^(\d{4}),?\s+(.+)$/.exec(text);
93
+ if (zipFirst?.[1] && zipFirst[2]) {
94
+ const parts = splitCityAndAddress(zipFirst[2]);
95
+ return parts ? buildAddress(zipFirst[1], parts.city, parts.address) : void 0;
96
+ }
97
+ const zipLast = /^(.+?),\s*(\d{4})\s+([^,\d][^,]*)$/.exec(text);
98
+ if (zipLast?.[1] && zipLast[2] && zipLast[3]) return buildAddress(zipLast[2], zipLast[3], zipLast[1]);
99
+ }
100
+ //#endregion
101
+ //#region src/validators/agent-key.ts
102
+ function isValidAgentKey(value) {
103
+ if (typeof value !== "string") return false;
104
+ const key = value.trim();
105
+ return key !== "" && !/\s/.test(key) && key === key.toLowerCase();
106
+ }
107
+ //#endregion
108
+ //#region src/validators/digits.ts
109
+ const SEPARATORS = /[\s-]+/g;
110
+ function stripSeparators(value) {
111
+ return value.replace(SEPARATORS, "");
112
+ }
113
+ function isDigits(value, length) {
114
+ if (!/^\d+$/.test(value)) return false;
115
+ return length === void 0 || value.length === length;
116
+ }
117
+ function weightedDigitSum(digits, weights) {
118
+ return [...digits].reduce((sum, digit, index) => sum + Number(digit) * Number(weights[index % weights.length]), 0);
119
+ }
120
+ //#endregion
121
+ //#region src/validators/bank-account.ts
122
+ const GIRO_WEIGHTS = [
123
+ 9,
124
+ 7,
125
+ 3,
126
+ 1
127
+ ];
128
+ const HU_IBAN_LENGTH = 28;
129
+ function hasValidCheckDigit(block) {
130
+ return weightedDigitSum(block, GIRO_WEIGHTS) % 10 === 0;
131
+ }
132
+ function groupsOfEight(digits) {
133
+ return digits.match(/\d{8}/g)?.join("-") ?? digits;
134
+ }
135
+ function parseHungarianBankAccount(value) {
136
+ if (typeof value !== "string") return void 0;
137
+ const digits = stripSeparators(value);
138
+ if (!isDigits(digits, 16) && !isDigits(digits, 24)) return void 0;
139
+ if (!hasValidCheckDigit(digits.slice(0, 8)) || !hasValidCheckDigit(digits.slice(8))) return;
140
+ return {
141
+ digits,
142
+ bankCode: digits.slice(0, 3),
143
+ branchCode: digits.slice(3, 7),
144
+ formatted: groupsOfEight(digits)
145
+ };
146
+ }
147
+ function isValidHungarianBankAccount(value) {
148
+ return parseHungarianBankAccount(value) !== void 0;
149
+ }
150
+ function formatHungarianBankAccount(value) {
151
+ return parseHungarianBankAccount(value)?.formatted;
152
+ }
153
+ function ibanMod97(iban) {
154
+ const rearranged = `${iban.slice(4)}${iban.slice(0, 4)}`;
155
+ let remainder = 0;
156
+ for (const char of rearranged) {
157
+ const code = char.charCodeAt(0);
158
+ const chunk = code >= 65 ? String(code - 55) : char;
159
+ for (const digit of chunk) remainder = (remainder * 10 + Number(digit)) % 97;
160
+ }
161
+ return remainder;
162
+ }
163
+ function isValidHungarianIban(value) {
164
+ if (typeof value !== "string") return false;
165
+ const iban = stripSeparators(value).toUpperCase();
166
+ if (iban.length !== HU_IBAN_LENGTH || !iban.startsWith("HU")) return false;
167
+ if (!isDigits(iban.slice(2), 26)) return false;
168
+ return ibanMod97(iban) === 1;
169
+ }
170
+ //#endregion
171
+ //#region src/validators/email.ts
172
+ const MAX_EMAIL_LENGTH = 254;
173
+ const EMAIL_PATTERN = /^[^\s@]+@[^\s@.]+(?:\.[^\s@.]+)+$/;
174
+ function normalizeEmail(value) {
175
+ return value.trim().toLowerCase();
176
+ }
177
+ function isValidEmail(value) {
178
+ if (typeof value !== "string") return false;
179
+ const email = value.trim();
180
+ return email.length <= MAX_EMAIL_LENGTH && EMAIL_PATTERN.test(email);
181
+ }
182
+ //#endregion
183
+ //#region src/validators/eu-vat.ts
184
+ const EU_VAT_NUMBER_PATTERNS = {
185
+ AT: /^U\d{8}$/,
186
+ BE: /^[01]\d{9}$/,
187
+ BG: /^\d{9,10}$/,
188
+ CY: /^\d{8}[A-Z]$/,
189
+ CZ: /^\d{8,10}$/,
190
+ DE: /^\d{9}$/,
191
+ DK: /^\d{8}$/,
192
+ EE: /^\d{9}$/,
193
+ EL: /^\d{9}$/,
194
+ ES: /^[A-Z0-9]\d{7}[A-Z0-9]$/,
195
+ FI: /^\d{8}$/,
196
+ FR: /^[A-HJ-NP-Z0-9]{2}\d{9}$/,
197
+ HR: /^\d{11}$/,
198
+ HU: /^\d{8}$/,
199
+ IE: /^(?:\d{7}[A-W][A-I]?|\d[A-Z+*]\d{5}[A-W])$/,
200
+ IT: /^\d{11}$/,
201
+ LT: /^(?:\d{9}|\d{12})$/,
202
+ LU: /^\d{8}$/,
203
+ LV: /^\d{11}$/,
204
+ MT: /^\d{8}$/,
205
+ NL: /^\d{9}B\d{2}$/,
206
+ PL: /^\d{10}$/,
207
+ PT: /^\d{9}$/,
208
+ RO: /^[1-9]\d{1,9}$/,
209
+ SE: /^\d{10}01$/,
210
+ SI: /^\d{8}$/,
211
+ SK: /^\d{10}$/,
212
+ XI: /^(?:\d{9}|\d{12}|GD[0-4]\d{2}|HA[5-9]\d{2})$/
213
+ };
214
+ function isEuVatCountryPrefix(value) {
215
+ return Object.hasOwn(EU_VAT_NUMBER_PATTERNS, value);
216
+ }
217
+ function normalizeEuVatNumber(value) {
218
+ return value.replace(/[\s.-]+/g, "").toUpperCase();
219
+ }
220
+ function isValidEuVatNumber(value) {
221
+ if (typeof value !== "string") return false;
222
+ const normalized = normalizeEuVatNumber(value);
223
+ const prefix = normalized.slice(0, 2);
224
+ if (!isEuVatCountryPrefix(prefix)) return false;
225
+ return EU_VAT_NUMBER_PATTERNS[prefix].test(normalized.slice(2));
226
+ }
227
+ //#endregion
228
+ //#region src/validators/tax-number.ts
229
+ const TAXPAYER_ID_WEIGHTS = [
230
+ 9,
231
+ 7,
232
+ 3,
233
+ 1,
234
+ 9,
235
+ 7,
236
+ 3
237
+ ];
238
+ const HUNGARIAN_TAX_COUNTY_CODES = [
239
+ ...Array.from({ length: 19 }, (_, index) => String(index + 2).padStart(2, "0")),
240
+ ...Array.from({ length: 23 }, (_, index) => String(index + 22)),
241
+ "51"
242
+ ];
243
+ const COUNTY_CODE_SET = new Set(HUNGARIAN_TAX_COUNTY_CODES);
244
+ function isVatCode(value) {
245
+ return value >= "1" && value <= "5" && value.length === 1;
246
+ }
247
+ function isValidHungarianTaxpayerId(value) {
248
+ const digits = stripSeparators(value);
249
+ if (!isDigits(digits, 8)) return false;
250
+ return (10 - weightedDigitSum(digits.slice(0, 7), TAXPAYER_ID_WEIGHTS) % 10) % 10 === Number(digits[7]);
251
+ }
252
+ function parseHungarianTaxNumber(value) {
253
+ if (typeof value !== "string") return void 0;
254
+ const digits = stripSeparators(value);
255
+ if (!isDigits(digits, 11)) return void 0;
256
+ const taxpayerId = digits.slice(0, 8);
257
+ const vatCode = digits.slice(8, 9);
258
+ const countyCode = digits.slice(9, 11);
259
+ if (!isValidHungarianTaxpayerId(taxpayerId)) return void 0;
260
+ if (!isVatCode(vatCode) || !COUNTY_CODE_SET.has(countyCode)) return void 0;
261
+ return {
262
+ taxpayerId,
263
+ vatCode,
264
+ countyCode,
265
+ formatted: `${taxpayerId}-${vatCode}-${countyCode}`
266
+ };
267
+ }
268
+ function isValidHungarianTaxNumber(value) {
269
+ return parseHungarianTaxNumber(value) !== void 0;
270
+ }
271
+ function isValidHungarianGroupTaxNumber(value) {
272
+ return parseHungarianTaxNumber(value)?.vatCode === "5";
273
+ }
274
+ function isHungarianVatGroupMemberTaxNumber(value) {
275
+ return parseHungarianTaxNumber(value)?.vatCode === "4";
276
+ }
277
+ //#endregion
278
+ export { EU_VAT_NUMBER_PATTERNS, HUNGARIAN_TAX_COUNTY_CODES, formatHungarianBankAccount, isHungarianVatGroupMemberTaxNumber, isValidAgentKey, isValidEmail, isValidEuVatNumber, isValidHungarianBankAccount, isValidHungarianGroupTaxNumber, isValidHungarianIban, isValidHungarianTaxNumber, isValidHungarianTaxpayerId, isValidHungarianZipCode, normalizeEmail, normalizeEuVatNumber, parseHungarianAddress, parseHungarianBankAccount, parseHungarianTaxNumber };
package/llms.txt ADDED
@@ -0,0 +1,16 @@
1
+ # kassza
2
+
3
+ > Zero-dependency TypeScript client for the Számlázz.hu Számla Agent API (Hungarian invoicing): invoices, proformas, receipts, reversals, payments, PDFs, NAV taxpayer lookup, IPN webhooks. Unofficial.
4
+
5
+ ## Docs for coding agents
6
+
7
+ - [Start here](agents/README.md): overview, the five rules, vocabulary
8
+ - [Pitfalls](agents/pitfalls.md): hard rules and error categories
9
+ - [API reference](agents/api.md): every method, input and output
10
+ - [Recipes](agents/recipes.md): webhooks, idempotency, receipts, IPN, storage, serverless, tests
11
+ - [Claude Code skill](agents/skills/kassza/SKILL.md)
12
+
13
+ ## Human docs
14
+
15
+ - [README](README.md): short Hungarian quick start
16
+ - [Official Számlázz.hu Agent docs](https://docs.szamlazz.hu/hu/agent/)