@ssddo/ecf-sdk 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.
package/README.md ADDED
@@ -0,0 +1,244 @@
1
+ # @ssddo/ecf-sdk
2
+
3
+ SDK de TypeScript para la API de ECF DGII (comprobantes fiscales electrónicos de República Dominicana).
4
+
5
+ Construido con [openapi-typescript](https://github.com/openapi-ts/openapi-typescript) y [openapi-fetch](https://github.com/openapi-ts/openapi-typescript/tree/main/packages/openapi-fetch) para acceso a la API completamente tipado.
6
+
7
+ ## Instalación
8
+
9
+ ```bash
10
+ npm install @ssddo/ecf-sdk
11
+ ```
12
+
13
+ ## Inicio rápido
14
+
15
+ ```typescript
16
+ import { EcfClient } from '@ssddo/ecf-sdk';
17
+
18
+ const client = new EcfClient({
19
+ apiKey: 'tu-token-jwt',
20
+ environment: 'test', // 'test' | 'cert' | 'prod'
21
+ });
22
+
23
+ // Enviar un ECF y esperar a que termine de procesarse
24
+ const result = await client.sendEcf({
25
+ Encabezado: {
26
+ IdDoc: {
27
+ ENCF: "E310000051630",
28
+ TipoeCF: "FacturaDeCreditoFiscalElectronica",
29
+ TipoPago: "Contado",
30
+ TipoIngresos: "01",
31
+ TablaFormasPago: [
32
+ { FormaPago: "Efectivo", MontoPago: 1015.25 },
33
+ ],
34
+ IndicadorMontoGravado: "ConITBISIncluido",
35
+ FechaVencimientoSecuencia: "2028-12-31T00:00:00",
36
+ },
37
+ Emisor: {
38
+ RNCEmisor: "131460941",
39
+ FechaEmision: "2026-01-10",
40
+ DireccionEmisor: "AVE. ISABEL AGUIAR NO. 269, ZONA INDUSTRIAL DE HERRERA",
41
+ RazonSocialEmisor: "DOCUMENTOS ELECTRONICOS DE 02",
42
+ },
43
+ Totales: {
44
+ ITBIS1: 18,
45
+ MontoGravadoI1: 762.71,
46
+ MontoGravadoTotal: 762.71,
47
+ TotalITBIS1: 137.29,
48
+ TotalITBIS: 137.29,
49
+ MontoNoFacturable: 100.0,
50
+ ImpuestosAdicionales: [
51
+ {
52
+ TipoImpuesto: "002",
53
+ TasaImpuestoAdicional: 2,
54
+ OtrosImpuestosAdicionales: 15.25,
55
+ },
56
+ ],
57
+ MontoImpuestoAdicional: 15.25,
58
+ MontoTotal: 1015.25,
59
+ MontoPeriodo: 1015.25,
60
+ },
61
+ Version: "Version1_0",
62
+ Comprador: {
63
+ RNCComprador: "131880681",
64
+ RazonSocialComprador: "DOCUMENTOS ELECTRONICOS DE 03",
65
+ },
66
+ },
67
+ DetallesItems: [
68
+ {
69
+ MontoItem: 1016.95,
70
+ NombreItem: "Iphone 18 Pro max",
71
+ NumeroLinea: 1,
72
+ CantidadItem: 1,
73
+ UnidadMedida: "Unidad",
74
+ PrecioUnitarioItem: 1016.95,
75
+ IndicadorFacturacion: "ITBIS1_18Percent",
76
+ IndicadorBienoServicio: "Bien",
77
+ TablaImpuestoAdicional: [{ TipoImpuesto: "002" }],
78
+ },
79
+ {
80
+ MontoItem: 100.0,
81
+ NombreItem: "Costo de Envío",
82
+ NumeroLinea: 2,
83
+ CantidadItem: 1,
84
+ UnidadMedida: "Unidad",
85
+ PrecioUnitarioItem: 100.0,
86
+ IndicadorFacturacion: "NoFacturable_18Percent",
87
+ IndicadorBienoServicio: "Servicio",
88
+ },
89
+ ],
90
+ DescuentosORecargos: [
91
+ {
92
+ TipoValor: "$",
93
+ TipoAjuste: "D",
94
+ NumeroLinea: 1,
95
+ MontoDescuentooRecargo: 84.75,
96
+ DescripcionDescuentooRecargo: "Descuento",
97
+ IndicadorFacturacionDescuentooRecargo: "ITBIS1_18Percent",
98
+ },
99
+ ],
100
+ });
101
+
102
+ console.log(result.progress); // 'Finished'
103
+ console.log(result.encf);
104
+ ```
105
+
106
+ ## Configuración de entorno
107
+
108
+ ```typescript
109
+ // Usando un entorno específico
110
+ const client = new EcfClient({
111
+ apiKey: 'tu-token-jwt',
112
+ environment: 'prod',
113
+ });
114
+
115
+ // Usando una URL base personalizada (sobreescribe el entorno)
116
+ const client = new EcfClient({
117
+ apiKey: 'tu-token-jwt',
118
+ baseUrl: 'https://api-personalizada.ejemplo.com',
119
+ });
120
+ ```
121
+
122
+ ### URLs de entorno
123
+
124
+ | Entorno | URL |
125
+ |---------|-----|
126
+ | `test` | `https://api.test.ecfx.ssd.com.do` |
127
+ | `cert` | `https://api.cert.ecfx.ssd.com.do` |
128
+ | `prod` | `https://api.prod.ecfx.ssd.com.do` |
129
+
130
+ ## Opciones de polling
131
+
132
+ El método `sendEcf` hace polling hasta que el ECF sea procesado. Puedes personalizar el comportamiento:
133
+
134
+ ```typescript
135
+ const result = await client.sendEcf(ecf, {
136
+ initialDelay: 1000, // Iniciar polling después de 1s (por defecto)
137
+ maxDelay: 30000, // Delay máximo entre polls (por defecto)
138
+ maxRetries: 60, // Máximo de intentos de poll (por defecto)
139
+ backoffMultiplier: 2, // Multiplicador de backoff exponencial (por defecto)
140
+ timeout: 120000, // Timeout total en ms
141
+ signal: abortController.signal, // AbortSignal para cancelación
142
+ });
143
+ ```
144
+
145
+ ## Arquitectura Backend / Frontend
146
+
147
+ En la mayoría de aplicaciones, el backend maneja la lógica de negocio (validación, almacenamiento, conversión) y envía el ECF. El frontend obtiene un token de solo lectura para consultar el estado directamente:
148
+
149
+ ```typescript
150
+ // Backend: tu endpoint de facturas
151
+ const ecfClient = new EcfClient({
152
+ apiKey: process.env.ECF_BACKEND_TOKEN,
153
+ environment: 'prod',
154
+ });
155
+
156
+ app.post('/api/v1/invoices', async (req, res) => {
157
+ // 1. Validar y guardar tu factura interna
158
+ const invoice = await validateAndSave(req.body);
159
+ // 2. Convertir a formato ECF
160
+ const ecf = convertToEcf(invoice);
161
+ // 3. Enviar a ECF SSD
162
+ const { data } = await ecfClient.raw.POST('/ecf/31', { body: ecf });
163
+ await updateInvoice(invoice.id, { messageId: data.messageId });
164
+ res.json({ id: invoice.id, messageId: data.messageId });
165
+ });
166
+
167
+ // Endpoint separado: generar token de solo lectura para el frontend
168
+ app.get('/api/v1/ecf-token', async (req, res) => {
169
+ const { data } = await ecfClient.createApiKey({ rnc: tenant.rnc });
170
+ res.json({ token: data.token });
171
+ });
172
+ ```
173
+
174
+ El frontend almacena el token de forma segura, lo renueva ante `401 Unauthorized` o expiración, y consulta ECF SSD directamente. Consulta el [README principal](../README.md#arquitectura-backend--frontend) para el diagrama completo y ejemplo con React.
175
+
176
+ > **`sendEcf`** envuelve envío + polling en una sola llamada. Ideal para scripts o backends simples sin frontend.
177
+
178
+ ## Acceso directo al cliente
179
+
180
+ Para acceso directo a todos los endpoints de la API con tipado completo:
181
+
182
+ ```typescript
183
+ // Operaciones de empresa
184
+ const { data } = await client.raw.GET('/company', {
185
+ params: { query: { Page: 1, Limit: 10 } },
186
+ });
187
+
188
+ // Consultar estado de ECF
189
+ const { data } = await client.raw.GET('/ecf/{rnc}/{encf}', {
190
+ params: { path: { rnc: '123456789', encf: 'E320000000001' } },
191
+ });
192
+
193
+ // Operaciones DGII
194
+ const { data } = await client.raw.GET('/dgii/{rnc}/consultaresultado/estado', {
195
+ params: { path: { rnc: '123456789' }, query: { trackId: 'abc123' } },
196
+ });
197
+ ```
198
+
199
+ ## Métodos de conveniencia
200
+
201
+ ```typescript
202
+ // Empresa
203
+ await client.getCompanies({ Page: 1, Limit: 10 });
204
+ await client.getCompanyByRnc('123456789');
205
+ await client.upsertCompany({ /* ... */ });
206
+ await client.deleteCompany('123456789');
207
+
208
+ // Certificados
209
+ await client.getCertificate('123456789');
210
+
211
+ // Consultas ECF
212
+ await client.queryEcf('123456789', 'E320000000001');
213
+ await client.searchEcfs('123456789');
214
+ await client.searchAllEcfs();
215
+ await client.getEcfById('123456789', 'message-uuid');
216
+
217
+ // Aprobación comercial
218
+ await client.aprobacionComercial('123456789', 'E320000000001', { /* ... */ });
219
+
220
+ // Anulación rangos
221
+ await client.anulacionRangos('123456789', { /* ... */ });
222
+ await client.listAnulaciones();
223
+
224
+ // Recepción
225
+ await client.searchEcfReceptionRequests();
226
+ await client.getEcfReceptionRequest('123456789', 'message-uuid');
227
+
228
+ // DGII
229
+ await client.consultaResultado('123456789', { trackId: 'abc' });
230
+ await client.consultaTimbre('123456789', { /* ... */ });
231
+ await client.estatusServicios('123456789');
232
+
233
+ // API Keys
234
+ await client.createApiKey({ rnc: '123456789' });
235
+ ```
236
+
237
+ ## Compatibilidad
238
+
239
+ Este SDK usa la API estándar `fetch` y funciona con:
240
+
241
+ - Node.js 18+
242
+ - Deno
243
+ - Bun
244
+ - Navegadores modernos
package/dist/index.cjs ADDED
@@ -0,0 +1,463 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ EcfClient: () => EcfClient,
34
+ EcfError: () => EcfError,
35
+ EcfFrontendClient: () => EcfFrontendClient,
36
+ PollingMaxRetriesError: () => PollingMaxRetriesError,
37
+ PollingTimeoutError: () => PollingTimeoutError,
38
+ createFrontendClient: () => createFrontendClient,
39
+ pollUntilComplete: () => pollUntilComplete
40
+ });
41
+ module.exports = __toCommonJS(index_exports);
42
+
43
+ // src/client.ts
44
+ var import_openapi_fetch = __toESM(require("openapi-fetch"), 1);
45
+
46
+ // src/polling.ts
47
+ var PollingTimeoutError = class extends Error {
48
+ constructor(message = "Polling timed out") {
49
+ super(message);
50
+ this.name = "PollingTimeoutError";
51
+ }
52
+ };
53
+ var PollingMaxRetriesError = class extends Error {
54
+ constructor(retries) {
55
+ super(`Polling exceeded maximum retries (${retries})`);
56
+ this.name = "PollingMaxRetriesError";
57
+ }
58
+ };
59
+ async function pollUntilComplete(fn, isComplete, options) {
60
+ const {
61
+ initialDelay = 1e3,
62
+ maxDelay = 3e4,
63
+ maxRetries = 60,
64
+ backoffMultiplier = 2,
65
+ timeout,
66
+ signal
67
+ } = options ?? {};
68
+ const startTime = Date.now();
69
+ let delay = initialDelay;
70
+ let retries = 0;
71
+ while (true) {
72
+ if (signal?.aborted) {
73
+ throw new DOMException("Polling was aborted", "AbortError");
74
+ }
75
+ const result = await fn();
76
+ if (isComplete(result)) {
77
+ return result;
78
+ }
79
+ retries++;
80
+ if (retries >= maxRetries) {
81
+ throw new PollingMaxRetriesError(maxRetries);
82
+ }
83
+ if (timeout !== void 0 && Date.now() - startTime >= timeout) {
84
+ throw new PollingTimeoutError();
85
+ }
86
+ await sleep(delay, signal);
87
+ delay = Math.min(delay * backoffMultiplier, maxDelay);
88
+ }
89
+ }
90
+ function sleep(ms, signal) {
91
+ return new Promise((resolve, reject) => {
92
+ if (signal?.aborted) {
93
+ reject(new DOMException("Polling was aborted", "AbortError"));
94
+ return;
95
+ }
96
+ const timer = setTimeout(resolve, ms);
97
+ signal?.addEventListener(
98
+ "abort",
99
+ () => {
100
+ clearTimeout(timer);
101
+ reject(new DOMException("Polling was aborted", "AbortError"));
102
+ },
103
+ { once: true }
104
+ );
105
+ });
106
+ }
107
+
108
+ // src/client.ts
109
+ var ENVIRONMENT_URLS = {
110
+ test: "https://api.test.ecfx.ssd.com.do",
111
+ cert: "https://api.cert.ecfx.ssd.com.do",
112
+ prod: "https://api.prod.ecfx.ssd.com.do"
113
+ };
114
+ var ECF_TYPE_ROUTE_MAP = {
115
+ FacturaDeCreditoFiscalElectronica: "31",
116
+ FacturaDeConsumoElectronica: "32",
117
+ NotaDeDebitoElectronica: "33",
118
+ NotaDeCreditoElectronica: "34",
119
+ ComprasElectronico: "41",
120
+ GastosMenoresElectronico: "43",
121
+ RegimenesEspecialesElectronico: "44",
122
+ GubernamentalElectronico: "45",
123
+ ComprobanteDeExportacionesElectronico: "46",
124
+ ComprobanteParaPagosAlExteriorElectronico: "47"
125
+ };
126
+ var EcfError = class extends Error {
127
+ response;
128
+ constructor(message, response) {
129
+ super(message);
130
+ this.name = "EcfError";
131
+ this.response = response;
132
+ }
133
+ };
134
+ var EcfClient = class {
135
+ /** The underlying openapi-fetch client for direct endpoint access. */
136
+ raw;
137
+ constructor(config) {
138
+ const baseUrl = config.baseUrl ?? ENVIRONMENT_URLS[config.environment ?? "test"];
139
+ const authMiddleware = {
140
+ async onRequest({ request }) {
141
+ request.headers.set("Authorization", `Bearer ${config.apiKey}`);
142
+ return request;
143
+ }
144
+ };
145
+ this.raw = (0, import_openapi_fetch.default)({ baseUrl });
146
+ this.raw.use(authMiddleware);
147
+ }
148
+ // ---------------------------------------------------------------------------
149
+ // ECF send + poll
150
+ // ---------------------------------------------------------------------------
151
+ /**
152
+ * Send an ECF and poll until processing completes.
153
+ *
154
+ * Determines the correct endpoint from `ecf.encabezado.idDoc.tipoeCF`,
155
+ * posts the ECF, then polls until `progress` is `Finished` or `Error`.
156
+ */
157
+ async sendEcf(ecf, pollingOptions) {
158
+ const tipoeCF = ecf.encabezado?.idDoc?.tipoeCF;
159
+ if (!tipoeCF) {
160
+ throw new Error("ECF must have encabezado.idDoc.tipoeCF");
161
+ }
162
+ const route = ECF_TYPE_ROUTE_MAP[tipoeCF];
163
+ if (!route) {
164
+ throw new Error(`Unknown tipoeCF: ${tipoeCF}`);
165
+ }
166
+ const rnc = ecf.encabezado?.emisor?.rncEmisor;
167
+ if (!rnc) {
168
+ throw new Error("ECF must have encabezado.emisor.rncEmisor");
169
+ }
170
+ const encf = ecf.encabezado?.idDoc?.encf;
171
+ if (!encf) {
172
+ throw new Error("ECF must have encabezado.idDoc.encf");
173
+ }
174
+ const response = await this.postEcf(route, ecf);
175
+ const result = await pollUntilComplete(
176
+ async () => {
177
+ const { data: queryData, error: queryError } = await this.raw.GET("/ecf/{rnc}/{encf}", {
178
+ params: { path: { rnc, encf } }
179
+ });
180
+ if (queryError) {
181
+ throw new Error(`Failed to query ECF status: ${JSON.stringify(queryError)}`);
182
+ }
183
+ const results = queryData;
184
+ const match = results.find((r) => r.messageId === response.messageId) ?? results[0];
185
+ if (!match) {
186
+ throw new Error("No ECF response found for the given rnc/encf");
187
+ }
188
+ return match;
189
+ },
190
+ (r) => r.progress === "Finished" || r.progress === "Error",
191
+ pollingOptions
192
+ );
193
+ if (result.progress === "Error") {
194
+ throw new EcfError(
195
+ result.errors ?? result.mensaje ?? "ECF processing failed",
196
+ result
197
+ );
198
+ }
199
+ return result;
200
+ }
201
+ async postEcf(route, body) {
202
+ const path = `/ecf/${route}`;
203
+ const { data, error } = await this.raw.POST(path, { body });
204
+ if (error) {
205
+ throw new Error(`Failed to send ECF: ${JSON.stringify(error)}`);
206
+ }
207
+ return data;
208
+ }
209
+ // ---------------------------------------------------------------------------
210
+ // Company operations
211
+ // ---------------------------------------------------------------------------
212
+ /** List companies with optional filters. */
213
+ async getCompanies(params) {
214
+ return this.raw.GET("/company", { params: { query: params } });
215
+ }
216
+ /** Get a company by RNC. */
217
+ async getCompanyByRnc(rnc) {
218
+ return this.raw.GET("/company/{rnc}", { params: { path: { rnc } } });
219
+ }
220
+ /** Create or update a company. */
221
+ async upsertCompany(body) {
222
+ return this.raw.PUT("/company", { body });
223
+ }
224
+ /** Delete a company by RNC. */
225
+ async deleteCompany(rnc) {
226
+ return this.raw.DELETE("/company/{rnc}", { params: { path: { rnc } } });
227
+ }
228
+ // ---------------------------------------------------------------------------
229
+ // Certificate operations
230
+ // ---------------------------------------------------------------------------
231
+ /** Get the current certificate for a company. */
232
+ async getCertificate(rnc) {
233
+ return this.raw.GET("/company/{rnc}/certificate", { params: { path: { rnc } } });
234
+ }
235
+ /** Update a company's certificate. Pass a FormData with 'certificate' file and 'password' field. */
236
+ async updateCertificate(rnc, body) {
237
+ const formData = new FormData();
238
+ formData.append("certificate", body.certificate);
239
+ formData.append("password", body.password);
240
+ return this.raw.PUT("/company/{rnc}/certificate", {
241
+ params: { path: { rnc } },
242
+ body: formData,
243
+ bodySerializer: (b) => b
244
+ });
245
+ }
246
+ // ---------------------------------------------------------------------------
247
+ // ECF query operations
248
+ // ---------------------------------------------------------------------------
249
+ /** Query ECFs by RNC and eNCF. */
250
+ async queryEcf(rnc, encf) {
251
+ return this.raw.GET("/ecf/{rnc}/{encf}", { params: { path: { rnc, encf } } });
252
+ }
253
+ /** Search ECFs for a specific RNC. */
254
+ async searchEcfs(rnc, params) {
255
+ return this.raw.GET("/ecf/{rnc}", {
256
+ params: { path: { rnc }, query: params }
257
+ });
258
+ }
259
+ /** Search all ECFs across all companies. */
260
+ async searchAllEcfs(params) {
261
+ return this.raw.GET("/ecf", { params: { query: params } });
262
+ }
263
+ /** Get a specific ECF by message ID. */
264
+ async getEcfById(rnc, id) {
265
+ return this.raw.GET("/ecf/{rnc}/message/{id}", { params: { path: { rnc, id } } });
266
+ }
267
+ // ---------------------------------------------------------------------------
268
+ // Aprobacion comercial
269
+ // ---------------------------------------------------------------------------
270
+ /** Send aprobacion comercial for an ECF. */
271
+ async aprobacionComercial(rnc, encf, body) {
272
+ return this.raw.POST("/ecf/aprobacioncomercial/{rnc}/{encf}", {
273
+ params: { path: { rnc, encf } },
274
+ body
275
+ });
276
+ }
277
+ // ---------------------------------------------------------------------------
278
+ // Anulacion rangos
279
+ // ---------------------------------------------------------------------------
280
+ /** Request range annulment. */
281
+ async anulacionRangos(rnc, body) {
282
+ return this.raw.POST("/ecf/anularrango/{rnc}", {
283
+ params: { path: { rnc } },
284
+ body
285
+ });
286
+ }
287
+ /** List annulments. */
288
+ async listAnulaciones(params) {
289
+ return this.raw.GET("/ecf/anulaciones", { params: { query: params } });
290
+ }
291
+ // ---------------------------------------------------------------------------
292
+ // Firmar semilla
293
+ // ---------------------------------------------------------------------------
294
+ /** Sign a seed for a company. */
295
+ async firmarSemilla(rnc, body) {
296
+ const formData = new FormData();
297
+ formData.append("xml", body.xml);
298
+ return this.raw.POST("/ecf/FirmarSemilla/{rnc}", {
299
+ params: { path: { rnc } },
300
+ body: formData,
301
+ bodySerializer: (b) => b
302
+ });
303
+ }
304
+ // ---------------------------------------------------------------------------
305
+ // Recepcion operations
306
+ // ---------------------------------------------------------------------------
307
+ /** Search ECF reception requests. */
308
+ async searchEcfReceptionRequests(params) {
309
+ return this.raw.GET("/recepcion/ecf", { params: { query: params } });
310
+ }
311
+ /** Search ACECF reception requests. */
312
+ async searchAcecfReceptionRequests(params) {
313
+ return this.raw.GET("/recepcion/acecf", { params: { query: params } });
314
+ }
315
+ /** Search ECF reception requests by RNC. */
316
+ async searchEcfReceptionRequestsByRnc(rnc, params) {
317
+ return this.raw.GET("/recepcion/{rnc}/ecf", {
318
+ params: { path: { rnc }, query: params }
319
+ });
320
+ }
321
+ /** Get a specific ECF reception request. */
322
+ async getEcfReceptionRequest(rnc, messageId) {
323
+ return this.raw.GET("/recepcion/{rnc}/ecf/{messageId}", {
324
+ params: { path: { rnc, messageId } }
325
+ });
326
+ }
327
+ /** Search ACECF reception requests by RNC. */
328
+ async searchAcecfReceptionRequestsByRnc(rnc, params) {
329
+ return this.raw.GET("/recepcion/{rnc}/acecf", {
330
+ params: { path: { rnc }, query: params }
331
+ });
332
+ }
333
+ /** Get a specific ACECF reception request. */
334
+ async getAcecfReceptionRequest(rnc, messageId) {
335
+ return this.raw.GET("/recepcion/{rnc}/acecf/{messageId}", {
336
+ params: { path: { rnc, messageId } }
337
+ });
338
+ }
339
+ // ---------------------------------------------------------------------------
340
+ // DGII operations
341
+ // ---------------------------------------------------------------------------
342
+ /** Consulta directorio - listado. */
343
+ async consultaDirectorioListado(rnc) {
344
+ return this.raw.GET("/dgii/{rnc}/consultadirectorio/listado", {
345
+ params: { path: { rnc } }
346
+ });
347
+ }
348
+ /** Consulta directorio - obtener directorio por RNC. */
349
+ async consultaDirectorioPorRnc(rnc, query) {
350
+ return this.raw.GET("/dgii/{rnc}/consultadirectorio/obtener-directorio-por-rnc", {
351
+ params: { path: { rnc }, query }
352
+ });
353
+ }
354
+ /** Consulta estado. */
355
+ async consultaEstado(rnc, query) {
356
+ return this.raw.GET("/dgii/{rnc}/consultaestado/estado", {
357
+ params: { path: { rnc }, query }
358
+ });
359
+ }
360
+ /** Consulta resultado. */
361
+ async consultaResultado(rnc, query) {
362
+ return this.raw.GET("/dgii/{rnc}/consultaresultado/estado", {
363
+ params: { path: { rnc }, query }
364
+ });
365
+ }
366
+ /** Consulta RFCE. */
367
+ async consultaRFCE(rnc, query) {
368
+ return this.raw.GET("/dgii/{rnc}/consultarfce/consulta", {
369
+ params: { path: { rnc }, query }
370
+ });
371
+ }
372
+ /** Consulta timbre. */
373
+ async consultaTimbre(rnc, query) {
374
+ return this.raw.GET("/dgii/{rnc}/consultatimbre", {
375
+ params: { path: { rnc }, query }
376
+ });
377
+ }
378
+ /** Consulta timbre FC. */
379
+ async consultaTimbreFC(rnc, query) {
380
+ return this.raw.GET("/dgii/{rnc}/consultatimbrefc", {
381
+ params: { path: { rnc }, query }
382
+ });
383
+ }
384
+ /** Consulta track IDs. */
385
+ async consultaTrackId(rnc, query) {
386
+ return this.raw.GET("/dgii/{rnc}/consultatrackids/consulta", {
387
+ params: { path: { rnc }, query }
388
+ });
389
+ }
390
+ /** Estatus servicios - obtener estatus. */
391
+ async estatusServicios(rnc) {
392
+ return this.raw.GET("/dgii/{rnc}/estatusservicios/obtener-estatus", {
393
+ params: { path: { rnc } }
394
+ });
395
+ }
396
+ /** Estatus servicios - obtener ventanas de mantenimiento. */
397
+ async ventanasMantenimiento(rnc) {
398
+ return this.raw.GET("/dgii/{rnc}/estatusservicios/obtener-ventanas-mantenimiento", {
399
+ params: { path: { rnc } }
400
+ });
401
+ }
402
+ // ---------------------------------------------------------------------------
403
+ // ApiKey operations
404
+ // ---------------------------------------------------------------------------
405
+ /** Create a new API key. */
406
+ async createApiKey(body) {
407
+ return this.raw.POST("/apiKey", { body });
408
+ }
409
+ };
410
+ var EcfFrontendClient = class {
411
+ /** The underlying openapi-fetch client for direct endpoint access. */
412
+ raw;
413
+ constructor(config) {
414
+ const baseUrl = config.baseUrl ?? ENVIRONMENT_URLS[config.environment ?? "test"];
415
+ const authMiddleware = {
416
+ async onRequest({ request }) {
417
+ request.headers.set("Authorization", `Bearer ${config.apiKey}`);
418
+ return request;
419
+ }
420
+ };
421
+ this.raw = (0, import_openapi_fetch.default)({ baseUrl });
422
+ this.raw.use(authMiddleware);
423
+ }
424
+ /** Query ECFs by RNC and eNCF. */
425
+ async queryEcf(rnc, encf) {
426
+ return this.raw.GET("/ecf/{rnc}/{encf}", { params: { path: { rnc, encf } } });
427
+ }
428
+ /** Search ECFs for a specific RNC. */
429
+ async searchEcfs(rnc, params) {
430
+ return this.raw.GET("/ecf/{rnc}", {
431
+ params: { path: { rnc }, query: params }
432
+ });
433
+ }
434
+ /** Search all ECFs across all companies. */
435
+ async searchAllEcfs(params) {
436
+ return this.raw.GET("/ecf", { params: { query: params } });
437
+ }
438
+ /** Get a specific ECF by message ID. */
439
+ async getEcfById(rnc, id) {
440
+ return this.raw.GET("/ecf/{rnc}/message/{id}", { params: { path: { rnc, id } } });
441
+ }
442
+ /** List companies with optional filters. */
443
+ async getCompanies(params) {
444
+ return this.raw.GET("/company", { params: { query: params } });
445
+ }
446
+ /** Get a company by RNC. */
447
+ async getCompanyByRnc(rnc) {
448
+ return this.raw.GET("/company/{rnc}", { params: { path: { rnc } } });
449
+ }
450
+ };
451
+ function createFrontendClient(config) {
452
+ return new EcfFrontendClient(config);
453
+ }
454
+ // Annotate the CommonJS export names for ESM import in node:
455
+ 0 && (module.exports = {
456
+ EcfClient,
457
+ EcfError,
458
+ EcfFrontendClient,
459
+ PollingMaxRetriesError,
460
+ PollingTimeoutError,
461
+ createFrontendClient,
462
+ pollUntilComplete
463
+ });