dgii-ts 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 (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +192 -0
  3. package/README.md +196 -0
  4. package/dist/bulk.cjs +11 -0
  5. package/dist/bulk.d.cts +30 -0
  6. package/dist/bulk.d.ts +30 -0
  7. package/dist/bulk.js +11 -0
  8. package/dist/chunk-2ST6QKHA.cjs +1 -0
  9. package/dist/chunk-4OYLCOUL.cjs +201 -0
  10. package/dist/chunk-4TKAQ4WV.js +198 -0
  11. package/dist/chunk-53A7IG4Y.js +23 -0
  12. package/dist/chunk-6CIP7YH6.cjs +198 -0
  13. package/dist/chunk-7RJWFPCZ.cjs +44 -0
  14. package/dist/chunk-D26S6EXD.js +12 -0
  15. package/dist/chunk-DMGDJEUY.js +44 -0
  16. package/dist/chunk-ERBXSS64.js +0 -0
  17. package/dist/chunk-EZKSB553.js +0 -0
  18. package/dist/chunk-FZ6QDTC4.cjs +98 -0
  19. package/dist/chunk-HGE4QZ3H.js +281 -0
  20. package/dist/chunk-HR4DCHH7.js +201 -0
  21. package/dist/chunk-KIS7MVIW.js +98 -0
  22. package/dist/chunk-QH4BK5X3.cjs +12 -0
  23. package/dist/chunk-QOSGH7F5.cjs +1 -0
  24. package/dist/chunk-SUSDEKID.cjs +784 -0
  25. package/dist/chunk-TZC6RSWL.js +784 -0
  26. package/dist/chunk-VJ4ZQQIQ.cjs +281 -0
  27. package/dist/chunk-WY6V4Y7S.cjs +23 -0
  28. package/dist/client.cjs +17 -0
  29. package/dist/client.d.cts +95 -0
  30. package/dist/client.d.ts +95 -0
  31. package/dist/client.js +17 -0
  32. package/dist/errors.cjs +14 -0
  33. package/dist/errors.d.cts +40 -0
  34. package/dist/errors.d.ts +40 -0
  35. package/dist/errors.js +14 -0
  36. package/dist/index-CS-7YY2y.d.cts +58 -0
  37. package/dist/index-CS-7YY2y.d.ts +58 -0
  38. package/dist/index.cjs +66 -0
  39. package/dist/index.d.cts +7 -0
  40. package/dist/index.d.ts +7 -0
  41. package/dist/index.js +66 -0
  42. package/dist/scraping.cjs +16 -0
  43. package/dist/scraping.d.cts +47 -0
  44. package/dist/scraping.d.ts +47 -0
  45. package/dist/scraping.js +16 -0
  46. package/dist/soap.cjs +23 -0
  47. package/dist/soap.d.cts +38 -0
  48. package/dist/soap.d.ts +38 -0
  49. package/dist/soap.js +23 -0
  50. package/dist/validators.cjs +17 -0
  51. package/dist/validators.d.cts +55 -0
  52. package/dist/validators.d.ts +55 -0
  53. package/dist/validators.js +17 -0
  54. package/package.json +109 -0
@@ -0,0 +1,281 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+ var _chunkWY6V4Y7Scjs = require('./chunk-WY6V4Y7S.cjs');
4
+
5
+
6
+
7
+ var _chunkQH4BK5X3cjs = require('./chunk-QH4BK5X3.cjs');
8
+
9
+
10
+
11
+ var _chunk7RJWFPCZcjs = require('./chunk-7RJWFPCZ.cjs');
12
+
13
+ // src/scraping/endpoints.ts
14
+ var DGII_RNC_URL = "https://dgii.gov.do/app/WebApps/ConsultasWeb2/ConsultasWeb/consultas/rnc.aspx";
15
+ var DGII_NCF_URL = "https://dgii.gov.do/app/WebApps/ConsultasWeb2/ConsultasWeb/consultas/ncf.aspx";
16
+ var FORM_FIELDS = /* @__PURE__ */ Object.freeze({
17
+ viewState: "__VIEWSTATE",
18
+ viewStateGenerator: "__VIEWSTATEGENERATOR",
19
+ eventValidation: "__EVENTVALIDATION",
20
+ rncInput: "ctl00$cphMain$txtRNCCedula",
21
+ rncSubmit: "ctl00$cphMain$btnBuscarPorRNC",
22
+ ncfRncInput: "ctl00$cphMain$txtRNC",
23
+ ncfInput: "ctl00$cphMain$txtNCF",
24
+ ncfSubmit: "ctl00$cphMain$btnConsultar"
25
+ });
26
+
27
+ // src/scraping/html-parser.ts
28
+ function extractViewStateTokens(html) {
29
+ const viewState = extractInputValue(html, "__VIEWSTATE");
30
+ const viewStateGenerator = extractInputValue(html, "__VIEWSTATEGENERATOR");
31
+ const eventValidation = extractInputValue(html, "__EVENTVALIDATION");
32
+ if (!viewState || !eventValidation) {
33
+ throw new (0, _chunk7RJWFPCZcjs.DgiiServiceError)(
34
+ "No se encontraron tokens ViewState en la p\xE1gina de la DGII"
35
+ );
36
+ }
37
+ return { viewState, viewStateGenerator, eventValidation };
38
+ }
39
+ function parseContribuyenteHtml(html) {
40
+ const infoStart = html.indexOf("cphMain_lblInformacion");
41
+ if (infoStart !== -1) {
42
+ const infoEnd = html.indexOf("</span>", infoStart);
43
+ if (infoEnd !== -1) {
44
+ const infoBlock = html.slice(infoStart, infoEnd);
45
+ if (infoBlock.includes("no se encuentra")) {
46
+ throw new (0, _chunk7RJWFPCZcjs.DgiiNotFoundError)(
47
+ "RNC o c\xE9dula no encontrado en el registro de la DGII"
48
+ );
49
+ }
50
+ }
51
+ }
52
+ const tableId = "cphMain_dvDatosContribuyentes";
53
+ const tableStart = html.indexOf(tableId);
54
+ if (tableStart === -1) {
55
+ throw new (0, _chunk7RJWFPCZcjs.DgiiServiceError)(
56
+ "Formato de respuesta HTML inesperado: tabla de resultados no encontrada"
57
+ );
58
+ }
59
+ const tableEnd = html.indexOf("</table>", tableStart);
60
+ if (tableEnd === -1) {
61
+ throw new (0, _chunk7RJWFPCZcjs.DgiiServiceError)(
62
+ "Formato de respuesta HTML inesperado: tabla incompleta"
63
+ );
64
+ }
65
+ const tableHtml = html.slice(tableStart, tableEnd);
66
+ const fields = extractTableFields(tableHtml);
67
+ const rnc = _nullishCoalesce(_nullishCoalesce(fields.get("Cedula/RNC"), () => ( fields.get("RNC"))), () => ( ""));
68
+ const nombre = _nullishCoalesce(_nullishCoalesce(fields.get("Nombre/Razon Social"), () => ( fields.get("Nombre / Razon Social"))), () => ( ""));
69
+ const nombreComercial = _nullishCoalesce(fields.get("Nombre Comercial"), () => ( ""));
70
+ const categoria = _nullishCoalesce(fields.get("Categoria"), () => ( ""));
71
+ const rawEstado = _nullishCoalesce(fields.get("Estado"), () => ( ""));
72
+ const actividadEconomica = _nullishCoalesce(fields.get("Actividad Economica"), () => ( void 0));
73
+ const regimenDePagos = _nullishCoalesce(fields.get("Regimen de pagos"), () => ( void 0));
74
+ const administracionLocal = _nullishCoalesce(fields.get("Administracion Local"), () => ( void 0));
75
+ return {
76
+ rnc: _chunkQH4BK5X3cjs.stripNonDigits.call(void 0, rnc),
77
+ nombre: _chunkQH4BK5X3cjs.collapseSpaces.call(void 0, nombre),
78
+ nombreComercial: _chunkQH4BK5X3cjs.collapseSpaces.call(void 0, nombreComercial),
79
+ estado: rawEstado.toUpperCase().includes("ACTIVO") ? "ACTIVO" : "INACTIVO",
80
+ categoria: _chunkQH4BK5X3cjs.collapseSpaces.call(void 0, categoria),
81
+ actividadEconomica: actividadEconomica ? _chunkQH4BK5X3cjs.collapseSpaces.call(void 0, actividadEconomica) : void 0,
82
+ regimenDePagos: regimenDePagos ? _chunkQH4BK5X3cjs.collapseSpaces.call(void 0, regimenDePagos) : void 0,
83
+ administracionLocal: administracionLocal ? _chunkQH4BK5X3cjs.collapseSpaces.call(void 0, administracionLocal) : void 0
84
+ };
85
+ }
86
+ function parseNcfHtml(html) {
87
+ const infoStart = html.indexOf("cphMain_lblInformacion");
88
+ if (infoStart !== -1) {
89
+ const infoEnd = html.indexOf("</span>", infoStart);
90
+ if (infoEnd !== -1) {
91
+ const infoBlock = html.slice(infoStart, infoEnd);
92
+ if (infoBlock.includes("no es v") || infoBlock.includes("no se encuentra") || infoBlock.includes("no existe")) {
93
+ return { valid: false, rnc: "", ncf: "", nombreComercial: void 0 };
94
+ }
95
+ }
96
+ }
97
+ const tableId = "cphMain_dvDatosComprobante";
98
+ const altTableId = "cphMain_dvDatosContribuyentes";
99
+ let tableStart = html.indexOf(tableId);
100
+ if (tableStart === -1) {
101
+ tableStart = html.indexOf(altTableId);
102
+ }
103
+ if (tableStart === -1) {
104
+ return { valid: false, rnc: "", ncf: "", nombreComercial: void 0 };
105
+ }
106
+ const tableEnd = html.indexOf("</table>", tableStart);
107
+ if (tableEnd === -1) {
108
+ return { valid: false, rnc: "", ncf: "", nombreComercial: void 0 };
109
+ }
110
+ const tableHtml = html.slice(tableStart, tableEnd);
111
+ const fields = extractTableFields(tableHtml);
112
+ const rnc = _nullishCoalesce(_nullishCoalesce(fields.get("RNC"), () => ( fields.get("Cedula/RNC"))), () => ( ""));
113
+ const ncf = _nullishCoalesce(_nullishCoalesce(fields.get("NCF"), () => ( fields.get("No. Comprobante Fiscal"))), () => ( ""));
114
+ const nombreComercial = _nullishCoalesce(_nullishCoalesce(fields.get("Nombre Comercial"), () => ( fields.get("Nombre / Razon Social"))), () => ( void 0));
115
+ return {
116
+ valid: true,
117
+ rnc: _chunkQH4BK5X3cjs.stripNonDigits.call(void 0, rnc),
118
+ ncf: _chunkQH4BK5X3cjs.collapseSpaces.call(void 0, ncf),
119
+ nombreComercial: nombreComercial ? _chunkQH4BK5X3cjs.collapseSpaces.call(void 0, nombreComercial) : void 0
120
+ };
121
+ }
122
+ function extractInputValue(html, name) {
123
+ let idx = html.indexOf(`id="${name}"`);
124
+ if (idx === -1) {
125
+ idx = html.indexOf(`name="${name}"`);
126
+ }
127
+ if (idx === -1) return "";
128
+ const tagStart = html.lastIndexOf("<", idx);
129
+ if (tagStart === -1) return "";
130
+ const tagEnd = html.indexOf(">", idx);
131
+ if (tagEnd === -1) return "";
132
+ const tag = html.slice(tagStart, tagEnd + 1);
133
+ const valueMatch = tag.match(/value="([^"]*)"/);
134
+ return _nullishCoalesce(_optionalChain([valueMatch, 'optionalAccess', _2 => _2[1]]), () => ( ""));
135
+ }
136
+ var BOLD_STYLE_PATTERN = /<td[^>]*style="font-weight:bold;"[^>]*>(.*?)<\/td>\s*<td[^>]*>(.*?)<\/td>/gi;
137
+ var BOLD_TAG_PATTERN = /<td><b>(.*?)<\/b><\/td>\s*<td>(.*?)<\/td>/gi;
138
+ function extractTableFields(tableHtml) {
139
+ const fields = /* @__PURE__ */ new Map();
140
+ for (const match of tableHtml.matchAll(BOLD_STYLE_PATTERN)) {
141
+ const label = normalizeLabel(_nullishCoalesce(match[1], () => ( "")));
142
+ const value = stripHtmlTags(_nullishCoalesce(match[2], () => ( "")));
143
+ if (label) fields.set(label, value);
144
+ }
145
+ if (fields.size === 0) {
146
+ for (const match of tableHtml.matchAll(BOLD_TAG_PATTERN)) {
147
+ const label = normalizeLabel(_nullishCoalesce(match[1], () => ( "")));
148
+ const value = stripHtmlTags(_nullishCoalesce(match[2], () => ( "")));
149
+ if (label) fields.set(label, value);
150
+ }
151
+ }
152
+ return fields;
153
+ }
154
+ var ACCENT_PATTERN = /[\u0300-\u036f]/g;
155
+ function normalizeLabel(raw) {
156
+ return decodeHtmlEntities(stripHtmlTags(raw)).replace(/\s+/g, " ").trim().normalize("NFD").replace(ACCENT_PATTERN, "");
157
+ }
158
+ function stripHtmlTags(str) {
159
+ return str.replace(/<[^>]*>/g, "").trim();
160
+ }
161
+ function decodeHtmlEntities(str) {
162
+ return str.replace(
163
+ /&#(\d+);/g,
164
+ (_, code) => String.fromCharCode(parseInt(code, 10))
165
+ ).replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&nbsp;/g, " ");
166
+ }
167
+
168
+ // src/scraping/client.ts
169
+ var DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
170
+ var ScrapingClient = class {
171
+ constructor(options) {
172
+ this._rncUrl = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _3 => _3.baseRncUrl]), () => ( DGII_RNC_URL));
173
+ this._ncfUrl = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _4 => _4.baseNcfUrl]), () => ( DGII_NCF_URL));
174
+ this._timeout = Math.min(
175
+ Math.max(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _5 => _5.timeout]), () => ( 15e3)), 1e3),
176
+ 12e4
177
+ );
178
+ }
179
+ /**
180
+ * Consulta datos de un contribuyente por RNC o cédula.
181
+ */
182
+ async getContribuyente(rnc) {
183
+ if (typeof rnc !== "string" || rnc.trim() === "") {
184
+ throw new (0, _chunk7RJWFPCZcjs.DgiiServiceError)("El par\xE1metro rnc es requerido");
185
+ }
186
+ const tokens = await this._fetchTokens(this._rncUrl);
187
+ const body = buildFormBody([
188
+ ["__EVENTTARGET", ""],
189
+ ["__EVENTARGUMENT", ""],
190
+ [FORM_FIELDS.viewState, tokens.viewState],
191
+ [FORM_FIELDS.viewStateGenerator, tokens.viewStateGenerator],
192
+ [FORM_FIELDS.eventValidation, tokens.eventValidation],
193
+ [FORM_FIELDS.rncInput, rnc.trim()],
194
+ ["ctl00$cphMain$txtRazonSocial", ""],
195
+ ["ctl00$cphMain$hidActiveTab", "rnc"],
196
+ [FORM_FIELDS.rncSubmit, "BUSCAR"]
197
+ ]);
198
+ const html = await this._post(this._rncUrl, body);
199
+ return parseContribuyenteHtml(html);
200
+ }
201
+ /**
202
+ * Valida un comprobante fiscal (NCF) contra la DGII.
203
+ */
204
+ async getNCF(rnc, ncf) {
205
+ if (typeof rnc !== "string" || rnc.trim() === "") {
206
+ throw new (0, _chunk7RJWFPCZcjs.DgiiServiceError)("El par\xE1metro rnc es requerido");
207
+ }
208
+ if (typeof ncf !== "string" || ncf.trim() === "") {
209
+ throw new (0, _chunk7RJWFPCZcjs.DgiiServiceError)("El par\xE1metro ncf es requerido");
210
+ }
211
+ const tokens = await this._fetchTokens(this._ncfUrl);
212
+ const body = buildFormBody([
213
+ [FORM_FIELDS.viewState, tokens.viewState],
214
+ [FORM_FIELDS.viewStateGenerator, tokens.viewStateGenerator],
215
+ [FORM_FIELDS.eventValidation, tokens.eventValidation],
216
+ [FORM_FIELDS.ncfRncInput, rnc.trim()],
217
+ [FORM_FIELDS.ncfInput, ncf.trim()],
218
+ [FORM_FIELDS.ncfSubmit, "Buscar"]
219
+ ]);
220
+ const html = await this._post(this._ncfUrl, body);
221
+ return parseNcfHtml(html);
222
+ }
223
+ async _fetchTokens(url) {
224
+ let response;
225
+ try {
226
+ response = await fetch(url, {
227
+ headers: { "User-Agent": DEFAULT_USER_AGENT },
228
+ signal: AbortSignal.timeout(this._timeout)
229
+ });
230
+ } catch (error) {
231
+ throw this._wrapFetchError(error);
232
+ }
233
+ if (!response.ok) {
234
+ throw new (0, _chunk7RJWFPCZcjs.DgiiServiceError)(
235
+ `La DGII respondi\xF3 con HTTP ${response.status} al obtener la p\xE1gina`,
236
+ { statusCode: response.status }
237
+ );
238
+ }
239
+ const html = await response.text();
240
+ return extractViewStateTokens(html);
241
+ }
242
+ async _post(url, body) {
243
+ let response;
244
+ try {
245
+ response = await fetch(url, {
246
+ method: "POST",
247
+ headers: {
248
+ "User-Agent": DEFAULT_USER_AGENT,
249
+ "Content-Type": "application/x-www-form-urlencoded",
250
+ "Referer": url
251
+ },
252
+ body,
253
+ signal: AbortSignal.timeout(this._timeout)
254
+ });
255
+ } catch (error) {
256
+ throw this._wrapFetchError(error);
257
+ }
258
+ if (!response.ok) {
259
+ throw new (0, _chunk7RJWFPCZcjs.DgiiServiceError)(
260
+ `La DGII respondi\xF3 con HTTP ${response.status}`,
261
+ { statusCode: response.status }
262
+ );
263
+ }
264
+ return response.text();
265
+ }
266
+ _wrapFetchError(error) {
267
+ return _chunkWY6V4Y7Scjs.wrapFetchError.call(void 0, error, this._timeout);
268
+ }
269
+ };
270
+ function buildFormBody(fields) {
271
+ return fields.map(
272
+ ([name, value]) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`.replace(/%24/g, "$")
273
+ ).join("&");
274
+ }
275
+
276
+
277
+
278
+
279
+
280
+
281
+ exports.DGII_RNC_URL = DGII_RNC_URL; exports.DGII_NCF_URL = DGII_NCF_URL; exports.FORM_FIELDS = FORM_FIELDS; exports.ScrapingClient = ScrapingClient;
@@ -0,0 +1,23 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+ var _chunk7RJWFPCZcjs = require('./chunk-7RJWFPCZ.cjs');
4
+
5
+ // src/utils/fetch-error.ts
6
+ function wrapFetchError(error, timeoutMs) {
7
+ const name = error.name;
8
+ const causeName = _optionalChain([error, 'access', _ => _.cause, 'optionalAccess', _2 => _2.name]);
9
+ if (name === "TimeoutError" || causeName === "TimeoutError" || name === "AbortError" || causeName === "AbortError") {
10
+ return new (0, _chunk7RJWFPCZcjs.DgiiConnectionError)(
11
+ `Timeout de ${timeoutMs}ms excedido al conectar con la DGII`,
12
+ { cause: error }
13
+ );
14
+ }
15
+ return new (0, _chunk7RJWFPCZcjs.DgiiConnectionError)(
16
+ "Error de conexi\xF3n con el servicio de la DGII",
17
+ { cause: error }
18
+ );
19
+ }
20
+
21
+
22
+
23
+ exports.wrapFetchError = wrapFetchError;
@@ -0,0 +1,17 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+
5
+
6
+ var _chunk6CIP7YH6cjs = require('./chunk-6CIP7YH6.cjs');
7
+ require('./chunk-4OYLCOUL.cjs');
8
+ require('./chunk-VJ4ZQQIQ.cjs');
9
+ require('./chunk-WY6V4Y7S.cjs');
10
+ require('./chunk-QH4BK5X3.cjs');
11
+ require('./chunk-7RJWFPCZ.cjs');
12
+
13
+
14
+
15
+
16
+
17
+ exports.ConsecutiveBreaker = _chunk6CIP7YH6cjs.ConsecutiveBreaker; exports.DgiiClient = _chunk6CIP7YH6cjs.DgiiClient; exports.isRetryableError = _chunk6CIP7YH6cjs.isRetryableError; exports.withRetry = _chunk6CIP7YH6cjs.withRetry;
@@ -0,0 +1,95 @@
1
+ import { C as Contribuyente, N as NcfQueryResult } from './index-CS-7YY2y.cjs';
2
+
3
+ interface RetryOptions {
4
+ /** Número máximo de reintentos (por defecto: 2) */
5
+ maxRetries: number;
6
+ /** Delay base en milisegundos (por defecto: 500) */
7
+ baseDelayMs: number;
8
+ /** Delay máximo en milisegundos (por defecto: 10000) */
9
+ maxDelayMs: number;
10
+ }
11
+ /**
12
+ * Determina si un error es reintentable.
13
+ *
14
+ * - DgiiConnectionError: siempre reintentable
15
+ * - DgiiServiceError con statusCode >= 500: reintentable
16
+ * - DgiiNotFoundError: nunca reintentable (resultado de negocio)
17
+ * - DgiiServiceError con 403/4xx: nunca reintentable
18
+ */
19
+ declare function isRetryableError(error: unknown): boolean;
20
+ /**
21
+ * Ejecuta una función con reintentos y backoff exponencial
22
+ * con jitter completo.
23
+ *
24
+ * Fórmula: delay = random(0, min(maxDelay, baseDelay * 2^attempt))
25
+ */
26
+ declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
27
+
28
+ interface CircuitBreakerOptions {
29
+ /** Fallos consecutivos para abrir el circuito (por defecto: 5) */
30
+ failureThreshold: number;
31
+ /** Tiempo en ms antes de probar de nuevo (por defecto: 60000) */
32
+ recoveryTimeoutMs: number;
33
+ /** Éxitos consecutivos en HALF_OPEN para cerrar (por defecto: 2) */
34
+ successThreshold: number;
35
+ }
36
+ type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
37
+ /**
38
+ * Circuit breaker basado en fallos consecutivos.
39
+ *
40
+ * - CLOSED: operación normal, se cuentan fallos consecutivos
41
+ * - OPEN: rechaza inmediatamente, espera recoveryTimeoutMs
42
+ * - HALF_OPEN: permite llamadas de prueba, cierra después
43
+ * de successThreshold éxitos consecutivos
44
+ */
45
+ declare class ConsecutiveBreaker {
46
+ private _state;
47
+ private _failures;
48
+ private _successes;
49
+ private _lastFailureTime;
50
+ private readonly _options;
51
+ constructor(options?: Partial<CircuitBreakerOptions>);
52
+ get state(): CircuitState;
53
+ execute<T>(fn: () => Promise<T>): Promise<T>;
54
+ reset(): void;
55
+ private _onSuccess;
56
+ private _onFailure;
57
+ }
58
+
59
+ interface ClientOptions {
60
+ /** Tiempo de espera en milisegundos (por defecto: 15000) */
61
+ timeout?: number;
62
+ /** Habilitar fallback a SOAP (por defecto: true) */
63
+ soapFallback?: boolean;
64
+ /** Opciones de reintentos */
65
+ retry?: Partial<RetryOptions>;
66
+ /** Opciones del circuit breaker */
67
+ circuitBreaker?: Partial<CircuitBreakerOptions>;
68
+ }
69
+
70
+ /**
71
+ * Cliente resiliente para consultas a la DGII.
72
+ *
73
+ * Usa web scraping como estrategia principal y SOAP como
74
+ * fallback (con circuit breaker y reintentos automáticos).
75
+ */
76
+ declare class DgiiClient {
77
+ private readonly _scraping;
78
+ private readonly _soap;
79
+ private readonly _scrapingBreaker;
80
+ private readonly _soapBreaker;
81
+ private readonly _retryOptions;
82
+ private readonly _soapFallback;
83
+ constructor(options?: ClientOptions);
84
+ /**
85
+ * Consulta datos de un contribuyente por RNC o cédula.
86
+ */
87
+ getContribuyente(rnc: string): Promise<Contribuyente>;
88
+ /**
89
+ * Valida un comprobante fiscal (NCF) contra la DGII.
90
+ */
91
+ getNCF(rnc: string, ncf: string): Promise<NcfQueryResult>;
92
+ private _executeWithFallback;
93
+ }
94
+
95
+ export { type CircuitBreakerOptions, type ClientOptions, ConsecutiveBreaker, DgiiClient, type RetryOptions, isRetryableError, withRetry };
@@ -0,0 +1,95 @@
1
+ import { C as Contribuyente, N as NcfQueryResult } from './index-CS-7YY2y.js';
2
+
3
+ interface RetryOptions {
4
+ /** Número máximo de reintentos (por defecto: 2) */
5
+ maxRetries: number;
6
+ /** Delay base en milisegundos (por defecto: 500) */
7
+ baseDelayMs: number;
8
+ /** Delay máximo en milisegundos (por defecto: 10000) */
9
+ maxDelayMs: number;
10
+ }
11
+ /**
12
+ * Determina si un error es reintentable.
13
+ *
14
+ * - DgiiConnectionError: siempre reintentable
15
+ * - DgiiServiceError con statusCode >= 500: reintentable
16
+ * - DgiiNotFoundError: nunca reintentable (resultado de negocio)
17
+ * - DgiiServiceError con 403/4xx: nunca reintentable
18
+ */
19
+ declare function isRetryableError(error: unknown): boolean;
20
+ /**
21
+ * Ejecuta una función con reintentos y backoff exponencial
22
+ * con jitter completo.
23
+ *
24
+ * Fórmula: delay = random(0, min(maxDelay, baseDelay * 2^attempt))
25
+ */
26
+ declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
27
+
28
+ interface CircuitBreakerOptions {
29
+ /** Fallos consecutivos para abrir el circuito (por defecto: 5) */
30
+ failureThreshold: number;
31
+ /** Tiempo en ms antes de probar de nuevo (por defecto: 60000) */
32
+ recoveryTimeoutMs: number;
33
+ /** Éxitos consecutivos en HALF_OPEN para cerrar (por defecto: 2) */
34
+ successThreshold: number;
35
+ }
36
+ type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
37
+ /**
38
+ * Circuit breaker basado en fallos consecutivos.
39
+ *
40
+ * - CLOSED: operación normal, se cuentan fallos consecutivos
41
+ * - OPEN: rechaza inmediatamente, espera recoveryTimeoutMs
42
+ * - HALF_OPEN: permite llamadas de prueba, cierra después
43
+ * de successThreshold éxitos consecutivos
44
+ */
45
+ declare class ConsecutiveBreaker {
46
+ private _state;
47
+ private _failures;
48
+ private _successes;
49
+ private _lastFailureTime;
50
+ private readonly _options;
51
+ constructor(options?: Partial<CircuitBreakerOptions>);
52
+ get state(): CircuitState;
53
+ execute<T>(fn: () => Promise<T>): Promise<T>;
54
+ reset(): void;
55
+ private _onSuccess;
56
+ private _onFailure;
57
+ }
58
+
59
+ interface ClientOptions {
60
+ /** Tiempo de espera en milisegundos (por defecto: 15000) */
61
+ timeout?: number;
62
+ /** Habilitar fallback a SOAP (por defecto: true) */
63
+ soapFallback?: boolean;
64
+ /** Opciones de reintentos */
65
+ retry?: Partial<RetryOptions>;
66
+ /** Opciones del circuit breaker */
67
+ circuitBreaker?: Partial<CircuitBreakerOptions>;
68
+ }
69
+
70
+ /**
71
+ * Cliente resiliente para consultas a la DGII.
72
+ *
73
+ * Usa web scraping como estrategia principal y SOAP como
74
+ * fallback (con circuit breaker y reintentos automáticos).
75
+ */
76
+ declare class DgiiClient {
77
+ private readonly _scraping;
78
+ private readonly _soap;
79
+ private readonly _scrapingBreaker;
80
+ private readonly _soapBreaker;
81
+ private readonly _retryOptions;
82
+ private readonly _soapFallback;
83
+ constructor(options?: ClientOptions);
84
+ /**
85
+ * Consulta datos de un contribuyente por RNC o cédula.
86
+ */
87
+ getContribuyente(rnc: string): Promise<Contribuyente>;
88
+ /**
89
+ * Valida un comprobante fiscal (NCF) contra la DGII.
90
+ */
91
+ getNCF(rnc: string, ncf: string): Promise<NcfQueryResult>;
92
+ private _executeWithFallback;
93
+ }
94
+
95
+ export { type CircuitBreakerOptions, type ClientOptions, ConsecutiveBreaker, DgiiClient, type RetryOptions, isRetryableError, withRetry };
package/dist/client.js ADDED
@@ -0,0 +1,17 @@
1
+ import {
2
+ ConsecutiveBreaker,
3
+ DgiiClient,
4
+ isRetryableError,
5
+ withRetry
6
+ } from "./chunk-4TKAQ4WV.js";
7
+ import "./chunk-HR4DCHH7.js";
8
+ import "./chunk-HGE4QZ3H.js";
9
+ import "./chunk-53A7IG4Y.js";
10
+ import "./chunk-D26S6EXD.js";
11
+ import "./chunk-DMGDJEUY.js";
12
+ export {
13
+ ConsecutiveBreaker,
14
+ DgiiClient,
15
+ isRetryableError,
16
+ withRetry
17
+ };
@@ -0,0 +1,14 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+
5
+
6
+
7
+ var _chunk7RJWFPCZcjs = require('./chunk-7RJWFPCZ.cjs');
8
+
9
+
10
+
11
+
12
+
13
+
14
+ exports.AllStrategiesFailedError = _chunk7RJWFPCZcjs.AllStrategiesFailedError; exports.DgiiConnectionError = _chunk7RJWFPCZcjs.DgiiConnectionError; exports.DgiiError = _chunk7RJWFPCZcjs.DgiiError; exports.DgiiNotFoundError = _chunk7RJWFPCZcjs.DgiiNotFoundError; exports.DgiiServiceError = _chunk7RJWFPCZcjs.DgiiServiceError;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Error base para todas las operaciones DGII.
3
+ */
4
+ declare class DgiiError extends Error {
5
+ readonly code: string;
6
+ constructor(message: string, code: string, options?: ErrorOptions);
7
+ }
8
+ /**
9
+ * Error de conexión: timeout, DNS, red caída, TLS.
10
+ */
11
+ declare class DgiiConnectionError extends DgiiError {
12
+ constructor(message: string, options?: ErrorOptions);
13
+ }
14
+ /**
15
+ * RNC/cédula o NCF no encontrado en el registro de la DGII.
16
+ * Esto NO es un error de infraestructura -- es un resultado
17
+ * válido de negocio.
18
+ */
19
+ declare class DgiiNotFoundError extends DgiiError {
20
+ constructor(message: string);
21
+ }
22
+ /**
23
+ * Error del servicio: SOAP fault, HTTP 403/5xx, respuesta
24
+ * inesperada.
25
+ */
26
+ declare class DgiiServiceError extends DgiiError {
27
+ readonly statusCode?: number;
28
+ constructor(message: string, options?: ErrorOptions & {
29
+ statusCode?: number;
30
+ });
31
+ }
32
+ /**
33
+ * Todas las estrategias de consulta fallaron.
34
+ */
35
+ declare class AllStrategiesFailedError extends DgiiError {
36
+ readonly errors: ReadonlyArray<Error>;
37
+ constructor(message: string, errors: ReadonlyArray<Error>);
38
+ }
39
+
40
+ export { AllStrategiesFailedError, DgiiConnectionError, DgiiError, DgiiNotFoundError, DgiiServiceError };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Error base para todas las operaciones DGII.
3
+ */
4
+ declare class DgiiError extends Error {
5
+ readonly code: string;
6
+ constructor(message: string, code: string, options?: ErrorOptions);
7
+ }
8
+ /**
9
+ * Error de conexión: timeout, DNS, red caída, TLS.
10
+ */
11
+ declare class DgiiConnectionError extends DgiiError {
12
+ constructor(message: string, options?: ErrorOptions);
13
+ }
14
+ /**
15
+ * RNC/cédula o NCF no encontrado en el registro de la DGII.
16
+ * Esto NO es un error de infraestructura -- es un resultado
17
+ * válido de negocio.
18
+ */
19
+ declare class DgiiNotFoundError extends DgiiError {
20
+ constructor(message: string);
21
+ }
22
+ /**
23
+ * Error del servicio: SOAP fault, HTTP 403/5xx, respuesta
24
+ * inesperada.
25
+ */
26
+ declare class DgiiServiceError extends DgiiError {
27
+ readonly statusCode?: number;
28
+ constructor(message: string, options?: ErrorOptions & {
29
+ statusCode?: number;
30
+ });
31
+ }
32
+ /**
33
+ * Todas las estrategias de consulta fallaron.
34
+ */
35
+ declare class AllStrategiesFailedError extends DgiiError {
36
+ readonly errors: ReadonlyArray<Error>;
37
+ constructor(message: string, errors: ReadonlyArray<Error>);
38
+ }
39
+
40
+ export { AllStrategiesFailedError, DgiiConnectionError, DgiiError, DgiiNotFoundError, DgiiServiceError };
package/dist/errors.js ADDED
@@ -0,0 +1,14 @@
1
+ import {
2
+ AllStrategiesFailedError,
3
+ DgiiConnectionError,
4
+ DgiiError,
5
+ DgiiNotFoundError,
6
+ DgiiServiceError
7
+ } from "./chunk-DMGDJEUY.js";
8
+ export {
9
+ AllStrategiesFailedError,
10
+ DgiiConnectionError,
11
+ DgiiError,
12
+ DgiiNotFoundError,
13
+ DgiiServiceError
14
+ };