@taxora/sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 theconcept-technologies
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,436 @@
1
+ <p>
2
+ <img src="https://taxora.io/assets/logo/taxora_logo.svg" alt="Taxora Logo" width="220"/>
3
+ </p>
4
+
5
+ # @taxora/sdk
6
+
7
+ > **Official Node.js/TypeScript SDK for the [Taxora VAT Validation API](https://taxora.io)**
8
+ > Validate EU VAT numbers, generate compliance certificates, and integrate VAT checks into your Node.js services โ€” with clean, modern TypeScript and zero runtime dependencies.
9
+
10
+ [![CI](https://github.com/theconcept-technologies/taxora-sdk-node/actions/workflows/ci.yml/badge.svg)](https://github.com/theconcept-technologies/taxora-sdk-node/actions)
11
+ [![npm](https://img.shields.io/npm/v/@taxora/sdk?color=cb3837&logo=npm&label=npm)](https://www.npmjs.com/package/@taxora/sdk)
12
+ [![Coverage](https://img.shields.io/badge/coverage-97%25-brightgreen)](#-testing)
13
+ [![Node](https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js&logoColor=white)](https://nodejs.org)
14
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
15
+ [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
16
+
17
+ ---
18
+
19
+ ## ๐Ÿš€ Overview
20
+
21
+ The **Taxora SDK** provides a clean, type-safe interface to the [Taxora API](https://taxora.io), supporting:
22
+
23
+ - โœ… Secure **API-Key** and **Bearer Token** authentication with auto-refresh
24
+ - โœ… Single & multiple VAT validation with AI-based company name matching
25
+ - โœ… VAT state history and full-text search endpoints
26
+ - โœ… Certificate generation (PDF) and bulk/list exports (ZIP or PDF)
27
+ - โœ… Strict TypeScript โ€” full type coverage, zero `any`
28
+ - โœ… Native `fetch` (Node 18+) โ€” **zero runtime dependencies**
29
+ - โœ… Dual **ESM + CJS** output for maximum compatibility
30
+
31
+ > ๐Ÿ”’ The SDK itself is free to use, but a **Taxora API subscription** is required.
32
+ > Obtain your `x-api-key` from your [Taxora account developer settings](https://app.taxora.io).
33
+
34
+ ---
35
+
36
+ ## ๐Ÿงฎ Installation
37
+
38
+ ```bash
39
+ npm install @taxora/sdk
40
+ ```
41
+
42
+ **Requirements:** Node.js โ‰ฅ 18 (uses native `fetch` โ€” no polyfill needed)
43
+
44
+ ---
45
+
46
+ ## โš™๏ธ Quick Start
47
+
48
+ ```ts
49
+ import { TaxoraClientFactory, Environment } from '@taxora/sdk';
50
+
51
+ const client = TaxoraClientFactory.create({
52
+ apiKey: 'YOUR_X_API_KEY',
53
+ environment: Environment.SANDBOX, // or Environment.PRODUCTION
54
+ });
55
+
56
+ // 1๏ธโƒฃ Authenticate
57
+ await client.auth.login('user@example.com', 'superSecret');
58
+
59
+ // 2๏ธโƒฃ Validate a VAT number
60
+ const vat = await client.vat.validate('ATU12345678', 'Example GmbH');
61
+ console.log(vat.state); // 'valid' | 'invalid' | 'fraud' | 'unknown'
62
+ console.log(vat.companyName); // Official company name from registry
63
+ console.log(vat.score); // Overall confidence score (0.0 โ€“ 1.0)
64
+
65
+ for (const step of vat.breakdown ?? []) {
66
+ console.log(`${step.stepName} โ†’ ${step.scoreContribution}`);
67
+ }
68
+
69
+ // Optional: typed address input for fallback name scoring
70
+ import { VatValidationAddressInput } from '@taxora/sdk';
71
+
72
+ const addressInput = new VatValidationAddressInput({
73
+ addressLine1: 'RingstraรŸe 1',
74
+ postalCode: '1010',
75
+ city: 'Vienna',
76
+ countryCode: 'AT',
77
+ });
78
+ const vatWithAddress = await client.vat.validate('ATU12345678', 'Example GmbH', 'vies', addressInput);
79
+
80
+ // 3๏ธโƒฃ Access company context
81
+ const company = await client.company.get();
82
+
83
+ // 4๏ธโƒฃ Export certificates
84
+ const exportJob = await client.vat.certificatesBulkExport('2024-01-01', '2024-12-31');
85
+ const zip = await client.vat.downloadBulkExport(exportJob.exportId);
86
+ import { writeFileSync } from 'fs';
87
+ writeFileSync('certificates.zip', zip);
88
+ ```
89
+
90
+ `vat.validate()` returns a `VatResource` with the canonical VAT UID, status, company data, and optional scoring details. The `score` reflects overall confidence (higher is better), while `breakdown` is an array of `ScoreBreakdown` objects describing each validation step, its score contribution, and metadata (e.g. matched addresses or mismatched fields).
91
+
92
+ Need a custom HTTP client (e.g. for logging or retries)? Pass it via the factory:
93
+
94
+ ```ts
95
+ import { TaxoraClientFactory, HttpClientInterface } from '@taxora/sdk';
96
+
97
+ class LoggingHttpClient implements HttpClientInterface {
98
+ async request(method: string, url: string, options?: RequestInit): Promise<Response> {
99
+ console.log(`โ†’ ${method} ${url}`);
100
+ return fetch(url, { ...options, method });
101
+ }
102
+ }
103
+
104
+ const client = TaxoraClientFactory.create({
105
+ apiKey: 'YOUR_X_API_KEY',
106
+ httpClient: new LoggingHttpClient(),
107
+ });
108
+ ```
109
+
110
+ ---
111
+
112
+ ## ๐Ÿงฉ Architecture
113
+
114
+ The SDK follows clean separation of concerns:
115
+
116
+ ```
117
+ TaxoraClientFactory.create()
118
+ โ””โ”€โ”€ TaxoraClient
119
+ โ”œโ”€โ”€ auth โ†’ AuthEndpoint (login, loginWithClientId, refresh)
120
+ โ”œโ”€โ”€ vat โ†’ VatEndpoint (validate, history, search, certificates)
121
+ โ””โ”€โ”€ company โ†’ CompanyEndpoint (company info)
122
+ ```
123
+
124
+ Each endpoint handles:
125
+
126
+ - Request signing with `x-api-key`
127
+ - Bearer token injection and proactive refresh if expired
128
+ - JSON/binary response parsing into typed DTOs
129
+
130
+ `VatEndpoint` and `CompanyEndpoint` use the `AuthRetryHttpClient` wrapper, which transparently handles preemptive token refresh and 401 retry โ€” `AuthEndpoint` always uses the raw HTTP client to avoid circular refresh calls.
131
+
132
+ ---
133
+
134
+ ## ๐Ÿ“ฆ DTOs
135
+
136
+ | Class | Description |
137
+ | --------------------------- | ----------------------------------------------------------------------------------------------- |
138
+ | `VatResource` | Single VAT validation result: state, score, breakdown, company name, address, provider document |
139
+ | `VatCollection` | Iterable collection of `VatResource` objects with optional pagination `self` link |
140
+ | `ScoreBreakdown` | Per-step scoring fragment: step name, score contribution, and metadata context |
141
+ | `CompanyAddress` | Structured company address; assembles `fullAddress` from components or parses JSON strings |
142
+ | `VatValidationAddressInput` | Typed input for address-based fallback scoring (validates lengths, country code format) |
143
+ | `Token` | Access token with type and expiry; 15-second buffer for proactive refresh |
144
+ | `VatCertificateExport` | Bulk export job reference (`exportId` + optional message) |
145
+ | `ProviderDocument` | Attached provider document metadata (type, date, MIME, hash, nested line item) |
146
+
147
+ All DTOs expose `toArray()` for serialization and a static `fromArray()` factory.
148
+
149
+ ```ts
150
+ const dto = await client.vat.validate('ATU12345678');
151
+ console.log(dto.toArray());
152
+ ```
153
+
154
+ ---
155
+
156
+ ## ๐Ÿ”„ Authentication Flow
157
+
158
+ ### 1. Login
159
+
160
+ ```ts
161
+ // Email + password
162
+ await client.auth.login('user@example.com', 'password', 'my-server-01');
163
+ // device_name is optional; omitted value falls back to os.hostname()
164
+
165
+ // Client ID + secret
166
+ await client.auth.loginWithClientId('client_abc123', 'client-secret', 'integration-box');
167
+
168
+ // Explicit identifier enum
169
+ import { LoginIdentifier } from '@taxora/sdk';
170
+ await client.auth.login('client_abc123', 'client-secret', undefined, LoginIdentifier.CLIENT_ID);
171
+ ```
172
+
173
+ โ†’ Stores and returns a `Token` DTO (valid for ~3600 seconds).
174
+
175
+ ### 2. Auto-refresh
176
+
177
+ The SDK automatically refreshes the token in two scenarios:
178
+
179
+ | Trigger | Behaviour |
180
+ | -------------------------------------------- | ----------------------------------------------- |
181
+ | **Preemptive** (token expired + 15 s buffer) | Refreshes before the next request |
182
+ | **Reactive** (API returns `401`) | Refreshes once and retries the original request |
183
+
184
+ You never need to call `refresh()` manually in normal usage.
185
+
186
+ ### 3. Manual refresh
187
+
188
+ ```ts
189
+ await client.auth.refresh();
190
+ ```
191
+
192
+ ### 4. Custom token storage
193
+
194
+ By default, tokens are stored in-memory and lost on process restart. Provide your own storage for persistence:
195
+
196
+ ```ts
197
+ import { TokenStorageInterface, Token } from '@taxora/sdk';
198
+
199
+ class RedisTokenStorage implements TokenStorageInterface {
200
+ get(): Token | null {
201
+ /* read from Redis */ return null;
202
+ }
203
+ set(token: Token): void {
204
+ /* write with TTL = token.expiresAt */
205
+ }
206
+ clear(): void {
207
+ /* delete key */
208
+ }
209
+ }
210
+
211
+ const client = TaxoraClientFactory.create({
212
+ apiKey: 'YOUR_X_API_KEY',
213
+ tokenStorage: new RedisTokenStorage(),
214
+ });
215
+ ```
216
+
217
+ ---
218
+
219
+ ## ๐Ÿงพ VAT Validation
220
+
221
+ ### Single Validation
222
+
223
+ ```ts
224
+ // Basic โ€” state only
225
+ const vat = await client.vat.validate('ATU12345678');
226
+
227
+ // With company name matching
228
+ const vat = await client.vat.validate('ATU12345678', 'Alpha Handels GmbH');
229
+
230
+ // With address input for enhanced score
231
+ const vat = await client.vat.validate('ATU12345678', 'Alpha Handels GmbH', 'vies', {
232
+ address_line_1: 'RingstraรŸe 1',
233
+ postal_code: '1010',
234
+ city: 'Wien',
235
+ country_code: 'AT',
236
+ });
237
+
238
+ console.log(vat.state); // VatState value
239
+ console.log(vat.score); // 0.0 โ€“ 1.0
240
+ console.log(vat.companyAddress?.toString()); // Full address string
241
+ console.log(vat.getBackendLink()); // https://app.taxora.io/vat/history/{uuid}
242
+ ```
243
+
244
+ ### Schema Validation (format check only)
245
+
246
+ ```ts
247
+ const result = await client.vat.validateSchema('ATU12345678');
248
+ ```
249
+
250
+ ### Batch Validation
251
+
252
+ ```ts
253
+ const collection = await client.vat.validateMultiple(
254
+ ['ATU12345678', 'DE123456789'],
255
+ ['Alpha Handels GmbH', 'Beta Technik GmbH'],
256
+ );
257
+
258
+ for (const vat of collection) {
259
+ console.log(vat.vatUid, vat.state);
260
+ }
261
+ console.log(collection.length); // 2
262
+ ```
263
+
264
+ ### VAT State Snapshot
265
+
266
+ ```ts
267
+ const vat = await client.vat.state('ATU12345678');
268
+ ```
269
+
270
+ ### History
271
+
272
+ ```ts
273
+ const history = await client.vat.history(); // all entries
274
+ const history = await client.vat.history('ATU12345678'); // filtered
275
+
276
+ for (const entry of history) {
277
+ console.log(entry.checkedAt, entry.state);
278
+ }
279
+ ```
280
+
281
+ ### Search
282
+
283
+ ```ts
284
+ const results = await client.vat.search('Alpha Handels', 25 /* perPage */);
285
+ ```
286
+
287
+ ---
288
+
289
+ ## ๐Ÿ“œ Certificates
290
+
291
+ ### Single Certificate (PDF)
292
+
293
+ ```ts
294
+ import { Language } from '@taxora/sdk';
295
+ import { writeFileSync } from 'fs';
296
+
297
+ const pdf = await client.vat.certificate('uuid-123', Language.GERMAN);
298
+ writeFileSync('certificate.pdf', pdf);
299
+ ```
300
+
301
+ ### Bulk Export (ZIP)
302
+
303
+ ```ts
304
+ const exportJob = await client.vat.certificatesBulkExport(
305
+ '2024-01-01', // or new Date('2024-01-01')
306
+ '2024-12-31',
307
+ ['AT', 'DE'], // optional: filter by country
308
+ Language.ENGLISH, // optional: language
309
+ );
310
+
311
+ console.log(exportJob.exportId); // poll or use directly
312
+
313
+ const zip = await client.vat.downloadBulkExport(exportJob.exportId);
314
+ writeFileSync('certificates.zip', zip);
315
+ ```
316
+
317
+ ### List Export
318
+
319
+ ```ts
320
+ const exportJob = await client.vat.certificatesListExport(new Date('2024-01-01'), new Date('2024-12-31'));
321
+ ```
322
+
323
+ ---
324
+
325
+ ## ๐ŸŒ Environments
326
+
327
+ | Environment | Base URL |
328
+ | ------------------------ | ------------------------------ |
329
+ | `Environment.SANDBOX` | `https://sandbox.taxora.io/v1` |
330
+ | `Environment.PRODUCTION` | `https://api.taxora.io/v1` |
331
+
332
+ ```ts
333
+ const client = TaxoraClientFactory.create({
334
+ apiKey: 'YOUR_X_API_KEY',
335
+ environment: Environment.PRODUCTION,
336
+ });
337
+ ```
338
+
339
+ Need sandbox sample data? Known VAT UIDs with deterministic responses live in `tests/fixtures/SandboxVatFixtures.ts`.
340
+
341
+ ---
342
+
343
+ ## โš ๏ธ Error Handling
344
+
345
+ ```ts
346
+ import { AuthenticationException, HttpException, ValidationException } from '@taxora/sdk';
347
+
348
+ try {
349
+ await client.vat.validate('ATU12345678');
350
+ } catch (err) {
351
+ if (err instanceof AuthenticationException) {
352
+ // HTTP 401 โ€” credentials invalid or token refresh failed
353
+ console.error('Auth failed:', err.message);
354
+ } else if (err instanceof ValidationException) {
355
+ // HTTP 422 โ€” request payload rejected by the API
356
+ console.error('Validation errors:', err.getErrors()); // Record<string, string[]>
357
+ } else if (err instanceof HttpException) {
358
+ // Any other HTTP error
359
+ console.error(`HTTP ${err.getStatusCode()}:`, err.getResponseBody());
360
+ }
361
+ }
362
+ ```
363
+
364
+ ---
365
+
366
+ ## ๐Ÿ”Œ VatState Helpers
367
+
368
+ ```ts
369
+ import { VatState, getFailedVatStates, describeVatState } from '@taxora/sdk';
370
+
371
+ VatState.VALID; // 'valid'
372
+ VatState.INVALID; // 'invalid'
373
+ VatState.FRAUD; // 'fraud'
374
+ VatState.UNKNOWN; // 'unknown'
375
+
376
+ getFailedVatStates(); // ['invalid', 'fraud']
377
+ describeVatState(VatState.VALID); // 'The VAT number is valid and active.'
378
+ ```
379
+
380
+ ---
381
+
382
+ ## ๐Ÿงช Testing
383
+
384
+ ```bash
385
+ npm test # Run all 129 tests
386
+ npm run test:coverage # Run with v8 coverage report
387
+ npm run test:watch # Watch mode during development
388
+ ```
389
+
390
+ CI runs on **Node 18**, **20**, and **22**, verifying:
391
+
392
+ - 129 Vitest unit tests across 18 test files
393
+ - 97.8 % statement coverage / 87 % branch coverage / 100 % function coverage
394
+ - Strict TypeScript compilation
395
+
396
+ ---
397
+
398
+ ## ๐Ÿ—๏ธ Build
399
+
400
+ ```bash
401
+ npm run build
402
+ # โ†’ dist/index.js (ESM)
403
+ # โ†’ dist/index.cjs (CommonJS)
404
+ # โ†’ dist/index.d.ts (TypeScript declarations)
405
+ ```
406
+
407
+ ---
408
+
409
+ ## โš ๏ธ Deprecations
410
+
411
+ So fresh there aren't even any deprecated features yet. Check back in a few months when we're on v47 and have made some regrettable decisions. ๐ŸŽ‰
412
+
413
+ ---
414
+
415
+ ## ๐Ÿชช License
416
+
417
+ Licensed under the **MIT License** ยฉ 2025 [theconcept technologies](https://www.theconcept-technologies.com).
418
+ The SDK is open-source, but API usage requires a valid **Taxora subscription**.
419
+
420
+ ---
421
+
422
+ ## ๐Ÿค Contributing
423
+
424
+ Contributions and pull requests are welcome!
425
+
426
+ - Follow the existing TypeScript code style (strict mode, no `any`).
427
+ - Run `npm test` before submitting a PR.
428
+ - Ensure new endpoints include DTOs + tests.
429
+
430
+ ---
431
+
432
+ ## ๐Ÿ’ฌ Support
433
+
434
+ Need help or enterprise support?
435
+ ๐Ÿ“ง **[support@taxora.io](mailto:support@taxora.io)**
436
+ ๐ŸŒ [https://taxora.io](https://taxora.io)