arca-sdk 0.1.2 → 0.1.4
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/dist/index.cjs +48 -13
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +48 -13
- package/dist/index.js.map +1 -0
- package/package.json +5 -8
package/dist/index.cjs
CHANGED
|
@@ -79,6 +79,12 @@ var ArcaValidationError = class extends ArcaError {
|
|
|
79
79
|
this.name = "ArcaValidationError";
|
|
80
80
|
}
|
|
81
81
|
};
|
|
82
|
+
var ArcaNetworkError = class extends ArcaError {
|
|
83
|
+
constructor(message, details) {
|
|
84
|
+
super(message, "NETWORK_ERROR", details);
|
|
85
|
+
this.name = "ArcaNetworkError";
|
|
86
|
+
}
|
|
87
|
+
};
|
|
82
88
|
|
|
83
89
|
// src/utils/xml.ts
|
|
84
90
|
var import_fast_xml_parser = require("fast-xml-parser");
|
|
@@ -245,21 +251,47 @@ var TicketManager = class {
|
|
|
245
251
|
// src/utils/network.ts
|
|
246
252
|
var import_https = __toESM(require("https"), 1);
|
|
247
253
|
async function callArcaApi(url, options) {
|
|
254
|
+
const timeout = options.timeout || 15e3;
|
|
248
255
|
const isNode = typeof process !== "undefined" && process.versions && process.versions.node;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
+
const controller = new AbortController();
|
|
257
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
258
|
+
try {
|
|
259
|
+
if (isNode) {
|
|
260
|
+
const agent = new import_https.default.Agent({
|
|
261
|
+
// Permitir llaves DH de 1024 bits (AFIP) bajando a Security Level 1
|
|
262
|
+
// solo para esta conexión específica.
|
|
263
|
+
ciphers: "DEFAULT@SECLEVEL=1",
|
|
264
|
+
rejectUnauthorized: true
|
|
265
|
+
});
|
|
266
|
+
return await fetch(url, {
|
|
267
|
+
method: options.method,
|
|
268
|
+
headers: options.headers,
|
|
269
|
+
body: options.body,
|
|
270
|
+
agent,
|
|
271
|
+
signal: controller.signal
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return await fetch(url, {
|
|
275
|
+
method: options.method,
|
|
276
|
+
headers: options.headers,
|
|
277
|
+
body: options.body,
|
|
278
|
+
signal: controller.signal
|
|
256
279
|
});
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (error.name === "AbortError") {
|
|
282
|
+
throw new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`);
|
|
283
|
+
}
|
|
284
|
+
let message = error.message;
|
|
285
|
+
if (message.includes("dh key too small")) {
|
|
286
|
+
message = "Error SSL de ARCA (DH Key too small). El SDK intent\xF3 mitigarlo pero fall\xF3. Verifique su versi\xF3n de Node.js.";
|
|
287
|
+
}
|
|
288
|
+
throw new ArcaNetworkError(`Error de red al comunicarse con ARCA: ${message}`, {
|
|
289
|
+
url,
|
|
290
|
+
originalError: error
|
|
260
291
|
});
|
|
292
|
+
} finally {
|
|
293
|
+
clearTimeout(timeoutId);
|
|
261
294
|
}
|
|
262
|
-
return fetch(url, options);
|
|
263
295
|
}
|
|
264
296
|
|
|
265
297
|
// src/auth/wsaa.ts
|
|
@@ -336,7 +368,8 @@ var WsaaService = class {
|
|
|
336
368
|
"Content-Type": "text/xml; charset=utf-8",
|
|
337
369
|
"SOAPAction": ""
|
|
338
370
|
},
|
|
339
|
-
body: this.buildSoapRequest(cms)
|
|
371
|
+
body: this.buildSoapRequest(cms),
|
|
372
|
+
timeout: this.config.timeout
|
|
340
373
|
});
|
|
341
374
|
if (!response.ok) {
|
|
342
375
|
throw new ArcaAuthError(
|
|
@@ -625,7 +658,8 @@ var WsfeService = class {
|
|
|
625
658
|
"Content-Type": "text/xml; charset=utf-8",
|
|
626
659
|
"SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAESolicitar"
|
|
627
660
|
},
|
|
628
|
-
body: soapRequest
|
|
661
|
+
body: soapRequest,
|
|
662
|
+
timeout: this.config.timeout
|
|
629
663
|
});
|
|
630
664
|
if (!response.ok) {
|
|
631
665
|
throw new ArcaError(
|
|
@@ -839,3 +873,4 @@ function generarUrlQR(caeResponse, cuitEmisor, total, comprador) {
|
|
|
839
873
|
WsfeService,
|
|
840
874
|
generarUrlQR
|
|
841
875
|
});
|
|
876
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constants/endpoints.ts","../src/types/common.ts","../src/utils/xml.ts","../src/utils/crypto.ts","../src/auth/ticket.ts","../src/utils/network.ts","../src/auth/wsaa.ts","../src/types/wsfe.ts","../src/utils/calculations.ts","../src/services/wsfe.ts","../src/utils/qr.ts"],"sourcesContent":["/**\r\n * arca-sdk - SDK moderna para ARCA (ex-AFIP)\r\n */\r\n\r\n// Servicios principales\r\nexport { WsaaService } from './auth/wsaa';\r\nexport { WsfeService } from './services/wsfe';\r\n\r\n// Tipos comunes\r\nexport type {\r\n Environment,\r\n ArcaConfig,\r\n} from './types/common';\r\n\r\n// Tipos WSAA\r\nexport type {\r\n WsaaConfig,\r\n LoginTicket,\r\n} from './types/wsaa';\r\n\r\n// Tipos WSFE\r\nexport type {\r\n WsfeConfig,\r\n FacturaItem,\r\n Comprador,\r\n EmitirFacturaRequest,\r\n CAEResponse,\r\n} from './types/wsfe';\r\n\r\n// Enums WSFE\r\nexport {\r\n TipoComprobante,\r\n Concepto,\r\n TipoDocumento,\r\n} from './types/wsfe';\r\n\r\n// Errores\r\nexport {\r\n ArcaError,\r\n ArcaAuthError,\r\n ArcaValidationError,\r\n} from './types/common';\r\n\r\n// Utilidades útiles para Frontend/Impresión\r\nexport { generarUrlQR } from './utils/qr';\r\n","import type { Environment } from '../types/common';\r\n\r\n/**\r\n * URLs de los servicios ARCA por ambiente\r\n */\r\nexport const WSAA_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms',\r\n produccion: 'https://wsaa.afip.gov.ar/ws/services/LoginCms',\r\n};\r\n\r\n/**\r\n * URLs del servicio WSFE por ambiente\r\n */\r\nexport const WSFE_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx',\r\n produccion: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx',\r\n};\r\n\r\n/**\r\n * Obtener endpoint WSAA según ambiente\r\n */\r\nexport function getWsaaEndpoint(environment: Environment): string {\r\n return WSAA_ENDPOINTS[environment];\r\n}\r\n\r\n/**\r\n * Obtener endpoint WSFE según ambiente\r\n */\r\nexport function getWsfeEndpoint(environment: Environment): string {\r\n return WSFE_ENDPOINTS[environment];\r\n}\r\n","/**\r\n * Tipos comunes compartidos en toda la SDK\r\n */\r\n\r\n/**\r\n * Ambiente de ejecución ARCA\r\n */\r\nexport type Environment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Configuración base para servicios ARCA\r\n */\r\nexport interface ArcaConfig {\r\n /** Ambiente (homologación o producción) */\r\n environment: Environment;\r\n /** CUIT del contribuyente (11 dígitos sin guiones) */\r\n cuit: string;\r\n /** Tiempo de espera para peticiones (ms). Defecto: 15000 */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Error personalizado de ARCA SDK\r\n */\r\nexport class ArcaError extends Error {\r\n constructor(\r\n message: string,\r\n public code: string,\r\n public details?: unknown\r\n ) {\r\n super(message);\r\n this.name = 'ArcaError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de autenticación WSAA\r\n */\r\nexport class ArcaAuthError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'AUTH_ERROR', details);\r\n this.name = 'ArcaAuthError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de validación de input\r\n */\r\nexport class ArcaValidationError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'VALIDATION_ERROR', details);\r\n this.name = 'ArcaValidationError';\r\n }\r\n}\r\n/**\r\n * Error de comunicación/red\r\n */\r\nexport class ArcaNetworkError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'NETWORK_ERROR', details);\r\n this.name = 'ArcaNetworkError';\r\n }\r\n}\r\n","import { XMLBuilder, XMLParser } from 'fast-xml-parser';\r\nimport { ArcaAuthError } from '../types/common';\r\nimport type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Genera el XML TRA (Ticket de Requerimiento de Acceso)\r\n * \r\n * @param service - Servicio ARCA (ej: 'wsfe')\r\n * @param cuit - CUIT del contribuyente\r\n * @returns XML del TRA\r\n */\r\nexport function buildTRA(service: string, cuit: string): string {\r\n const now = new Date();\r\n const expirationTime = new Date(now.getTime() + 12 * 60 * 60 * 1000); // +12 horas\r\n\r\n const tra = {\r\n loginTicketRequest: {\r\n '@_version': '1.0',\r\n header: {\r\n uniqueId: Math.floor(now.getTime() / 1000),\r\n generationTime: now.toISOString(),\r\n expirationTime: expirationTime.toISOString(),\r\n },\r\n service,\r\n },\r\n };\r\n\r\n const builder = new XMLBuilder({\r\n ignoreAttributes: false,\r\n format: true,\r\n });\r\n\r\n const xml = builder.build(tra);\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n${xml}`;\r\n}\r\n\r\n/**\r\n * Parsea la respuesta XML de WSAA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Ticket de login\r\n */\r\nexport function parseWsaaResponse(xml: string): LoginTicket {\r\n try {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true, // Sugerencia: más robusto contra cambios de prefijos soapenv\r\n });\r\n\r\n const result = parser.parse(xml);\r\n\r\n // Navegar estructura XML de respuesta WSAA\r\n // Gracias a removeNSPrefix: true, evitamos depender de 'soapenv:'\r\n const credentials = result?.Envelope?.Body?.loginCmsResponse?.loginCmsReturn;\r\n\r\n if (!credentials) {\r\n // Intentar detectar error de ARCA en respuesta\r\n const fault = result?.Envelope?.Body?.Fault;\r\n if (fault) {\r\n throw new ArcaAuthError(\r\n `Error ARCA: ${fault.faultstring || 'Error desconocido'}`,\r\n { faultCode: fault.faultcode, detail: fault.detail }\r\n );\r\n }\r\n\r\n throw new ArcaAuthError(\r\n 'Respuesta WSAA inválida: estructura no reconocida',\r\n { receivedStructure: result }\r\n );\r\n }\r\n\r\n // fast-xml-parser con removeNSPrefix puede dejar los campos limpios\r\n return {\r\n token: credentials.token,\r\n sign: credentials.sign,\r\n generationTime: new Date(credentials.header.generationTime),\r\n expirationTime: new Date(credentials.header.expirationTime),\r\n };\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al parsear respuesta WSAA',\r\n { originalError: error }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Parsea un XML genérico de ARCA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Objeto parseado\r\n */\r\nexport function parseXml(xml: string): any {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true,\r\n });\r\n return parser.parse(xml);\r\n}\r\n\r\n/**\r\n * Valida CUIT (11 dígitos sin guiones)\r\n */\r\nexport function validateCUIT(cuit: string): boolean {\r\n return /^\\d{11}$/.test(cuit);\r\n}\r\n","import * as forge from 'node-forge';\r\nimport { ArcaAuthError } from '../types/common';\r\n\r\n/**\r\n * Firma un XML en formato CMS (PKCS#7) usando certificado y clave privada\r\n * \r\n * @param xml - Contenido XML a firmar (TRA)\r\n * @param certPem - Certificado en formato PEM\r\n * @param keyPem - Clave privada en formato PEM\r\n * @returns CMS firmado en base64\r\n */\r\nexport function signCMS(xml: string, certPem: string, keyPem: string): string {\r\n try {\r\n // 1. Parsear certificado\r\n const cert = forge.pki.certificateFromPem(certPem);\r\n\r\n // 2. Parsear clave privada\r\n const privateKey = forge.pki.privateKeyFromPem(keyPem);\r\n\r\n // 3. Crear contenedor PKCS#7\r\n const p7 = forge.pkcs7.createSignedData();\r\n\r\n // 4. Agregar contenido a firmar\r\n p7.content = forge.util.createBuffer(xml, 'utf8');\r\n\r\n // 5. Agregar certificado\r\n p7.addCertificate(cert);\r\n\r\n // 6. Firmar con SHA256\r\n p7.addSigner({\r\n key: privateKey,\r\n certificate: cert,\r\n digestAlgorithm: forge.pki.oids.sha256,\r\n authenticatedAttributes: [\r\n {\r\n type: forge.pki.oids.contentType,\r\n value: forge.pki.oids.data,\r\n },\r\n {\r\n type: forge.pki.oids.messageDigest,\r\n // El valor será calculado automáticamente\r\n },\r\n {\r\n type: forge.pki.oids.signingTime,\r\n // node-forge expects a Date object, but its typings might be missing or expect string/any\r\n value: new Date() as any,\r\n },\r\n ],\r\n });\r\n\r\n // 7. Firmar\r\n p7.sign();\r\n\r\n // 8. Convertir a DER y luego a base64\r\n const derBytes = forge.asn1.toDer(p7.toAsn1()).getBytes();\r\n const base64 = forge.util.encode64(derBytes);\r\n\r\n return base64;\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al firmar XML con certificado usando PKCS#7',\r\n {\r\n originalError: error,\r\n hint: 'Verificar que el certificado y la clave privada sean válidos y correspondan entre sí'\r\n }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Valida formato de certificado PEM\r\n */\r\nexport function validateCertificate(cert: string): boolean {\r\n return cert.includes('-----BEGIN CERTIFICATE-----') &&\r\n cert.includes('-----END CERTIFICATE-----');\r\n}\r\n\r\n/**\r\n * Valida formato de clave privada PEM\r\n */\r\nexport function validatePrivateKey(key: string): boolean {\r\n return (\r\n (key.includes('-----BEGIN PRIVATE KEY-----') &&\r\n key.includes('-----END PRIVATE KEY-----')) ||\r\n (key.includes('-----BEGIN RSA PRIVATE KEY-----') &&\r\n key.includes('-----END RSA PRIVATE KEY-----'))\r\n );\r\n}\r\n","import type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Gestor de tickets de autenticación\r\n * Maneja cache en memoria del ticket WSAA\r\n */\r\nexport class TicketManager {\r\n private ticket: LoginTicket | null = null;\r\n\r\n /**\r\n * Guarda un ticket en cache\r\n */\r\n setTicket(ticket: LoginTicket): void {\r\n this.ticket = ticket;\r\n }\r\n\r\n /**\r\n * Obtiene el ticket actual si es válido\r\n * @returns Ticket válido o null si expiró\r\n */\r\n getTicket(): LoginTicket | null {\r\n if (!this.ticket) return null;\r\n\r\n const now = new Date();\r\n const expiresIn = this.ticket.expirationTime.getTime() - now.getTime();\r\n\r\n // Si expira en menos de 5 minutos, considerarlo inválido\r\n const BUFFER_MS = 5 * 60 * 1000;\r\n\r\n if (expiresIn < BUFFER_MS) {\r\n this.ticket = null;\r\n return null;\r\n }\r\n\r\n return this.ticket;\r\n }\r\n\r\n /**\r\n * Verifica si hay un ticket válido\r\n */\r\n hasValidTicket(): boolean {\r\n return this.getTicket() !== null;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache\r\n */\r\n clearTicket(): void {\r\n this.ticket = null;\r\n }\r\n}\r\n","import https from 'https';\r\nimport { ArcaNetworkError } from '../types/common';\r\n\r\n/**\r\n * Entornos compatibles\r\n */\r\nexport type ArcaEnvironment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Opciones para la llamada API\r\n */\r\nexport interface CallApiOptions {\r\n method: string;\r\n headers: Record<string, string>;\r\n body: string;\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Realiza una llamada a la API de ARCA (WSAA o WSFE)\r\n * Maneja la compatibilidad SSL con los servidores de AFIP (DH key size)\r\n * y añade robustez (timeouts, mejores errores).\r\n */\r\nexport async function callArcaApi(\r\n url: string,\r\n options: CallApiOptions\r\n): Promise<Response> {\r\n const timeout = options.timeout || 15000; // 15 segundos por defecto\r\n\r\n // Si estamos en Node.js, necesitamos usar un agente HTTPS que permita SECLEVEL=1\r\n // para evitar el error \"dh key too small\" de AFIP (OpenSSL 3.0+)\r\n const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;\r\n\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n if (isNode) {\r\n const agent = new https.Agent({\r\n // Permitir llaves DH de 1024 bits (AFIP) bajando a Security Level 1\r\n // solo para esta conexión específica.\r\n ciphers: 'DEFAULT@SECLEVEL=1',\r\n rejectUnauthorized: true,\r\n });\r\n\r\n // @ts-ignore - node-fetch o fetch nativo en Node 18+ aceptan el agente\r\n return await fetch(url, {\r\n method: options.method,\r\n headers: options.headers,\r\n body: options.body,\r\n agent,\r\n signal: controller.signal\r\n } as any);\r\n }\r\n\r\n return await fetch(url, {\r\n method: options.method,\r\n headers: options.headers,\r\n body: options.body,\r\n signal: controller.signal\r\n });\r\n } catch (error: any) {\r\n if (error.name === 'AbortError') {\r\n throw new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`);\r\n }\r\n\r\n // Mapear errores de SSL comunes para dar pistas claras al desarrollador\r\n let message = error.message;\r\n if (message.includes('dh key too small')) {\r\n message = 'Error SSL de ARCA (DH Key too small). El SDK intentó mitigarlo pero falló. Verifique su versión de Node.js.';\r\n }\r\n\r\n throw new ArcaNetworkError(`Error de red al comunicarse con ARCA: ${message}`, {\r\n url,\r\n originalError: error\r\n });\r\n } finally {\r\n clearTimeout(timeoutId);\r\n }\r\n}\r\n","import { getWsaaEndpoint } from '../constants/endpoints';\r\nimport { ArcaAuthError, ArcaValidationError } from '../types/common';\r\nimport type { WsaaConfig, LoginTicket } from '../types/wsaa';\r\nimport { buildTRA, parseWsaaResponse, validateCUIT } from '../utils/xml';\r\nimport { validateCertificate, validatePrivateKey, signCMS } from '../utils/crypto';\r\nimport { TicketManager } from './ticket';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de autenticación WSAA (Web Service de Autenticación y Autorización)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsaa = new WsaaService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * cert: fs.readFileSync('cert.pem', 'utf-8'),\r\n * key: fs.readFileSync('key.pem', 'utf-8'),\r\n * service: 'wsfe',\r\n * });\r\n * \r\n * const ticket = await wsaa.login();\r\n * console.log('Token:', ticket.token);\r\n * ```\r\n */\r\nexport class WsaaService {\r\n private config: WsaaConfig;\r\n private ticketManager: TicketManager;\r\n\r\n constructor(config: WsaaConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n this.ticketManager = new TicketManager();\r\n }\r\n\r\n /**\r\n * Valida la configuración\r\n */\r\n private validateConfig(config: WsaaConfig): void {\r\n if (!validateCUIT(config.cuit)) {\r\n throw new ArcaValidationError(\r\n 'CUIT inválido: debe tener 11 dígitos sin guiones',\r\n { cuit: config.cuit }\r\n );\r\n }\r\n\r\n if (!validateCertificate(config.cert)) {\r\n throw new ArcaValidationError(\r\n 'Certificado inválido: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN CERTIFICATE-----' }\r\n );\r\n }\r\n\r\n if (!validatePrivateKey(config.key)) {\r\n throw new ArcaValidationError(\r\n 'Clave privada inválida: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN PRIVATE KEY----- o -----BEGIN RSA PRIVATE KEY-----' }\r\n );\r\n }\r\n\r\n if (!config.service || config.service.trim() === '') {\r\n throw new ArcaValidationError(\r\n 'Servicio ARCA no especificado',\r\n { hint: 'Ejemplos: \"wsfe\", \"wsmtxca\"' }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Obtiene un ticket de acceso válido\r\n * Usa cache si el ticket actual es válido\r\n * \r\n * @returns Ticket de acceso\r\n */\r\n async login(): Promise<LoginTicket> {\r\n // Intentar usar ticket en cache\r\n const cachedTicket = this.ticketManager.getTicket();\r\n if (cachedTicket) {\r\n return cachedTicket;\r\n }\r\n\r\n // Generar nuevo ticket\r\n const ticket = await this.requestNewTicket();\r\n this.ticketManager.setTicket(ticket);\r\n return ticket;\r\n }\r\n\r\n /**\r\n * Solicita un nuevo ticket a WSAA\r\n */\r\n private async requestNewTicket(): Promise<LoginTicket> {\r\n // 1. Generar TRA (Ticket de Requerimiento de Acceso)\r\n const tra = buildTRA(this.config.service, this.config.cuit);\r\n\r\n // 2. Firmar TRA con certificado (genera CMS)\r\n let cms: string;\r\n try {\r\n cms = signCMS(tra, this.config.cert, this.config.key);\r\n } catch (error) {\r\n throw new ArcaAuthError(\r\n 'Error al firmar TRA con certificado',\r\n { originalError: error }\r\n );\r\n }\r\n\r\n // 3. Enviar CMS a WSAA\r\n const endpoint = getWsaaEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': '',\r\n },\r\n body: this.buildSoapRequest(cms),\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaAuthError(\r\n `Error HTTP al comunicarse con WSAA: ${response.status} ${response.statusText}`,\r\n { status: response.status, statusText: response.statusText }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 4. Parsear respuesta\r\n return parseWsaaResponse(responseXml);\r\n }\r\n\r\n /**\r\n * Construye el SOAP request para WSAA\r\n */\r\n private buildSoapRequest(cms: string): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:wsaa=\"http://wsaa.view.sua.dvadac.desein.afip.gov\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <wsaa:loginCms>\r\n <wsaa:in0>${cms}</wsaa:in0>\r\n </wsaa:loginCms>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache (forzar renovación)\r\n */\r\n clearCache(): void {\r\n this.ticketManager.clearTicket();\r\n }\r\n}\r\n","import type { ArcaConfig } from './common';\r\nimport type { LoginTicket } from './wsaa';\r\n\r\n/**\r\n * Configuración para WSFE\r\n */\r\nexport interface WsfeConfig extends ArcaConfig {\r\n /** Ticket de autenticación WSAA */\r\n ticket: LoginTicket;\r\n /** Punto de venta (4 dígitos) */\r\n puntoVenta: number;\r\n}\r\n\r\n/**\r\n * Tipo de comprobante\r\n */\r\nexport enum TipoComprobante {\r\n FACTURA_A = 1,\r\n FACTURA_B = 6,\r\n FACTURA_C = 11,\r\n TICKET_A = 81,\r\n TICKET_B = 82,\r\n TICKET_C = 83,\r\n}\r\n\r\n/**\r\n * Concepto de facturación\r\n */\r\nexport enum Concepto {\r\n PRODUCTOS = 1, // Productos\r\n SERVICIOS = 2, // Servicios\r\n PRODUCTOS_Y_SERVICIOS = 3, // Mixto\r\n}\r\n\r\n/**\r\n * Tipo de documento del cliente\r\n */\r\nexport enum TipoDocumento {\r\n CUIT = 80,\r\n CUIL = 86,\r\n CDI = 87,\r\n LE = 89,\r\n LC = 90,\r\n CI_EXTRANJERA = 91,\r\n PASAPORTE = 94,\r\n CI_BUENOS_AIRES = 95,\r\n CI_POLICIA_FEDERAL = 96,\r\n DNI = 96,\r\n CONSUMIDOR_FINAL = 99, // Sin documento\r\n}\r\n\r\n/**\r\n * Item de factura\r\n */\r\nexport interface FacturaItem {\r\n /** Descripción del producto/servicio */\r\n descripcion: string;\r\n /** Cantidad */\r\n cantidad: number;\r\n /** Precio unitario */\r\n precioUnitario: number;\r\n /** IVA % (0, 10.5, 21, 27) */\r\n alicuotaIva?: number;\r\n}\r\n\r\n/**\r\n * Datos del comprador\r\n */\r\nexport interface Comprador {\r\n /** Tipo de documento */\r\n tipoDocumento: TipoDocumento;\r\n /** Número de documento (sin guiones) */\r\n nroDocumento: string;\r\n}\r\n\r\n/**\r\n * Request para emitir factura\r\n */\r\nexport interface EmitirFacturaRequest {\r\n /** Tipo de comprobante */\r\n tipo: TipoComprobante;\r\n /** Concepto */\r\n concepto: Concepto;\r\n /** Comprador (opcional para Factura C consumidor final) */\r\n comprador?: Comprador;\r\n /** Items de la factura (calculan el total si se proveen) */\r\n items?: FacturaItem[];\r\n /** Monto total (requerido si no hay items) */\r\n total?: number;\r\n /** Desglose de IVA (requerido para Factura B/A) */\r\n ivaData?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n /** Indica si los preciosUnitarios de los items YA incluyen el IVA (Precio Final). Defecto: false */\r\n incluyeIva?: boolean;\r\n /** Fecha del comprobante (default: hoy) */\r\n fecha?: Date;\r\n}\r\n\r\n/**\r\n * Respuesta CAE (Código de Autorización Electrónico)\r\n */\r\nexport interface CAEResponse {\r\n /** Tipo de comprobante */\r\n tipoComprobante: number;\r\n /** Punto de venta */\r\n puntoVenta: number;\r\n /** Número de comprobante */\r\n nroComprobante: number;\r\n /** Fecha de emisión */\r\n fecha: string;\r\n /** CAE asignado */\r\n cae: string;\r\n /** Fecha de vencimiento del CAE */\r\n vencimientoCae: string;\r\n /** Resultado (A = Aprobado, R = Rechazado) */\r\n resultado: 'A' | 'R';\r\n /** Observaciones de ARCA (si hay) */\r\n observaciones?: string[];\r\n /** Items (se retornan si fueron proveídos en el request) */\r\n items?: FacturaItem[];\r\n /** Desglose IVA (solo para Factura B/A) */\r\n iva?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n}\r\n","import type { FacturaItem } from '../types/wsfe';\r\n\r\n/**\r\n * Calcula el subtotal de items (sin IVA)\r\n */\r\nexport function calcularSubtotal(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && item.alicuotaIva) {\r\n precioNeto = item.precioUnitario / (1 + (item.alicuotaIva / 100));\r\n }\r\n return sum + (item.cantidad * precioNeto);\r\n }, 0);\r\n}\r\n\r\n\r\n/**\r\n * Calcula el IVA total de items\r\n */\r\nexport function calcularIVA(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n const subtotalNeto = item.cantidad * precioNeto;\r\n return sum + (subtotalNeto * alicuota / 100);\r\n }, 0);\r\n}\r\n\r\n/**\r\n * Calcula el total de la factura (subtotal + IVA)\r\n */\r\nexport function calcularTotal(items: FacturaItem[], incluyeIva = false): number {\r\n if (incluyeIva) {\r\n return items.reduce((sum, item) => sum + (item.cantidad * item.precioUnitario), 0);\r\n }\r\n const subtotal = calcularSubtotal(items, false);\r\n const iva = calcularIVA(items, false);\r\n return subtotal + iva;\r\n}\r\n\r\n/**\r\n * Redondea a 2 decimales\r\n */\r\nexport function redondear(valor: number): number {\r\n return Math.round(valor * 100) / 100;\r\n}\r\n","import { getWsfeEndpoint } from '../constants/endpoints';\r\nimport { ArcaError, ArcaValidationError } from '../types/common';\r\nimport type {\r\n WsfeConfig,\r\n EmitirFacturaRequest,\r\n CAEResponse,\r\n FacturaItem,\r\n Comprador,\r\n} from '../types/wsfe';\r\nimport {\r\n TipoComprobante,\r\n Concepto,\r\n TipoDocumento\r\n} from '../types/wsfe';\r\nimport {\r\n calcularSubtotal,\r\n calcularIVA,\r\n calcularTotal,\r\n redondear,\r\n} from '../utils/calculations';\r\nimport { parseXml } from '../utils/xml';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de Facturación Electrónica (WSFE v1)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsfe = new WsfeService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * ticket: await wsaa.login(),\r\n * puntoVenta: 4,\r\n * });\r\n * \r\n * const cae = await wsfe.emitirFacturaC({\r\n * items: [\r\n * { descripcion: 'Producto 1', cantidad: 2, precioUnitario: 100 }\r\n * ]\r\n * });\r\n * \r\n * console.log('CAE:', cae.cae);\r\n * ```\r\n */\r\nexport class WsfeService {\r\n private config: WsfeConfig;\r\n\r\n constructor(config: WsfeConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n }\r\n\r\n private validateConfig(config: WsfeConfig): void {\r\n if (!config.ticket || !config.ticket.token) {\r\n throw new ArcaValidationError(\r\n 'Ticket WSAA requerido. Ejecutá wsaa.login() primero.',\r\n { hint: 'El ticket se obtiene del servicio WSAA' }\r\n );\r\n }\r\n\r\n if (!config.puntoVenta || config.puntoVenta < 1 || config.puntoVenta > 9999) {\r\n throw new ArcaValidationError(\r\n 'Punto de venta inválido: debe ser un número entre 1 y 9999',\r\n { puntoVenta: config.puntoVenta }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Emite un Ticket C de forma simple (solo total)\r\n * Tipo de comprobante: 83\r\n */\r\n async emitirTicketCSimple(params: {\r\n total: number;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total: params.total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Emite un Ticket C completo (con detalle de items)\r\n * Los items no se envían a ARCA, pero se retornan en la respuesta.\r\n */\r\n async emitirTicketC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n const cae = await this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n\r\n return {\r\n ...cae,\r\n items: params.items,\r\n };\r\n }\r\n /**\r\n * Emite una Factura B (monotributo a responsable inscripto)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaB(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_B,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Emite una Factura A (RI a RI)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaA(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_A,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Valida que todos los items tengan alícuota IVA definida\r\n */\r\n private validateItemsWithIVA(items: FacturaItem[]): void {\r\n const sinIva = items.filter(item =>\r\n item.alicuotaIva === undefined || item.alicuotaIva === null\r\n );\r\n\r\n if (sinIva.length > 0) {\r\n throw new ArcaValidationError(\r\n 'Esta operación requiere IVA discriminado en todos los items',\r\n {\r\n itemsSinIva: sinIva.map(i => i.descripcion),\r\n hint: 'Agregá alicuotaIva a cada item (21, 10.5, 27, o 0)'\r\n }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Calcula IVA agrupado por alícuota\r\n * ARCA requiere esto para Factura B/A\r\n */\r\n private calcularIVAPorAlicuota(items: FacturaItem[], incluyeIva = false): {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[] {\r\n const porAlicuota = new Map<number, { base: number; importe: number }>();\r\n\r\n items.forEach(item => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n\r\n const base = item.cantidad * precioNeto;\r\n const importe = base * alicuota / 100;\r\n\r\n const actual = porAlicuota.get(alicuota) || { base: 0, importe: 0 };\r\n porAlicuota.set(alicuota, {\r\n base: actual.base + base,\r\n importe: actual.importe + importe,\r\n });\r\n });\r\n\r\n return Array.from(porAlicuota.entries()).map(([alicuota, valores]) => ({\r\n alicuota,\r\n baseImponible: redondear(valores.base),\r\n importe: redondear(valores.importe),\r\n }));\r\n }\r\n\r\n /**\r\n * Emite una Factura C (consumidor final)\r\n * Forma simplificada sin especificar comprador\r\n */\r\n async emitirFacturaC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n items: params.items,\r\n });\r\n }\r\n\r\n /**\r\n * Emite un comprobante (método genérico interno)\r\n */\r\n async emitirComprobante(request: EmitirFacturaRequest): Promise<CAEResponse> {\r\n // 1. Obtener próximo número de comprobante\r\n const nroComprobante = await this.obtenerProximoNumero(request.tipo);\r\n\r\n // 2. Determinar total\r\n let total = request.total || 0;\r\n let subtotal = total; // Para Factura C/Ticket C, el neto es igual al total si no discriminamos\r\n let iva = 0;\r\n\r\n if (request.items && request.items.length > 0) {\r\n const incluyeIva = request.incluyeIva || false;\r\n subtotal = redondear(calcularSubtotal(request.items, incluyeIva));\r\n iva = redondear(calcularIVA(request.items, incluyeIva));\r\n total = redondear(calcularTotal(request.items, incluyeIva));\r\n }\r\n\r\n if (total <= 0) {\r\n throw new ArcaValidationError('El monto total debe ser mayor a 0');\r\n }\r\n\r\n // 3. Preparar request SOAP\r\n const soapRequest = this.buildCAESolicitarRequest({\r\n tipo: request.tipo,\r\n puntoVenta: this.config.puntoVenta,\r\n nroComprobante,\r\n concepto: request.concepto,\r\n fecha: request.fecha || new Date(),\r\n comprador: request.comprador,\r\n subtotal,\r\n iva,\r\n total,\r\n ivaData: request.ivaData,\r\n });\r\n\r\n // 4. Enviar a ARCA\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECAESolicitar',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(\r\n `Error HTTP al comunicarse con WSFE: ${response.status}`,\r\n 'HTTP_ERROR',\r\n { status: response.status }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 5. Parsear respuesta CAE\r\n const result = await this.parseCAEResponse(responseXml);\r\n\r\n return {\r\n ...result,\r\n items: request.items,\r\n iva: request.ivaData,\r\n };\r\n }\r\n\r\n /**\r\n * Obtiene el próximo número de comprobante disponible\r\n */\r\n private async obtenerProximoNumero(tipo: TipoComprobante): Promise<number> {\r\n const soapRequest = this.buildProximoNumeroRequest(tipo);\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await fetch(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECompUltimoAutorizado',\r\n },\r\n body: soapRequest,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(`Error HTTP al consultar próximo número: ${response.status}`, 'HTTP_ERROR');\r\n }\r\n\r\n const responseXml = await response.text();\r\n const result = parseXml(responseXml);\r\n\r\n const data = result?.Envelope?.Body?.FECompUltimoAutorizadoResponse?.FECompUltimoAutorizadoResult;\r\n\r\n if (data?.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const nro = data?.CbteNro;\r\n return typeof nro === 'number' ? nro + 1 : 1;\r\n }\r\n\r\n private buildCAESolicitarRequest(params: {\r\n tipo: TipoComprobante;\r\n puntoVenta: number;\r\n nroComprobante: number;\r\n concepto: Concepto;\r\n fecha: Date;\r\n comprador?: EmitirFacturaRequest['comprador'];\r\n subtotal: number;\r\n iva: number;\r\n total: number;\r\n ivaData?: EmitirFacturaRequest['ivaData'];\r\n }): string {\r\n const fechaStr = params.fecha.toISOString().split('T')[0].replace(/-/g, '');\r\n\r\n let ivaXml = '';\r\n if (params.ivaData && params.ivaData.length > 0) {\r\n ivaXml = '<ar:Iva>';\r\n params.ivaData.forEach(aliquot => {\r\n ivaXml += `\r\n <ar:AlicIva>\r\n <ar:Id>${this.getCodigoAlicuota(aliquot.alicuota)}</ar:Id>\r\n <ar:BaseImp>${aliquot.baseImponible.toFixed(2)}</ar:BaseImp>\r\n <ar:Importe>${aliquot.importe.toFixed(2)}</ar:Importe>\r\n </ar:AlicIva>`;\r\n });\r\n ivaXml += '\\n </ar:Iva>';\r\n }\r\n\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECAESolicitar>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:FeCAEReq>\r\n <ar:FeCabReq>\r\n <ar:CantReg>1</ar:CantReg>\r\n <ar:PtoVta>${params.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${params.tipo}</ar:CbteTipo>\r\n </ar:FeCabReq>\r\n <ar:FeDetReq>\r\n <ar:FECAEDetRequest>\r\n <ar:Concepto>${params.concepto}</ar:Concepto>\r\n <ar:DocTipo>${params.comprador?.tipoDocumento || 99}</ar:DocTipo>\r\n <ar:DocNro>${params.comprador?.nroDocumento || 0}</ar:DocNro>\r\n <ar:CbteDesde>${params.nroComprobante}</ar:CbteDesde>\r\n <ar:CbteHasta>${params.nroComprobante}</ar:CbteHasta>\r\n <ar:CbteFch>${fechaStr}</ar:CbteFch>\r\n <ar:ImpTotal>${params.total.toFixed(2)}</ar:ImpTotal>\r\n <ar:ImpTotConc>0.00</ar:ImpTotConc>\r\n <ar:ImpNeto>${params.subtotal.toFixed(2)}</ar:ImpNeto>\r\n <ar:ImpOpEx>0.00</ar:ImpOpEx>\r\n <ar:ImpIVA>${params.iva.toFixed(2)}</ar:ImpIVA>\r\n <ar:ImpTrib>0.00</ar:ImpTrib>\r\n <ar:MonId>PES</ar:MonId>\r\n <ar:MonCotiz>1</ar:MonCotiz>\r\n ${ivaXml}\r\n </ar:FECAEDetRequest>\r\n </ar:FeDetReq>\r\n </ar:FeCAEReq>\r\n </ar:FECAESolicitar>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Mapea alícuota % a código ARCA\r\n */\r\n private getCodigoAlicuota(porcentaje: number): number {\r\n const mapa: Record<number, number> = {\r\n 0: 3,\r\n 10.5: 4,\r\n 21: 5,\r\n 27: 6,\r\n };\r\n\r\n const codigo = mapa[porcentaje];\r\n if (!codigo) {\r\n throw new ArcaValidationError(\r\n `Alícuota IVA inválida: ${porcentaje}%`,\r\n {\r\n alicuotasValidas: [0, 10.5, 21, 27],\r\n hint: 'Usar una de las alícuotas oficiales de Argentina'\r\n }\r\n );\r\n }\r\n\r\n return codigo;\r\n }\r\n\r\n private buildProximoNumeroRequest(tipo: TipoComprobante): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECompUltimoAutorizado>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:PtoVta>${this.config.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${tipo}</ar:CbteTipo>\r\n </ar:FECompUltimoAutorizado>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n private async parseCAEResponse(xml: string): Promise<CAEResponse> {\r\n const result = parseXml(xml);\r\n const data = result?.Envelope?.Body?.FECAESolicitarResponse?.FECAESolicitarResult;\r\n\r\n if (!data) {\r\n throw new ArcaError('Respuesta WSFE inválida: estructura no reconocida', 'PARSE_ERROR', { xml });\r\n }\r\n\r\n if (data.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const cab = data.FeCabResp;\r\n const det = Array.isArray(data.FeDetResp.FECAEDetResponse)\r\n ? data.FeDetResp.FECAEDetResponse[0]\r\n : data.FeDetResp.FECAEDetResponse;\r\n\r\n if (!det) {\r\n throw new ArcaError('Respuesta WSFE incompleta: falta detalle del comprobante', 'PARSE_ERROR');\r\n }\r\n\r\n const observaciones: string[] = [];\r\n if (det.Observaciones) {\r\n const obsArray = Array.isArray(det.Observaciones.Obs)\r\n ? det.Observaciones.Obs\r\n : [det.Observaciones.Obs];\r\n obsArray.forEach((o: any) => observaciones.push(o.Msg));\r\n }\r\n\r\n return {\r\n tipoComprobante: cab.CbteTipo,\r\n puntoVenta: cab.PtoVta,\r\n nroComprobante: det.CbteDesde,\r\n fecha: det.CbteFch,\r\n cae: det.CAE,\r\n vencimientoCae: det.CAEFchVto,\r\n resultado: det.Resultado,\r\n observaciones: observaciones.length > 0 ? observaciones : undefined,\r\n };\r\n }\r\n}\r\n","import type { CAEResponse, Comprador } from '../types/wsfe';\r\nimport { TipoDocumento } from '../types/wsfe';\r\n\r\n/**\r\n * Interfaz de los datos requeridos por AFIP para el QR\r\n * @see https://www.afip.gob.ar/fe/qr/especificaciones.asp\r\n */\r\ninterface AFIPQRData {\r\n ver: number;\r\n fecha: string;\r\n cuit: number;\r\n ptoVta: number;\r\n tipoCmp: number;\r\n nroCmp: number;\r\n importe: number;\r\n moneda: string;\r\n ctz: number;\r\n tipoDocRec: number;\r\n nroDocRec: number;\r\n tipoCodAut: string;\r\n codAut: number;\r\n}\r\n\r\n/**\r\n * Genera la URL completa con el código QR para un comprobante emitido\r\n * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (11 dígitos, sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional, si no se pasa asume Consumidor Final)\r\n * @returns La URL lista para embeber en un generador de QR\r\n */\r\nexport function generarUrlQR(\r\n caeResponse: CAEResponse,\r\n cuitEmisor: string,\r\n total: number,\r\n comprador?: Comprador\r\n): string {\r\n // 1. Formatear fecha a YYYY-MM-DD (AFIP la devuelve como YYYYMMDD)\r\n const fDate = caeResponse.fecha;\r\n const fechaFormat = fDate.length === 8\r\n ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}`\r\n : fDate;\r\n\r\n // 2. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? parseInt(comprador.nroDocumento, 10) : 0;\r\n\r\n // 3. Armar objeto JSON\r\n const qrData: AFIPQRData = {\r\n ver: 1, // Versión estándar exigida por AFIP\r\n fecha: fechaFormat,\r\n cuit: parseInt(cuitEmisor, 10),\r\n ptoVta: caeResponse.puntoVenta,\r\n tipoCmp: caeResponse.tipoComprobante,\r\n nroCmp: caeResponse.nroComprobante,\r\n importe: parseFloat(total.toFixed(2)),\r\n moneda: 'PES', // Por defecto PES (Pesos Argentinos)\r\n ctz: 1, // Cotización (siempre 1 para PES)\r\n tipoDocRec: docTipo,\r\n nroDocRec: docNro,\r\n tipoCodAut: 'E', // 'E' para comprobantes electrónicos\r\n codAut: parseInt(caeResponse.cae, 10)\r\n };\r\n\r\n // 4. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrData);\r\n\r\n // Uso robusto de Buffer para evitar problemas de btoa en Node/Edge\r\n let base64 = typeof Buffer !== 'undefined'\r\n ? Buffer.from(jsonString).toString('base64')\r\n : btoa(jsonString); // Fallback para navegadores\r\n\r\n // 5. Retornar URL lista\r\n return `https://www.afip.gob.ar/fe/qr/?p=${base64}`;\r\n}\r\n"],"mappings":"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","names":["https","TipoComprobante","Concepto","TipoDocumento"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -13,6 +13,8 @@ interface ArcaConfig {
|
|
|
13
13
|
environment: Environment;
|
|
14
14
|
/** CUIT del contribuyente (11 dígitos sin guiones) */
|
|
15
15
|
cuit: string;
|
|
16
|
+
/** Tiempo de espera para peticiones (ms). Defecto: 15000 */
|
|
17
|
+
timeout?: number;
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
20
|
* Error personalizado de ARCA SDK
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ interface ArcaConfig {
|
|
|
13
13
|
environment: Environment;
|
|
14
14
|
/** CUIT del contribuyente (11 dígitos sin guiones) */
|
|
15
15
|
cuit: string;
|
|
16
|
+
/** Tiempo de espera para peticiones (ms). Defecto: 15000 */
|
|
17
|
+
timeout?: number;
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
20
|
* Error personalizado de ARCA SDK
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,12 @@ var ArcaValidationError = class extends ArcaError {
|
|
|
35
35
|
this.name = "ArcaValidationError";
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
+
var ArcaNetworkError = class extends ArcaError {
|
|
39
|
+
constructor(message, details) {
|
|
40
|
+
super(message, "NETWORK_ERROR", details);
|
|
41
|
+
this.name = "ArcaNetworkError";
|
|
42
|
+
}
|
|
43
|
+
};
|
|
38
44
|
|
|
39
45
|
// src/utils/xml.ts
|
|
40
46
|
import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
@@ -201,21 +207,47 @@ var TicketManager = class {
|
|
|
201
207
|
// src/utils/network.ts
|
|
202
208
|
import https from "https";
|
|
203
209
|
async function callArcaApi(url, options) {
|
|
210
|
+
const timeout = options.timeout || 15e3;
|
|
204
211
|
const isNode = typeof process !== "undefined" && process.versions && process.versions.node;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
const controller = new AbortController();
|
|
213
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
214
|
+
try {
|
|
215
|
+
if (isNode) {
|
|
216
|
+
const agent = new https.Agent({
|
|
217
|
+
// Permitir llaves DH de 1024 bits (AFIP) bajando a Security Level 1
|
|
218
|
+
// solo para esta conexión específica.
|
|
219
|
+
ciphers: "DEFAULT@SECLEVEL=1",
|
|
220
|
+
rejectUnauthorized: true
|
|
221
|
+
});
|
|
222
|
+
return await fetch(url, {
|
|
223
|
+
method: options.method,
|
|
224
|
+
headers: options.headers,
|
|
225
|
+
body: options.body,
|
|
226
|
+
agent,
|
|
227
|
+
signal: controller.signal
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
return await fetch(url, {
|
|
231
|
+
method: options.method,
|
|
232
|
+
headers: options.headers,
|
|
233
|
+
body: options.body,
|
|
234
|
+
signal: controller.signal
|
|
212
235
|
});
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (error.name === "AbortError") {
|
|
238
|
+
throw new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`);
|
|
239
|
+
}
|
|
240
|
+
let message = error.message;
|
|
241
|
+
if (message.includes("dh key too small")) {
|
|
242
|
+
message = "Error SSL de ARCA (DH Key too small). El SDK intent\xF3 mitigarlo pero fall\xF3. Verifique su versi\xF3n de Node.js.";
|
|
243
|
+
}
|
|
244
|
+
throw new ArcaNetworkError(`Error de red al comunicarse con ARCA: ${message}`, {
|
|
245
|
+
url,
|
|
246
|
+
originalError: error
|
|
216
247
|
});
|
|
248
|
+
} finally {
|
|
249
|
+
clearTimeout(timeoutId);
|
|
217
250
|
}
|
|
218
|
-
return fetch(url, options);
|
|
219
251
|
}
|
|
220
252
|
|
|
221
253
|
// src/auth/wsaa.ts
|
|
@@ -292,7 +324,8 @@ var WsaaService = class {
|
|
|
292
324
|
"Content-Type": "text/xml; charset=utf-8",
|
|
293
325
|
"SOAPAction": ""
|
|
294
326
|
},
|
|
295
|
-
body: this.buildSoapRequest(cms)
|
|
327
|
+
body: this.buildSoapRequest(cms),
|
|
328
|
+
timeout: this.config.timeout
|
|
296
329
|
});
|
|
297
330
|
if (!response.ok) {
|
|
298
331
|
throw new ArcaAuthError(
|
|
@@ -581,7 +614,8 @@ var WsfeService = class {
|
|
|
581
614
|
"Content-Type": "text/xml; charset=utf-8",
|
|
582
615
|
"SOAPAction": "http://ar.gov.afip.dif.FEV1/FECAESolicitar"
|
|
583
616
|
},
|
|
584
|
-
body: soapRequest
|
|
617
|
+
body: soapRequest,
|
|
618
|
+
timeout: this.config.timeout
|
|
585
619
|
});
|
|
586
620
|
if (!response.ok) {
|
|
587
621
|
throw new ArcaError(
|
|
@@ -794,3 +828,4 @@ export {
|
|
|
794
828
|
WsfeService,
|
|
795
829
|
generarUrlQR
|
|
796
830
|
};
|
|
831
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants/endpoints.ts","../src/types/common.ts","../src/utils/xml.ts","../src/utils/crypto.ts","../src/auth/ticket.ts","../src/utils/network.ts","../src/auth/wsaa.ts","../src/types/wsfe.ts","../src/utils/calculations.ts","../src/services/wsfe.ts","../src/utils/qr.ts"],"sourcesContent":["import type { Environment } from '../types/common';\r\n\r\n/**\r\n * URLs de los servicios ARCA por ambiente\r\n */\r\nexport const WSAA_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wsaahomo.afip.gov.ar/ws/services/LoginCms',\r\n produccion: 'https://wsaa.afip.gov.ar/ws/services/LoginCms',\r\n};\r\n\r\n/**\r\n * URLs del servicio WSFE por ambiente\r\n */\r\nexport const WSFE_ENDPOINTS: Record<Environment, string> = {\r\n homologacion: 'https://wswhomo.afip.gov.ar/wsfev1/service.asmx',\r\n produccion: 'https://servicios1.afip.gov.ar/wsfev1/service.asmx',\r\n};\r\n\r\n/**\r\n * Obtener endpoint WSAA según ambiente\r\n */\r\nexport function getWsaaEndpoint(environment: Environment): string {\r\n return WSAA_ENDPOINTS[environment];\r\n}\r\n\r\n/**\r\n * Obtener endpoint WSFE según ambiente\r\n */\r\nexport function getWsfeEndpoint(environment: Environment): string {\r\n return WSFE_ENDPOINTS[environment];\r\n}\r\n","/**\r\n * Tipos comunes compartidos en toda la SDK\r\n */\r\n\r\n/**\r\n * Ambiente de ejecución ARCA\r\n */\r\nexport type Environment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Configuración base para servicios ARCA\r\n */\r\nexport interface ArcaConfig {\r\n /** Ambiente (homologación o producción) */\r\n environment: Environment;\r\n /** CUIT del contribuyente (11 dígitos sin guiones) */\r\n cuit: string;\r\n /** Tiempo de espera para peticiones (ms). Defecto: 15000 */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Error personalizado de ARCA SDK\r\n */\r\nexport class ArcaError extends Error {\r\n constructor(\r\n message: string,\r\n public code: string,\r\n public details?: unknown\r\n ) {\r\n super(message);\r\n this.name = 'ArcaError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de autenticación WSAA\r\n */\r\nexport class ArcaAuthError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'AUTH_ERROR', details);\r\n this.name = 'ArcaAuthError';\r\n }\r\n}\r\n\r\n/**\r\n * Error de validación de input\r\n */\r\nexport class ArcaValidationError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'VALIDATION_ERROR', details);\r\n this.name = 'ArcaValidationError';\r\n }\r\n}\r\n/**\r\n * Error de comunicación/red\r\n */\r\nexport class ArcaNetworkError extends ArcaError {\r\n constructor(message: string, details?: unknown) {\r\n super(message, 'NETWORK_ERROR', details);\r\n this.name = 'ArcaNetworkError';\r\n }\r\n}\r\n","import { XMLBuilder, XMLParser } from 'fast-xml-parser';\r\nimport { ArcaAuthError } from '../types/common';\r\nimport type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Genera el XML TRA (Ticket de Requerimiento de Acceso)\r\n * \r\n * @param service - Servicio ARCA (ej: 'wsfe')\r\n * @param cuit - CUIT del contribuyente\r\n * @returns XML del TRA\r\n */\r\nexport function buildTRA(service: string, cuit: string): string {\r\n const now = new Date();\r\n const expirationTime = new Date(now.getTime() + 12 * 60 * 60 * 1000); // +12 horas\r\n\r\n const tra = {\r\n loginTicketRequest: {\r\n '@_version': '1.0',\r\n header: {\r\n uniqueId: Math.floor(now.getTime() / 1000),\r\n generationTime: now.toISOString(),\r\n expirationTime: expirationTime.toISOString(),\r\n },\r\n service,\r\n },\r\n };\r\n\r\n const builder = new XMLBuilder({\r\n ignoreAttributes: false,\r\n format: true,\r\n });\r\n\r\n const xml = builder.build(tra);\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n${xml}`;\r\n}\r\n\r\n/**\r\n * Parsea la respuesta XML de WSAA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Ticket de login\r\n */\r\nexport function parseWsaaResponse(xml: string): LoginTicket {\r\n try {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true, // Sugerencia: más robusto contra cambios de prefijos soapenv\r\n });\r\n\r\n const result = parser.parse(xml);\r\n\r\n // Navegar estructura XML de respuesta WSAA\r\n // Gracias a removeNSPrefix: true, evitamos depender de 'soapenv:'\r\n const credentials = result?.Envelope?.Body?.loginCmsResponse?.loginCmsReturn;\r\n\r\n if (!credentials) {\r\n // Intentar detectar error de ARCA en respuesta\r\n const fault = result?.Envelope?.Body?.Fault;\r\n if (fault) {\r\n throw new ArcaAuthError(\r\n `Error ARCA: ${fault.faultstring || 'Error desconocido'}`,\r\n { faultCode: fault.faultcode, detail: fault.detail }\r\n );\r\n }\r\n\r\n throw new ArcaAuthError(\r\n 'Respuesta WSAA inválida: estructura no reconocida',\r\n { receivedStructure: result }\r\n );\r\n }\r\n\r\n // fast-xml-parser con removeNSPrefix puede dejar los campos limpios\r\n return {\r\n token: credentials.token,\r\n sign: credentials.sign,\r\n generationTime: new Date(credentials.header.generationTime),\r\n expirationTime: new Date(credentials.header.expirationTime),\r\n };\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al parsear respuesta WSAA',\r\n { originalError: error }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Parsea un XML genérico de ARCA\r\n * \r\n * @param xml - XML de respuesta\r\n * @returns Objeto parseado\r\n */\r\nexport function parseXml(xml: string): any {\r\n const parser = new XMLParser({\r\n ignoreAttributes: false,\r\n parseAttributeValue: true,\r\n removeNSPrefix: true,\r\n });\r\n return parser.parse(xml);\r\n}\r\n\r\n/**\r\n * Valida CUIT (11 dígitos sin guiones)\r\n */\r\nexport function validateCUIT(cuit: string): boolean {\r\n return /^\\d{11}$/.test(cuit);\r\n}\r\n","import * as forge from 'node-forge';\r\nimport { ArcaAuthError } from '../types/common';\r\n\r\n/**\r\n * Firma un XML en formato CMS (PKCS#7) usando certificado y clave privada\r\n * \r\n * @param xml - Contenido XML a firmar (TRA)\r\n * @param certPem - Certificado en formato PEM\r\n * @param keyPem - Clave privada en formato PEM\r\n * @returns CMS firmado en base64\r\n */\r\nexport function signCMS(xml: string, certPem: string, keyPem: string): string {\r\n try {\r\n // 1. Parsear certificado\r\n const cert = forge.pki.certificateFromPem(certPem);\r\n\r\n // 2. Parsear clave privada\r\n const privateKey = forge.pki.privateKeyFromPem(keyPem);\r\n\r\n // 3. Crear contenedor PKCS#7\r\n const p7 = forge.pkcs7.createSignedData();\r\n\r\n // 4. Agregar contenido a firmar\r\n p7.content = forge.util.createBuffer(xml, 'utf8');\r\n\r\n // 5. Agregar certificado\r\n p7.addCertificate(cert);\r\n\r\n // 6. Firmar con SHA256\r\n p7.addSigner({\r\n key: privateKey,\r\n certificate: cert,\r\n digestAlgorithm: forge.pki.oids.sha256,\r\n authenticatedAttributes: [\r\n {\r\n type: forge.pki.oids.contentType,\r\n value: forge.pki.oids.data,\r\n },\r\n {\r\n type: forge.pki.oids.messageDigest,\r\n // El valor será calculado automáticamente\r\n },\r\n {\r\n type: forge.pki.oids.signingTime,\r\n // node-forge expects a Date object, but its typings might be missing or expect string/any\r\n value: new Date() as any,\r\n },\r\n ],\r\n });\r\n\r\n // 7. Firmar\r\n p7.sign();\r\n\r\n // 8. Convertir a DER y luego a base64\r\n const derBytes = forge.asn1.toDer(p7.toAsn1()).getBytes();\r\n const base64 = forge.util.encode64(derBytes);\r\n\r\n return base64;\r\n } catch (error) {\r\n if (error instanceof ArcaAuthError) throw error;\r\n throw new ArcaAuthError(\r\n 'Error al firmar XML con certificado usando PKCS#7',\r\n {\r\n originalError: error,\r\n hint: 'Verificar que el certificado y la clave privada sean válidos y correspondan entre sí'\r\n }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Valida formato de certificado PEM\r\n */\r\nexport function validateCertificate(cert: string): boolean {\r\n return cert.includes('-----BEGIN CERTIFICATE-----') &&\r\n cert.includes('-----END CERTIFICATE-----');\r\n}\r\n\r\n/**\r\n * Valida formato de clave privada PEM\r\n */\r\nexport function validatePrivateKey(key: string): boolean {\r\n return (\r\n (key.includes('-----BEGIN PRIVATE KEY-----') &&\r\n key.includes('-----END PRIVATE KEY-----')) ||\r\n (key.includes('-----BEGIN RSA PRIVATE KEY-----') &&\r\n key.includes('-----END RSA PRIVATE KEY-----'))\r\n );\r\n}\r\n","import type { LoginTicket } from '../types/wsaa';\r\n\r\n/**\r\n * Gestor de tickets de autenticación\r\n * Maneja cache en memoria del ticket WSAA\r\n */\r\nexport class TicketManager {\r\n private ticket: LoginTicket | null = null;\r\n\r\n /**\r\n * Guarda un ticket en cache\r\n */\r\n setTicket(ticket: LoginTicket): void {\r\n this.ticket = ticket;\r\n }\r\n\r\n /**\r\n * Obtiene el ticket actual si es válido\r\n * @returns Ticket válido o null si expiró\r\n */\r\n getTicket(): LoginTicket | null {\r\n if (!this.ticket) return null;\r\n\r\n const now = new Date();\r\n const expiresIn = this.ticket.expirationTime.getTime() - now.getTime();\r\n\r\n // Si expira en menos de 5 minutos, considerarlo inválido\r\n const BUFFER_MS = 5 * 60 * 1000;\r\n\r\n if (expiresIn < BUFFER_MS) {\r\n this.ticket = null;\r\n return null;\r\n }\r\n\r\n return this.ticket;\r\n }\r\n\r\n /**\r\n * Verifica si hay un ticket válido\r\n */\r\n hasValidTicket(): boolean {\r\n return this.getTicket() !== null;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache\r\n */\r\n clearTicket(): void {\r\n this.ticket = null;\r\n }\r\n}\r\n","import https from 'https';\r\nimport { ArcaNetworkError } from '../types/common';\r\n\r\n/**\r\n * Entornos compatibles\r\n */\r\nexport type ArcaEnvironment = 'homologacion' | 'produccion';\r\n\r\n/**\r\n * Opciones para la llamada API\r\n */\r\nexport interface CallApiOptions {\r\n method: string;\r\n headers: Record<string, string>;\r\n body: string;\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Realiza una llamada a la API de ARCA (WSAA o WSFE)\r\n * Maneja la compatibilidad SSL con los servidores de AFIP (DH key size)\r\n * y añade robustez (timeouts, mejores errores).\r\n */\r\nexport async function callArcaApi(\r\n url: string,\r\n options: CallApiOptions\r\n): Promise<Response> {\r\n const timeout = options.timeout || 15000; // 15 segundos por defecto\r\n\r\n // Si estamos en Node.js, necesitamos usar un agente HTTPS que permita SECLEVEL=1\r\n // para evitar el error \"dh key too small\" de AFIP (OpenSSL 3.0+)\r\n const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;\r\n\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n if (isNode) {\r\n const agent = new https.Agent({\r\n // Permitir llaves DH de 1024 bits (AFIP) bajando a Security Level 1\r\n // solo para esta conexión específica.\r\n ciphers: 'DEFAULT@SECLEVEL=1',\r\n rejectUnauthorized: true,\r\n });\r\n\r\n // @ts-ignore - node-fetch o fetch nativo en Node 18+ aceptan el agente\r\n return await fetch(url, {\r\n method: options.method,\r\n headers: options.headers,\r\n body: options.body,\r\n agent,\r\n signal: controller.signal\r\n } as any);\r\n }\r\n\r\n return await fetch(url, {\r\n method: options.method,\r\n headers: options.headers,\r\n body: options.body,\r\n signal: controller.signal\r\n });\r\n } catch (error: any) {\r\n if (error.name === 'AbortError') {\r\n throw new ArcaNetworkError(`Tiempo de espera agotado (${timeout}ms) al conectar con ARCA: ${url}`);\r\n }\r\n\r\n // Mapear errores de SSL comunes para dar pistas claras al desarrollador\r\n let message = error.message;\r\n if (message.includes('dh key too small')) {\r\n message = 'Error SSL de ARCA (DH Key too small). El SDK intentó mitigarlo pero falló. Verifique su versión de Node.js.';\r\n }\r\n\r\n throw new ArcaNetworkError(`Error de red al comunicarse con ARCA: ${message}`, {\r\n url,\r\n originalError: error\r\n });\r\n } finally {\r\n clearTimeout(timeoutId);\r\n }\r\n}\r\n","import { getWsaaEndpoint } from '../constants/endpoints';\r\nimport { ArcaAuthError, ArcaValidationError } from '../types/common';\r\nimport type { WsaaConfig, LoginTicket } from '../types/wsaa';\r\nimport { buildTRA, parseWsaaResponse, validateCUIT } from '../utils/xml';\r\nimport { validateCertificate, validatePrivateKey, signCMS } from '../utils/crypto';\r\nimport { TicketManager } from './ticket';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de autenticación WSAA (Web Service de Autenticación y Autorización)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsaa = new WsaaService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * cert: fs.readFileSync('cert.pem', 'utf-8'),\r\n * key: fs.readFileSync('key.pem', 'utf-8'),\r\n * service: 'wsfe',\r\n * });\r\n * \r\n * const ticket = await wsaa.login();\r\n * console.log('Token:', ticket.token);\r\n * ```\r\n */\r\nexport class WsaaService {\r\n private config: WsaaConfig;\r\n private ticketManager: TicketManager;\r\n\r\n constructor(config: WsaaConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n this.ticketManager = new TicketManager();\r\n }\r\n\r\n /**\r\n * Valida la configuración\r\n */\r\n private validateConfig(config: WsaaConfig): void {\r\n if (!validateCUIT(config.cuit)) {\r\n throw new ArcaValidationError(\r\n 'CUIT inválido: debe tener 11 dígitos sin guiones',\r\n { cuit: config.cuit }\r\n );\r\n }\r\n\r\n if (!validateCertificate(config.cert)) {\r\n throw new ArcaValidationError(\r\n 'Certificado inválido: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN CERTIFICATE-----' }\r\n );\r\n }\r\n\r\n if (!validatePrivateKey(config.key)) {\r\n throw new ArcaValidationError(\r\n 'Clave privada inválida: debe estar en formato PEM',\r\n { hint: 'Debe contener -----BEGIN PRIVATE KEY----- o -----BEGIN RSA PRIVATE KEY-----' }\r\n );\r\n }\r\n\r\n if (!config.service || config.service.trim() === '') {\r\n throw new ArcaValidationError(\r\n 'Servicio ARCA no especificado',\r\n { hint: 'Ejemplos: \"wsfe\", \"wsmtxca\"' }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Obtiene un ticket de acceso válido\r\n * Usa cache si el ticket actual es válido\r\n * \r\n * @returns Ticket de acceso\r\n */\r\n async login(): Promise<LoginTicket> {\r\n // Intentar usar ticket en cache\r\n const cachedTicket = this.ticketManager.getTicket();\r\n if (cachedTicket) {\r\n return cachedTicket;\r\n }\r\n\r\n // Generar nuevo ticket\r\n const ticket = await this.requestNewTicket();\r\n this.ticketManager.setTicket(ticket);\r\n return ticket;\r\n }\r\n\r\n /**\r\n * Solicita un nuevo ticket a WSAA\r\n */\r\n private async requestNewTicket(): Promise<LoginTicket> {\r\n // 1. Generar TRA (Ticket de Requerimiento de Acceso)\r\n const tra = buildTRA(this.config.service, this.config.cuit);\r\n\r\n // 2. Firmar TRA con certificado (genera CMS)\r\n let cms: string;\r\n try {\r\n cms = signCMS(tra, this.config.cert, this.config.key);\r\n } catch (error) {\r\n throw new ArcaAuthError(\r\n 'Error al firmar TRA con certificado',\r\n { originalError: error }\r\n );\r\n }\r\n\r\n // 3. Enviar CMS a WSAA\r\n const endpoint = getWsaaEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': '',\r\n },\r\n body: this.buildSoapRequest(cms),\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaAuthError(\r\n `Error HTTP al comunicarse con WSAA: ${response.status} ${response.statusText}`,\r\n { status: response.status, statusText: response.statusText }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 4. Parsear respuesta\r\n return parseWsaaResponse(responseXml);\r\n }\r\n\r\n /**\r\n * Construye el SOAP request para WSAA\r\n */\r\n private buildSoapRequest(cms: string): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:wsaa=\"http://wsaa.view.sua.dvadac.desein.afip.gov\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <wsaa:loginCms>\r\n <wsaa:in0>${cms}</wsaa:in0>\r\n </wsaa:loginCms>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Limpia el ticket en cache (forzar renovación)\r\n */\r\n clearCache(): void {\r\n this.ticketManager.clearTicket();\r\n }\r\n}\r\n","import type { ArcaConfig } from './common';\r\nimport type { LoginTicket } from './wsaa';\r\n\r\n/**\r\n * Configuración para WSFE\r\n */\r\nexport interface WsfeConfig extends ArcaConfig {\r\n /** Ticket de autenticación WSAA */\r\n ticket: LoginTicket;\r\n /** Punto de venta (4 dígitos) */\r\n puntoVenta: number;\r\n}\r\n\r\n/**\r\n * Tipo de comprobante\r\n */\r\nexport enum TipoComprobante {\r\n FACTURA_A = 1,\r\n FACTURA_B = 6,\r\n FACTURA_C = 11,\r\n TICKET_A = 81,\r\n TICKET_B = 82,\r\n TICKET_C = 83,\r\n}\r\n\r\n/**\r\n * Concepto de facturación\r\n */\r\nexport enum Concepto {\r\n PRODUCTOS = 1, // Productos\r\n SERVICIOS = 2, // Servicios\r\n PRODUCTOS_Y_SERVICIOS = 3, // Mixto\r\n}\r\n\r\n/**\r\n * Tipo de documento del cliente\r\n */\r\nexport enum TipoDocumento {\r\n CUIT = 80,\r\n CUIL = 86,\r\n CDI = 87,\r\n LE = 89,\r\n LC = 90,\r\n CI_EXTRANJERA = 91,\r\n PASAPORTE = 94,\r\n CI_BUENOS_AIRES = 95,\r\n CI_POLICIA_FEDERAL = 96,\r\n DNI = 96,\r\n CONSUMIDOR_FINAL = 99, // Sin documento\r\n}\r\n\r\n/**\r\n * Item de factura\r\n */\r\nexport interface FacturaItem {\r\n /** Descripción del producto/servicio */\r\n descripcion: string;\r\n /** Cantidad */\r\n cantidad: number;\r\n /** Precio unitario */\r\n precioUnitario: number;\r\n /** IVA % (0, 10.5, 21, 27) */\r\n alicuotaIva?: number;\r\n}\r\n\r\n/**\r\n * Datos del comprador\r\n */\r\nexport interface Comprador {\r\n /** Tipo de documento */\r\n tipoDocumento: TipoDocumento;\r\n /** Número de documento (sin guiones) */\r\n nroDocumento: string;\r\n}\r\n\r\n/**\r\n * Request para emitir factura\r\n */\r\nexport interface EmitirFacturaRequest {\r\n /** Tipo de comprobante */\r\n tipo: TipoComprobante;\r\n /** Concepto */\r\n concepto: Concepto;\r\n /** Comprador (opcional para Factura C consumidor final) */\r\n comprador?: Comprador;\r\n /** Items de la factura (calculan el total si se proveen) */\r\n items?: FacturaItem[];\r\n /** Monto total (requerido si no hay items) */\r\n total?: number;\r\n /** Desglose de IVA (requerido para Factura B/A) */\r\n ivaData?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n /** Indica si los preciosUnitarios de los items YA incluyen el IVA (Precio Final). Defecto: false */\r\n incluyeIva?: boolean;\r\n /** Fecha del comprobante (default: hoy) */\r\n fecha?: Date;\r\n}\r\n\r\n/**\r\n * Respuesta CAE (Código de Autorización Electrónico)\r\n */\r\nexport interface CAEResponse {\r\n /** Tipo de comprobante */\r\n tipoComprobante: number;\r\n /** Punto de venta */\r\n puntoVenta: number;\r\n /** Número de comprobante */\r\n nroComprobante: number;\r\n /** Fecha de emisión */\r\n fecha: string;\r\n /** CAE asignado */\r\n cae: string;\r\n /** Fecha de vencimiento del CAE */\r\n vencimientoCae: string;\r\n /** Resultado (A = Aprobado, R = Rechazado) */\r\n resultado: 'A' | 'R';\r\n /** Observaciones de ARCA (si hay) */\r\n observaciones?: string[];\r\n /** Items (se retornan si fueron proveídos en el request) */\r\n items?: FacturaItem[];\r\n /** Desglose IVA (solo para Factura B/A) */\r\n iva?: {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[];\r\n}\r\n","import type { FacturaItem } from '../types/wsfe';\r\n\r\n/**\r\n * Calcula el subtotal de items (sin IVA)\r\n */\r\nexport function calcularSubtotal(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && item.alicuotaIva) {\r\n precioNeto = item.precioUnitario / (1 + (item.alicuotaIva / 100));\r\n }\r\n return sum + (item.cantidad * precioNeto);\r\n }, 0);\r\n}\r\n\r\n\r\n/**\r\n * Calcula el IVA total de items\r\n */\r\nexport function calcularIVA(items: FacturaItem[], incluyeIva = false): number {\r\n return items.reduce((sum, item) => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n const subtotalNeto = item.cantidad * precioNeto;\r\n return sum + (subtotalNeto * alicuota / 100);\r\n }, 0);\r\n}\r\n\r\n/**\r\n * Calcula el total de la factura (subtotal + IVA)\r\n */\r\nexport function calcularTotal(items: FacturaItem[], incluyeIva = false): number {\r\n if (incluyeIva) {\r\n return items.reduce((sum, item) => sum + (item.cantidad * item.precioUnitario), 0);\r\n }\r\n const subtotal = calcularSubtotal(items, false);\r\n const iva = calcularIVA(items, false);\r\n return subtotal + iva;\r\n}\r\n\r\n/**\r\n * Redondea a 2 decimales\r\n */\r\nexport function redondear(valor: number): number {\r\n return Math.round(valor * 100) / 100;\r\n}\r\n","import { getWsfeEndpoint } from '../constants/endpoints';\r\nimport { ArcaError, ArcaValidationError } from '../types/common';\r\nimport type {\r\n WsfeConfig,\r\n EmitirFacturaRequest,\r\n CAEResponse,\r\n FacturaItem,\r\n Comprador,\r\n} from '../types/wsfe';\r\nimport {\r\n TipoComprobante,\r\n Concepto,\r\n TipoDocumento\r\n} from '../types/wsfe';\r\nimport {\r\n calcularSubtotal,\r\n calcularIVA,\r\n calcularTotal,\r\n redondear,\r\n} from '../utils/calculations';\r\nimport { parseXml } from '../utils/xml';\r\nimport { callArcaApi } from '../utils/network';\r\n\r\n/**\r\n * Servicio de Facturación Electrónica (WSFE v1)\r\n * \r\n * @example\r\n * ```typescript\r\n * const wsfe = new WsfeService({\r\n * environment: 'homologacion',\r\n * cuit: '20123456789',\r\n * ticket: await wsaa.login(),\r\n * puntoVenta: 4,\r\n * });\r\n * \r\n * const cae = await wsfe.emitirFacturaC({\r\n * items: [\r\n * { descripcion: 'Producto 1', cantidad: 2, precioUnitario: 100 }\r\n * ]\r\n * });\r\n * \r\n * console.log('CAE:', cae.cae);\r\n * ```\r\n */\r\nexport class WsfeService {\r\n private config: WsfeConfig;\r\n\r\n constructor(config: WsfeConfig) {\r\n this.validateConfig(config);\r\n this.config = config;\r\n }\r\n\r\n private validateConfig(config: WsfeConfig): void {\r\n if (!config.ticket || !config.ticket.token) {\r\n throw new ArcaValidationError(\r\n 'Ticket WSAA requerido. Ejecutá wsaa.login() primero.',\r\n { hint: 'El ticket se obtiene del servicio WSAA' }\r\n );\r\n }\r\n\r\n if (!config.puntoVenta || config.puntoVenta < 1 || config.puntoVenta > 9999) {\r\n throw new ArcaValidationError(\r\n 'Punto de venta inválido: debe ser un número entre 1 y 9999',\r\n { puntoVenta: config.puntoVenta }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Emite un Ticket C de forma simple (solo total)\r\n * Tipo de comprobante: 83\r\n */\r\n async emitirTicketCSimple(params: {\r\n total: number;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total: params.total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Emite un Ticket C completo (con detalle de items)\r\n * Los items no se envían a ARCA, pero se retornan en la respuesta.\r\n */\r\n async emitirTicketC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n const cae = await this.emitirComprobante({\r\n tipo: TipoComprobante.TICKET_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n });\r\n\r\n return {\r\n ...cae,\r\n items: params.items,\r\n };\r\n }\r\n /**\r\n * Emite una Factura B (monotributo a responsable inscripto)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaB(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_B,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Emite una Factura A (RI a RI)\r\n * REQUIERE detalle de items con IVA discriminado\r\n */\r\n async emitirFacturaA(params: {\r\n items: FacturaItem[];\r\n comprador: Comprador;\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n incluyeIva?: boolean;\r\n }): Promise<CAEResponse> {\r\n this.validateItemsWithIVA(params.items);\r\n const incluyeIva = params.incluyeIva || false;\r\n const ivaData = this.calcularIVAPorAlicuota(params.items, incluyeIva);\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_A,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n items: params.items,\r\n comprador: params.comprador,\r\n fecha: params.fecha,\r\n ivaData,\r\n incluyeIva,\r\n });\r\n }\r\n\r\n /**\r\n * Valida que todos los items tengan alícuota IVA definida\r\n */\r\n private validateItemsWithIVA(items: FacturaItem[]): void {\r\n const sinIva = items.filter(item =>\r\n item.alicuotaIva === undefined || item.alicuotaIva === null\r\n );\r\n\r\n if (sinIva.length > 0) {\r\n throw new ArcaValidationError(\r\n 'Esta operación requiere IVA discriminado en todos los items',\r\n {\r\n itemsSinIva: sinIva.map(i => i.descripcion),\r\n hint: 'Agregá alicuotaIva a cada item (21, 10.5, 27, o 0)'\r\n }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Calcula IVA agrupado por alícuota\r\n * ARCA requiere esto para Factura B/A\r\n */\r\n private calcularIVAPorAlicuota(items: FacturaItem[], incluyeIva = false): {\r\n alicuota: number;\r\n baseImponible: number;\r\n importe: number;\r\n }[] {\r\n const porAlicuota = new Map<number, { base: number; importe: number }>();\r\n\r\n items.forEach(item => {\r\n const alicuota = item.alicuotaIva || 0;\r\n let precioNeto = item.precioUnitario;\r\n\r\n if (incluyeIva && alicuota) {\r\n precioNeto = item.precioUnitario / (1 + (alicuota / 100));\r\n }\r\n\r\n const base = item.cantidad * precioNeto;\r\n const importe = base * alicuota / 100;\r\n\r\n const actual = porAlicuota.get(alicuota) || { base: 0, importe: 0 };\r\n porAlicuota.set(alicuota, {\r\n base: actual.base + base,\r\n importe: actual.importe + importe,\r\n });\r\n });\r\n\r\n return Array.from(porAlicuota.entries()).map(([alicuota, valores]) => ({\r\n alicuota,\r\n baseImponible: redondear(valores.base),\r\n importe: redondear(valores.importe),\r\n }));\r\n }\r\n\r\n /**\r\n * Emite una Factura C (consumidor final)\r\n * Forma simplificada sin especificar comprador\r\n */\r\n async emitirFacturaC(params: {\r\n items: FacturaItem[];\r\n concepto?: Concepto;\r\n fecha?: Date;\r\n }): Promise<CAEResponse> {\r\n const total = redondear(calcularTotal(params.items));\r\n\r\n return this.emitirComprobante({\r\n tipo: TipoComprobante.FACTURA_C,\r\n concepto: params.concepto || Concepto.PRODUCTOS,\r\n total,\r\n fecha: params.fecha,\r\n comprador: {\r\n tipoDocumento: TipoDocumento.CONSUMIDOR_FINAL,\r\n nroDocumento: '0',\r\n },\r\n items: params.items,\r\n });\r\n }\r\n\r\n /**\r\n * Emite un comprobante (método genérico interno)\r\n */\r\n async emitirComprobante(request: EmitirFacturaRequest): Promise<CAEResponse> {\r\n // 1. Obtener próximo número de comprobante\r\n const nroComprobante = await this.obtenerProximoNumero(request.tipo);\r\n\r\n // 2. Determinar total\r\n let total = request.total || 0;\r\n let subtotal = total; // Para Factura C/Ticket C, el neto es igual al total si no discriminamos\r\n let iva = 0;\r\n\r\n if (request.items && request.items.length > 0) {\r\n const incluyeIva = request.incluyeIva || false;\r\n subtotal = redondear(calcularSubtotal(request.items, incluyeIva));\r\n iva = redondear(calcularIVA(request.items, incluyeIva));\r\n total = redondear(calcularTotal(request.items, incluyeIva));\r\n }\r\n\r\n if (total <= 0) {\r\n throw new ArcaValidationError('El monto total debe ser mayor a 0');\r\n }\r\n\r\n // 3. Preparar request SOAP\r\n const soapRequest = this.buildCAESolicitarRequest({\r\n tipo: request.tipo,\r\n puntoVenta: this.config.puntoVenta,\r\n nroComprobante,\r\n concepto: request.concepto,\r\n fecha: request.fecha || new Date(),\r\n comprador: request.comprador,\r\n subtotal,\r\n iva,\r\n total,\r\n ivaData: request.ivaData,\r\n });\r\n\r\n // 4. Enviar a ARCA\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await callArcaApi(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECAESolicitar',\r\n },\r\n body: soapRequest,\r\n timeout: this.config.timeout,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(\r\n `Error HTTP al comunicarse con WSFE: ${response.status}`,\r\n 'HTTP_ERROR',\r\n { status: response.status }\r\n );\r\n }\r\n\r\n const responseXml = await response.text();\r\n\r\n // 5. Parsear respuesta CAE\r\n const result = await this.parseCAEResponse(responseXml);\r\n\r\n return {\r\n ...result,\r\n items: request.items,\r\n iva: request.ivaData,\r\n };\r\n }\r\n\r\n /**\r\n * Obtiene el próximo número de comprobante disponible\r\n */\r\n private async obtenerProximoNumero(tipo: TipoComprobante): Promise<number> {\r\n const soapRequest = this.buildProximoNumeroRequest(tipo);\r\n const endpoint = getWsfeEndpoint(this.config.environment);\r\n\r\n const response = await fetch(endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'text/xml; charset=utf-8',\r\n 'SOAPAction': 'http://ar.gov.afip.dif.FEV1/FECompUltimoAutorizado',\r\n },\r\n body: soapRequest,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ArcaError(`Error HTTP al consultar próximo número: ${response.status}`, 'HTTP_ERROR');\r\n }\r\n\r\n const responseXml = await response.text();\r\n const result = parseXml(responseXml);\r\n\r\n const data = result?.Envelope?.Body?.FECompUltimoAutorizadoResponse?.FECompUltimoAutorizadoResult;\r\n\r\n if (data?.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const nro = data?.CbteNro;\r\n return typeof nro === 'number' ? nro + 1 : 1;\r\n }\r\n\r\n private buildCAESolicitarRequest(params: {\r\n tipo: TipoComprobante;\r\n puntoVenta: number;\r\n nroComprobante: number;\r\n concepto: Concepto;\r\n fecha: Date;\r\n comprador?: EmitirFacturaRequest['comprador'];\r\n subtotal: number;\r\n iva: number;\r\n total: number;\r\n ivaData?: EmitirFacturaRequest['ivaData'];\r\n }): string {\r\n const fechaStr = params.fecha.toISOString().split('T')[0].replace(/-/g, '');\r\n\r\n let ivaXml = '';\r\n if (params.ivaData && params.ivaData.length > 0) {\r\n ivaXml = '<ar:Iva>';\r\n params.ivaData.forEach(aliquot => {\r\n ivaXml += `\r\n <ar:AlicIva>\r\n <ar:Id>${this.getCodigoAlicuota(aliquot.alicuota)}</ar:Id>\r\n <ar:BaseImp>${aliquot.baseImponible.toFixed(2)}</ar:BaseImp>\r\n <ar:Importe>${aliquot.importe.toFixed(2)}</ar:Importe>\r\n </ar:AlicIva>`;\r\n });\r\n ivaXml += '\\n </ar:Iva>';\r\n }\r\n\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECAESolicitar>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:FeCAEReq>\r\n <ar:FeCabReq>\r\n <ar:CantReg>1</ar:CantReg>\r\n <ar:PtoVta>${params.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${params.tipo}</ar:CbteTipo>\r\n </ar:FeCabReq>\r\n <ar:FeDetReq>\r\n <ar:FECAEDetRequest>\r\n <ar:Concepto>${params.concepto}</ar:Concepto>\r\n <ar:DocTipo>${params.comprador?.tipoDocumento || 99}</ar:DocTipo>\r\n <ar:DocNro>${params.comprador?.nroDocumento || 0}</ar:DocNro>\r\n <ar:CbteDesde>${params.nroComprobante}</ar:CbteDesde>\r\n <ar:CbteHasta>${params.nroComprobante}</ar:CbteHasta>\r\n <ar:CbteFch>${fechaStr}</ar:CbteFch>\r\n <ar:ImpTotal>${params.total.toFixed(2)}</ar:ImpTotal>\r\n <ar:ImpTotConc>0.00</ar:ImpTotConc>\r\n <ar:ImpNeto>${params.subtotal.toFixed(2)}</ar:ImpNeto>\r\n <ar:ImpOpEx>0.00</ar:ImpOpEx>\r\n <ar:ImpIVA>${params.iva.toFixed(2)}</ar:ImpIVA>\r\n <ar:ImpTrib>0.00</ar:ImpTrib>\r\n <ar:MonId>PES</ar:MonId>\r\n <ar:MonCotiz>1</ar:MonCotiz>\r\n ${ivaXml}\r\n </ar:FECAEDetRequest>\r\n </ar:FeDetReq>\r\n </ar:FeCAEReq>\r\n </ar:FECAESolicitar>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n /**\r\n * Mapea alícuota % a código ARCA\r\n */\r\n private getCodigoAlicuota(porcentaje: number): number {\r\n const mapa: Record<number, number> = {\r\n 0: 3,\r\n 10.5: 4,\r\n 21: 5,\r\n 27: 6,\r\n };\r\n\r\n const codigo = mapa[porcentaje];\r\n if (!codigo) {\r\n throw new ArcaValidationError(\r\n `Alícuota IVA inválida: ${porcentaje}%`,\r\n {\r\n alicuotasValidas: [0, 10.5, 21, 27],\r\n hint: 'Usar una de las alícuotas oficiales de Argentina'\r\n }\r\n );\r\n }\r\n\r\n return codigo;\r\n }\r\n\r\n private buildProximoNumeroRequest(tipo: TipoComprobante): string {\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" \r\n xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\">\r\n <soapenv:Header/>\r\n <soapenv:Body>\r\n <ar:FECompUltimoAutorizado>\r\n <ar:Auth>\r\n <ar:Token>${this.config.ticket.token}</ar:Token>\r\n <ar:Sign>${this.config.ticket.sign}</ar:Sign>\r\n <ar:Cuit>${this.config.cuit}</ar:Cuit>\r\n </ar:Auth>\r\n <ar:PtoVta>${this.config.puntoVenta}</ar:PtoVta>\r\n <ar:CbteTipo>${tipo}</ar:CbteTipo>\r\n </ar:FECompUltimoAutorizado>\r\n </soapenv:Body>\r\n</soapenv:Envelope>`;\r\n }\r\n\r\n private async parseCAEResponse(xml: string): Promise<CAEResponse> {\r\n const result = parseXml(xml);\r\n const data = result?.Envelope?.Body?.FECAESolicitarResponse?.FECAESolicitarResult;\r\n\r\n if (!data) {\r\n throw new ArcaError('Respuesta WSFE inválida: estructura no reconocida', 'PARSE_ERROR', { xml });\r\n }\r\n\r\n if (data.Errors) {\r\n const error = Array.isArray(data.Errors.Err) ? data.Errors.Err[0] : data.Errors.Err;\r\n throw new ArcaError(`Error ARCA: ${error?.Msg || 'Error desconocido'}`, 'ARCA_ERROR', data.Errors);\r\n }\r\n\r\n const cab = data.FeCabResp;\r\n const det = Array.isArray(data.FeDetResp.FECAEDetResponse)\r\n ? data.FeDetResp.FECAEDetResponse[0]\r\n : data.FeDetResp.FECAEDetResponse;\r\n\r\n if (!det) {\r\n throw new ArcaError('Respuesta WSFE incompleta: falta detalle del comprobante', 'PARSE_ERROR');\r\n }\r\n\r\n const observaciones: string[] = [];\r\n if (det.Observaciones) {\r\n const obsArray = Array.isArray(det.Observaciones.Obs)\r\n ? det.Observaciones.Obs\r\n : [det.Observaciones.Obs];\r\n obsArray.forEach((o: any) => observaciones.push(o.Msg));\r\n }\r\n\r\n return {\r\n tipoComprobante: cab.CbteTipo,\r\n puntoVenta: cab.PtoVta,\r\n nroComprobante: det.CbteDesde,\r\n fecha: det.CbteFch,\r\n cae: det.CAE,\r\n vencimientoCae: det.CAEFchVto,\r\n resultado: det.Resultado,\r\n observaciones: observaciones.length > 0 ? observaciones : undefined,\r\n };\r\n }\r\n}\r\n","import type { CAEResponse, Comprador } from '../types/wsfe';\r\nimport { TipoDocumento } from '../types/wsfe';\r\n\r\n/**\r\n * Interfaz de los datos requeridos por AFIP para el QR\r\n * @see https://www.afip.gob.ar/fe/qr/especificaciones.asp\r\n */\r\ninterface AFIPQRData {\r\n ver: number;\r\n fecha: string;\r\n cuit: number;\r\n ptoVta: number;\r\n tipoCmp: number;\r\n nroCmp: number;\r\n importe: number;\r\n moneda: string;\r\n ctz: number;\r\n tipoDocRec: number;\r\n nroDocRec: number;\r\n tipoCodAut: string;\r\n codAut: number;\r\n}\r\n\r\n/**\r\n * Genera la URL completa con el código QR para un comprobante emitido\r\n * \r\n * @param caeResponse Respuesta obtenida al emitir la factura (CAEResponse)\r\n * @param cuitEmisor Tu CUIT (11 dígitos, sin guiones)\r\n * @param total Importe total del comprobante\r\n * @param comprador Datos del comprador (opcional, si no se pasa asume Consumidor Final)\r\n * @returns La URL lista para embeber en un generador de QR\r\n */\r\nexport function generarUrlQR(\r\n caeResponse: CAEResponse,\r\n cuitEmisor: string,\r\n total: number,\r\n comprador?: Comprador\r\n): string {\r\n // 1. Formatear fecha a YYYY-MM-DD (AFIP la devuelve como YYYYMMDD)\r\n const fDate = caeResponse.fecha;\r\n const fechaFormat = fDate.length === 8\r\n ? `${fDate.substring(0, 4)}-${fDate.substring(4, 6)}-${fDate.substring(6, 8)}`\r\n : fDate;\r\n\r\n // 2. Determinar comprador\r\n const docTipo = comprador?.tipoDocumento || TipoDocumento.CONSUMIDOR_FINAL;\r\n const docNro = comprador?.nroDocumento ? parseInt(comprador.nroDocumento, 10) : 0;\r\n\r\n // 3. Armar objeto JSON\r\n const qrData: AFIPQRData = {\r\n ver: 1, // Versión estándar exigida por AFIP\r\n fecha: fechaFormat,\r\n cuit: parseInt(cuitEmisor, 10),\r\n ptoVta: caeResponse.puntoVenta,\r\n tipoCmp: caeResponse.tipoComprobante,\r\n nroCmp: caeResponse.nroComprobante,\r\n importe: parseFloat(total.toFixed(2)),\r\n moneda: 'PES', // Por defecto PES (Pesos Argentinos)\r\n ctz: 1, // Cotización (siempre 1 para PES)\r\n tipoDocRec: docTipo,\r\n nroDocRec: docNro,\r\n tipoCodAut: 'E', // 'E' para comprobantes electrónicos\r\n codAut: parseInt(caeResponse.cae, 10)\r\n };\r\n\r\n // 4. Convertir a String -> Base64\r\n const jsonString = JSON.stringify(qrData);\r\n\r\n // Uso robusto de Buffer para evitar problemas de btoa en Node/Edge\r\n let base64 = typeof Buffer !== 'undefined'\r\n ? Buffer.from(jsonString).toString('base64')\r\n : btoa(jsonString); // Fallback para navegadores\r\n\r\n // 5. Retornar URL lista\r\n return `https://www.afip.gob.ar/fe/qr/?p=${base64}`;\r\n}\r\n"],"mappings":";AAKO,IAAM,iBAA8C;AAAA,EACvD,cAAc;AAAA,EACd,YAAY;AAChB;AAKO,IAAM,iBAA8C;AAAA,EACvD,cAAc;AAAA,EACd,YAAY;AAChB;AAKO,SAAS,gBAAgB,aAAkC;AAC9D,SAAO,eAAe,WAAW;AACrC;AAKO,SAAS,gBAAgB,aAAkC;AAC9D,SAAO,eAAe,WAAW;AACrC;;;ACNO,IAAM,YAAN,cAAwB,MAAM;AAAA,EACjC,YACI,SACO,MACA,SACT;AACE,UAAM,OAAO;AAHN;AACA;AAGP,SAAK,OAAO;AAAA,EAChB;AACJ;AAKO,IAAM,gBAAN,cAA4B,UAAU;AAAA,EACzC,YAAY,SAAiB,SAAmB;AAC5C,UAAM,SAAS,cAAc,OAAO;AACpC,SAAK,OAAO;AAAA,EAChB;AACJ;AAKO,IAAM,sBAAN,cAAkC,UAAU;AAAA,EAC/C,YAAY,SAAiB,SAAmB;AAC5C,UAAM,SAAS,oBAAoB,OAAO;AAC1C,SAAK,OAAO;AAAA,EAChB;AACJ;AAIO,IAAM,mBAAN,cAA+B,UAAU;AAAA,EAC5C,YAAY,SAAiB,SAAmB;AAC5C,UAAM,SAAS,iBAAiB,OAAO;AACvC,SAAK,OAAO;AAAA,EAChB;AACJ;;;AC9DA,SAAS,YAAY,iBAAiB;AAW/B,SAAS,SAAS,SAAiB,MAAsB;AAC5D,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,iBAAiB,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,KAAK,KAAK,GAAI;AAEnE,QAAM,MAAM;AAAA,IACR,oBAAoB;AAAA,MAChB,aAAa;AAAA,MACb,QAAQ;AAAA,QACJ,UAAU,KAAK,MAAM,IAAI,QAAQ,IAAI,GAAI;AAAA,QACzC,gBAAgB,IAAI,YAAY;AAAA,QAChC,gBAAgB,eAAe,YAAY;AAAA,MAC/C;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAEA,QAAM,UAAU,IAAI,WAAW;AAAA,IAC3B,kBAAkB;AAAA,IAClB,QAAQ;AAAA,EACZ,CAAC;AAED,QAAM,MAAM,QAAQ,MAAM,GAAG;AAC7B,SAAO;AAAA,EAA2C,GAAG;AACzD;AAQO,SAAS,kBAAkB,KAA0B;AACxD,MAAI;AACA,UAAM,SAAS,IAAI,UAAU;AAAA,MACzB,kBAAkB;AAAA,MAClB,qBAAqB;AAAA,MACrB,gBAAgB;AAAA;AAAA,IACpB,CAAC;AAED,UAAM,SAAS,OAAO,MAAM,GAAG;AAI/B,UAAM,cAAc,QAAQ,UAAU,MAAM,kBAAkB;AAE9D,QAAI,CAAC,aAAa;AAEd,YAAM,QAAQ,QAAQ,UAAU,MAAM;AACtC,UAAI,OAAO;AACP,cAAM,IAAI;AAAA,UACN,eAAe,MAAM,eAAe,mBAAmB;AAAA,UACvD,EAAE,WAAW,MAAM,WAAW,QAAQ,MAAM,OAAO;AAAA,QACvD;AAAA,MACJ;AAEA,YAAM,IAAI;AAAA,QACN;AAAA,QACA,EAAE,mBAAmB,OAAO;AAAA,MAChC;AAAA,IACJ;AAGA,WAAO;AAAA,MACH,OAAO,YAAY;AAAA,MACnB,MAAM,YAAY;AAAA,MAClB,gBAAgB,IAAI,KAAK,YAAY,OAAO,cAAc;AAAA,MAC1D,gBAAgB,IAAI,KAAK,YAAY,OAAO,cAAc;AAAA,IAC9D;AAAA,EACJ,SAAS,OAAO;AACZ,QAAI,iBAAiB,cAAe,OAAM;AAC1C,UAAM,IAAI;AAAA,MACN;AAAA,MACA,EAAE,eAAe,MAAM;AAAA,IAC3B;AAAA,EACJ;AACJ;AAQO,SAAS,SAAS,KAAkB;AACvC,QAAM,SAAS,IAAI,UAAU;AAAA,IACzB,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,gBAAgB;AAAA,EACpB,CAAC;AACD,SAAO,OAAO,MAAM,GAAG;AAC3B;AAKO,SAAS,aAAa,MAAuB;AAChD,SAAO,WAAW,KAAK,IAAI;AAC/B;;;AC5GA,YAAY,WAAW;AAWhB,SAAS,QAAQ,KAAa,SAAiB,QAAwB;AAC1E,MAAI;AAEA,UAAM,OAAa,UAAI,mBAAmB,OAAO;AAGjD,UAAM,aAAmB,UAAI,kBAAkB,MAAM;AAGrD,UAAM,KAAW,YAAM,iBAAiB;AAGxC,OAAG,UAAgB,WAAK,aAAa,KAAK,MAAM;AAGhD,OAAG,eAAe,IAAI;AAGtB,OAAG,UAAU;AAAA,MACT,KAAK;AAAA,MACL,aAAa;AAAA,MACb,iBAAuB,UAAI,KAAK;AAAA,MAChC,yBAAyB;AAAA,QACrB;AAAA,UACI,MAAY,UAAI,KAAK;AAAA,UACrB,OAAa,UAAI,KAAK;AAAA,QAC1B;AAAA,QACA;AAAA,UACI,MAAY,UAAI,KAAK;AAAA;AAAA,QAEzB;AAAA,QACA;AAAA,UACI,MAAY,UAAI,KAAK;AAAA;AAAA,UAErB,OAAO,oBAAI,KAAK;AAAA,QACpB;AAAA,MACJ;AAAA,IACJ,CAAC;AAGD,OAAG,KAAK;AAGR,UAAM,WAAiB,WAAK,MAAM,GAAG,OAAO,CAAC,EAAE,SAAS;AACxD,UAAM,SAAe,WAAK,SAAS,QAAQ;AAE3C,WAAO;AAAA,EACX,SAAS,OAAO;AACZ,QAAI,iBAAiB,cAAe,OAAM;AAC1C,UAAM,IAAI;AAAA,MACN;AAAA,MACA;AAAA,QACI,eAAe;AAAA,QACf,MAAM;AAAA,MACV;AAAA,IACJ;AAAA,EACJ;AACJ;AAKO,SAAS,oBAAoB,MAAuB;AACvD,SAAO,KAAK,SAAS,6BAA6B,KAC9C,KAAK,SAAS,2BAA2B;AACjD;AAKO,SAAS,mBAAmB,KAAsB;AACrD,SACK,IAAI,SAAS,6BAA6B,KACvC,IAAI,SAAS,2BAA2B,KAC3C,IAAI,SAAS,iCAAiC,KAC3C,IAAI,SAAS,+BAA+B;AAExD;;;AClFO,IAAM,gBAAN,MAAoB;AAAA,EACf,SAA6B;AAAA;AAAA;AAAA;AAAA,EAKrC,UAAU,QAA2B;AACjC,SAAK,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAgC;AAC5B,QAAI,CAAC,KAAK,OAAQ,QAAO;AAEzB,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,YAAY,KAAK,OAAO,eAAe,QAAQ,IAAI,IAAI,QAAQ;AAGrE,UAAM,YAAY,IAAI,KAAK;AAE3B,QAAI,YAAY,WAAW;AACvB,WAAK,SAAS;AACd,aAAO;AAAA,IACX;AAEA,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,iBAA0B;AACtB,WAAO,KAAK,UAAU,MAAM;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,cAAoB;AAChB,SAAK,SAAS;AAAA,EAClB;AACJ;;;AClDA,OAAO,WAAW;AAuBlB,eAAsB,YAClB,KACA,SACiB;AACjB,QAAM,UAAU,QAAQ,WAAW;AAInC,QAAM,SAAS,OAAO,YAAY,eAAe,QAAQ,YAAY,QAAQ,SAAS;AAEtF,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE9D,MAAI;AACA,QAAI,QAAQ;AACR,YAAM,QAAQ,IAAI,MAAM,MAAM;AAAA;AAAA;AAAA,QAG1B,SAAS;AAAA,QACT,oBAAoB;AAAA,MACxB,CAAC;AAGD,aAAO,MAAM,MAAM,KAAK;AAAA,QACpB,QAAQ,QAAQ;AAAA,QAChB,SAAS,QAAQ;AAAA,QACjB,MAAM,QAAQ;AAAA,QACd;AAAA,QACA,QAAQ,WAAW;AAAA,MACvB,CAAQ;AAAA,IACZ;AAEA,WAAO,MAAM,MAAM,KAAK;AAAA,MACpB,QAAQ,QAAQ;AAAA,MAChB,SAAS,QAAQ;AAAA,MACjB,MAAM,QAAQ;AAAA,MACd,QAAQ,WAAW;AAAA,IACvB,CAAC;AAAA,EACL,SAAS,OAAY;AACjB,QAAI,MAAM,SAAS,cAAc;AAC7B,YAAM,IAAI,iBAAiB,6BAA6B,OAAO,6BAA6B,GAAG,EAAE;AAAA,IACrG;AAGA,QAAI,UAAU,MAAM;AACpB,QAAI,QAAQ,SAAS,kBAAkB,GAAG;AACtC,gBAAU;AAAA,IACd;AAEA,UAAM,IAAI,iBAAiB,yCAAyC,OAAO,IAAI;AAAA,MAC3E;AAAA,MACA,eAAe;AAAA,IACnB,CAAC;AAAA,EACL,UAAE;AACE,iBAAa,SAAS;AAAA,EAC1B;AACJ;;;ACtDO,IAAM,cAAN,MAAkB;AAAA,EACb;AAAA,EACA;AAAA,EAER,YAAY,QAAoB;AAC5B,SAAK,eAAe,MAAM;AAC1B,SAAK,SAAS;AACd,SAAK,gBAAgB,IAAI,cAAc;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,QAA0B;AAC7C,QAAI,CAAC,aAAa,OAAO,IAAI,GAAG;AAC5B,YAAM,IAAI;AAAA,QACN;AAAA,QACA,EAAE,MAAM,OAAO,KAAK;AAAA,MACxB;AAAA,IACJ;AAEA,QAAI,CAAC,oBAAoB,OAAO,IAAI,GAAG;AACnC,YAAM,IAAI;AAAA,QACN;AAAA,QACA,EAAE,MAAM,4CAA4C;AAAA,MACxD;AAAA,IACJ;AAEA,QAAI,CAAC,mBAAmB,OAAO,GAAG,GAAG;AACjC,YAAM,IAAI;AAAA,QACN;AAAA,QACA,EAAE,MAAM,8EAA8E;AAAA,MAC1F;AAAA,IACJ;AAEA,QAAI,CAAC,OAAO,WAAW,OAAO,QAAQ,KAAK,MAAM,IAAI;AACjD,YAAM,IAAI;AAAA,QACN;AAAA,QACA,EAAE,MAAM,8BAA8B;AAAA,MAC1C;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,QAA8B;AAEhC,UAAM,eAAe,KAAK,cAAc,UAAU;AAClD,QAAI,cAAc;AACd,aAAO;AAAA,IACX;AAGA,UAAM,SAAS,MAAM,KAAK,iBAAiB;AAC3C,SAAK,cAAc,UAAU,MAAM;AACnC,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,mBAAyC;AAEnD,UAAM,MAAM,SAAS,KAAK,OAAO,SAAS,KAAK,OAAO,IAAI;AAG1D,QAAI;AACJ,QAAI;AACA,YAAM,QAAQ,KAAK,KAAK,OAAO,MAAM,KAAK,OAAO,GAAG;AAAA,IACxD,SAAS,OAAO;AACZ,YAAM,IAAI;AAAA,QACN;AAAA,QACA,EAAE,eAAe,MAAM;AAAA,MAC3B;AAAA,IACJ;AAGA,UAAM,WAAW,gBAAgB,KAAK,OAAO,WAAW;AAExD,UAAM,WAAW,MAAM,YAAY,UAAU;AAAA,MACzC,QAAQ;AAAA,MACR,SAAS;AAAA,QACL,gBAAgB;AAAA,QAChB,cAAc;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,iBAAiB,GAAG;AAAA,MAC/B,SAAS,KAAK,OAAO;AAAA,IACzB,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AACd,YAAM,IAAI;AAAA,QACN,uCAAuC,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,QAC7E,EAAE,QAAQ,SAAS,QAAQ,YAAY,SAAS,WAAW;AAAA,MAC/D;AAAA,IACJ;AAEA,UAAM,cAAc,MAAM,SAAS,KAAK;AAGxC,WAAO,kBAAkB,WAAW;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,KAAqB;AAC1C,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAMG,GAAG;AAAA;AAAA;AAAA;AAAA,EAIjB;AAAA;AAAA;AAAA;AAAA,EAKA,aAAmB;AACf,SAAK,cAAc,YAAY;AAAA,EACnC;AACJ;;;ACzIO,IAAK,kBAAL,kBAAKA,qBAAL;AACH,EAAAA,kCAAA,eAAY,KAAZ;AACA,EAAAA,kCAAA,eAAY,KAAZ;AACA,EAAAA,kCAAA,eAAY,MAAZ;AACA,EAAAA,kCAAA,cAAW,MAAX;AACA,EAAAA,kCAAA,cAAW,MAAX;AACA,EAAAA,kCAAA,cAAW,MAAX;AANQ,SAAAA;AAAA,GAAA;AAYL,IAAK,WAAL,kBAAKC,cAAL;AACH,EAAAA,oBAAA,eAAY,KAAZ;AACA,EAAAA,oBAAA,eAAY,KAAZ;AACA,EAAAA,oBAAA,2BAAwB,KAAxB;AAHQ,SAAAA;AAAA,GAAA;AASL,IAAK,gBAAL,kBAAKC,mBAAL;AACH,EAAAA,8BAAA,UAAO,MAAP;AACA,EAAAA,8BAAA,UAAO,MAAP;AACA,EAAAA,8BAAA,SAAM,MAAN;AACA,EAAAA,8BAAA,QAAK,MAAL;AACA,EAAAA,8BAAA,QAAK,MAAL;AACA,EAAAA,8BAAA,mBAAgB,MAAhB;AACA,EAAAA,8BAAA,eAAY,MAAZ;AACA,EAAAA,8BAAA,qBAAkB,MAAlB;AACA,EAAAA,8BAAA,wBAAqB,MAArB;AACA,EAAAA,8BAAA,SAAM,MAAN;AACA,EAAAA,8BAAA,sBAAmB,MAAnB;AAXQ,SAAAA;AAAA,GAAA;;;AChCL,SAAS,iBAAiB,OAAsB,aAAa,OAAe;AAC/E,SAAO,MAAM,OAAO,CAAC,KAAK,SAAS;AAC/B,QAAI,aAAa,KAAK;AACtB,QAAI,cAAc,KAAK,aAAa;AAChC,mBAAa,KAAK,kBAAkB,IAAK,KAAK,cAAc;AAAA,IAChE;AACA,WAAO,MAAO,KAAK,WAAW;AAAA,EAClC,GAAG,CAAC;AACR;AAMO,SAAS,YAAY,OAAsB,aAAa,OAAe;AAC1E,SAAO,MAAM,OAAO,CAAC,KAAK,SAAS;AAC/B,UAAM,WAAW,KAAK,eAAe;AACrC,QAAI,aAAa,KAAK;AACtB,QAAI,cAAc,UAAU;AACxB,mBAAa,KAAK,kBAAkB,IAAK,WAAW;AAAA,IACxD;AACA,UAAM,eAAe,KAAK,WAAW;AACrC,WAAO,MAAO,eAAe,WAAW;AAAA,EAC5C,GAAG,CAAC;AACR;AAKO,SAAS,cAAc,OAAsB,aAAa,OAAe;AAC5E,MAAI,YAAY;AACZ,WAAO,MAAM,OAAO,CAAC,KAAK,SAAS,MAAO,KAAK,WAAW,KAAK,gBAAiB,CAAC;AAAA,EACrF;AACA,QAAM,WAAW,iBAAiB,OAAO,KAAK;AAC9C,QAAM,MAAM,YAAY,OAAO,KAAK;AACpC,SAAO,WAAW;AACtB;AAKO,SAAS,UAAU,OAAuB;AAC7C,SAAO,KAAK,MAAM,QAAQ,GAAG,IAAI;AACrC;;;ACJO,IAAM,cAAN,MAAkB;AAAA,EACb;AAAA,EAER,YAAY,QAAoB;AAC5B,SAAK,eAAe,MAAM;AAC1B,SAAK,SAAS;AAAA,EAClB;AAAA,EAEQ,eAAe,QAA0B;AAC7C,QAAI,CAAC,OAAO,UAAU,CAAC,OAAO,OAAO,OAAO;AACxC,YAAM,IAAI;AAAA,QACN;AAAA,QACA,EAAE,MAAM,yCAAyC;AAAA,MACrD;AAAA,IACJ;AAEA,QAAI,CAAC,OAAO,cAAc,OAAO,aAAa,KAAK,OAAO,aAAa,MAAM;AACzE,YAAM,IAAI;AAAA,QACN;AAAA,QACA,EAAE,YAAY,OAAO,WAAW;AAAA,MACpC;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,oBAAoB,QAID;AACrB,WAAO,KAAK,kBAAkB;AAAA,MAC1B;AAAA,MACA,UAAU,OAAO;AAAA,MACjB,OAAO,OAAO;AAAA,MACd,OAAO,OAAO;AAAA,MACd,WAAW;AAAA,QACP;AAAA,QACA,cAAc;AAAA,MAClB;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAc,QAIK;AACrB,UAAM,QAAQ,UAAU,cAAc,OAAO,KAAK,CAAC;AAEnD,UAAM,MAAM,MAAM,KAAK,kBAAkB;AAAA,MACrC;AAAA,MACA,UAAU,OAAO;AAAA,MACjB;AAAA,MACA,OAAO,OAAO;AAAA,MACd,WAAW;AAAA,QACP;AAAA,QACA,cAAc;AAAA,MAClB;AAAA,IACJ,CAAC;AAED,WAAO;AAAA,MACH,GAAG;AAAA,MACH,OAAO,OAAO;AAAA,IAClB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,QAMI;AACrB,SAAK,qBAAqB,OAAO,KAAK;AACtC,UAAM,aAAa,OAAO,cAAc;AACxC,UAAM,UAAU,KAAK,uBAAuB,OAAO,OAAO,UAAU;AAEpE,WAAO,KAAK,kBAAkB;AAAA,MAC1B;AAAA,MACA,UAAU,OAAO;AAAA,MACjB,OAAO,OAAO;AAAA,MACd,WAAW,OAAO;AAAA,MAClB,OAAO,OAAO;AAAA,MACd;AAAA,MACA;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAe,QAMI;AACrB,SAAK,qBAAqB,OAAO,KAAK;AACtC,UAAM,aAAa,OAAO,cAAc;AACxC,UAAM,UAAU,KAAK,uBAAuB,OAAO,OAAO,UAAU;AAEpE,WAAO,KAAK,kBAAkB;AAAA,MAC1B;AAAA,MACA,UAAU,OAAO;AAAA,MACjB,OAAO,OAAO;AAAA,MACd,WAAW,OAAO;AAAA,MAClB,OAAO,OAAO;AAAA,MACd;AAAA,MACA;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAAqB,OAA4B;AACrD,UAAM,SAAS,MAAM;AAAA,MAAO,UACxB,KAAK,gBAAgB,UAAa,KAAK,gBAAgB;AAAA,IAC3D;AAEA,QAAI,OAAO,SAAS,GAAG;AACnB,YAAM,IAAI;AAAA,QACN;AAAA,QACA;AAAA,UACI,aAAa,OAAO,IAAI,OAAK,EAAE,WAAW;AAAA,UAC1C,MAAM;AAAA,QACV;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,uBAAuB,OAAsB,aAAa,OAI9D;AACA,UAAM,cAAc,oBAAI,IAA+C;AAEvE,UAAM,QAAQ,UAAQ;AAClB,YAAM,WAAW,KAAK,eAAe;AACrC,UAAI,aAAa,KAAK;AAEtB,UAAI,cAAc,UAAU;AACxB,qBAAa,KAAK,kBAAkB,IAAK,WAAW;AAAA,MACxD;AAEA,YAAM,OAAO,KAAK,WAAW;AAC7B,YAAM,UAAU,OAAO,WAAW;AAElC,YAAM,SAAS,YAAY,IAAI,QAAQ,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE;AAClE,kBAAY,IAAI,UAAU;AAAA,QACtB,MAAM,OAAO,OAAO;AAAA,QACpB,SAAS,OAAO,UAAU;AAAA,MAC9B,CAAC;AAAA,IACL,CAAC;AAED,WAAO,MAAM,KAAK,YAAY,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,UAAU,OAAO,OAAO;AAAA,MACnE;AAAA,MACA,eAAe,UAAU,QAAQ,IAAI;AAAA,MACrC,SAAS,UAAU,QAAQ,OAAO;AAAA,IACtC,EAAE;AAAA,EACN;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAe,QAII;AACrB,UAAM,QAAQ,UAAU,cAAc,OAAO,KAAK,CAAC;AAEnD,WAAO,KAAK,kBAAkB;AAAA,MAC1B;AAAA,MACA,UAAU,OAAO;AAAA,MACjB;AAAA,MACA,OAAO,OAAO;AAAA,MACd,WAAW;AAAA,QACP;AAAA,QACA,cAAc;AAAA,MAClB;AAAA,MACA,OAAO,OAAO;AAAA,IAClB,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBAAkB,SAAqD;AAEzE,UAAM,iBAAiB,MAAM,KAAK,qBAAqB,QAAQ,IAAI;AAGnE,QAAI,QAAQ,QAAQ,SAAS;AAC7B,QAAI,WAAW;AACf,QAAI,MAAM;AAEV,QAAI,QAAQ,SAAS,QAAQ,MAAM,SAAS,GAAG;AAC3C,YAAM,aAAa,QAAQ,cAAc;AACzC,iBAAW,UAAU,iBAAiB,QAAQ,OAAO,UAAU,CAAC;AAChE,YAAM,UAAU,YAAY,QAAQ,OAAO,UAAU,CAAC;AACtD,cAAQ,UAAU,cAAc,QAAQ,OAAO,UAAU,CAAC;AAAA,IAC9D;AAEA,QAAI,SAAS,GAAG;AACZ,YAAM,IAAI,oBAAoB,mCAAmC;AAAA,IACrE;AAGA,UAAM,cAAc,KAAK,yBAAyB;AAAA,MAC9C,MAAM,QAAQ;AAAA,MACd,YAAY,KAAK,OAAO;AAAA,MACxB;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB,OAAO,QAAQ,SAAS,oBAAI,KAAK;AAAA,MACjC,WAAW,QAAQ;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,QAAQ;AAAA,IACrB,CAAC;AAGD,UAAM,WAAW,gBAAgB,KAAK,OAAO,WAAW;AAExD,UAAM,WAAW,MAAM,YAAY,UAAU;AAAA,MACzC,QAAQ;AAAA,MACR,SAAS;AAAA,QACL,gBAAgB;AAAA,QAChB,cAAc;AAAA,MAClB;AAAA,MACA,MAAM;AAAA,MACN,SAAS,KAAK,OAAO;AAAA,IACzB,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AACd,YAAM,IAAI;AAAA,QACN,uCAAuC,SAAS,MAAM;AAAA,QACtD;AAAA,QACA,EAAE,QAAQ,SAAS,OAAO;AAAA,MAC9B;AAAA,IACJ;AAEA,UAAM,cAAc,MAAM,SAAS,KAAK;AAGxC,UAAM,SAAS,MAAM,KAAK,iBAAiB,WAAW;AAEtD,WAAO;AAAA,MACH,GAAG;AAAA,MACH,OAAO,QAAQ;AAAA,MACf,KAAK,QAAQ;AAAA,IACjB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,qBAAqB,MAAwC;AACvE,UAAM,cAAc,KAAK,0BAA0B,IAAI;AACvD,UAAM,WAAW,gBAAgB,KAAK,OAAO,WAAW;AAExD,UAAM,WAAW,MAAM,MAAM,UAAU;AAAA,MACnC,QAAQ;AAAA,MACR,SAAS;AAAA,QACL,gBAAgB;AAAA,QAChB,cAAc;AAAA,MAClB;AAAA,MACA,MAAM;AAAA,IACV,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AACd,YAAM,IAAI,UAAU,iDAA2C,SAAS,MAAM,IAAI,YAAY;AAAA,IAClG;AAEA,UAAM,cAAc,MAAM,SAAS,KAAK;AACxC,UAAM,SAAS,SAAS,WAAW;AAEnC,UAAM,OAAO,QAAQ,UAAU,MAAM,gCAAgC;AAErE,QAAI,MAAM,QAAQ;AACd,YAAM,QAAQ,MAAM,QAAQ,KAAK,OAAO,GAAG,IAAI,KAAK,OAAO,IAAI,CAAC,IAAI,KAAK,OAAO;AAChF,YAAM,IAAI,UAAU,eAAe,OAAO,OAAO,mBAAmB,IAAI,cAAc,KAAK,MAAM;AAAA,IACrG;AAEA,UAAM,MAAM,MAAM;AAClB,WAAO,OAAO,QAAQ,WAAW,MAAM,IAAI;AAAA,EAC/C;AAAA,EAEQ,yBAAyB,QAWtB;AACP,UAAM,WAAW,OAAO,MAAM,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,EAAE,QAAQ,MAAM,EAAE;AAE1E,QAAI,SAAS;AACb,QAAI,OAAO,WAAW,OAAO,QAAQ,SAAS,GAAG;AAC7C,eAAS;AACT,aAAO,QAAQ,QAAQ,aAAW;AAC9B,kBAAU;AAAA;AAAA,mBAEP,KAAK,kBAAkB,QAAQ,QAAQ,CAAC;AAAA,wBACnC,QAAQ,cAAc,QAAQ,CAAC,CAAC;AAAA,wBAChC,QAAQ,QAAQ,QAAQ,CAAC,CAAC;AAAA;AAAA,MAEtC,CAAC;AACD,gBAAU;AAAA,IACd;AAEA,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAOK,KAAK,OAAO,OAAO,KAAK;AAAA,mBACzB,KAAK,OAAO,OAAO,IAAI;AAAA,mBACvB,KAAK,OAAO,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,uBAKZ,OAAO,UAAU;AAAA,yBACf,OAAO,IAAI;AAAA;AAAA;AAAA;AAAA,2BAIT,OAAO,QAAQ;AAAA,0BAChB,OAAO,WAAW,iBAAiB,EAAE;AAAA,yBACtC,OAAO,WAAW,gBAAgB,CAAC;AAAA,4BAChC,OAAO,cAAc;AAAA,4BACrB,OAAO,cAAc;AAAA,0BACvB,QAAQ;AAAA,2BACP,OAAO,MAAM,QAAQ,CAAC,CAAC;AAAA;AAAA,0BAExB,OAAO,SAAS,QAAQ,CAAC,CAAC;AAAA;AAAA,yBAE3B,OAAO,IAAI,QAAQ,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,cAIhC,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOhB;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,YAA4B;AAClD,UAAM,OAA+B;AAAA,MACjC,GAAG;AAAA,MACH,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,IAAI;AAAA,IACR;AAEA,UAAM,SAAS,KAAK,UAAU;AAC9B,QAAI,CAAC,QAAQ;AACT,YAAM,IAAI;AAAA,QACN,gCAA0B,UAAU;AAAA,QACpC;AAAA,UACI,kBAAkB,CAAC,GAAG,MAAM,IAAI,EAAE;AAAA,UAClC,MAAM;AAAA,QACV;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AAAA,EAEQ,0BAA0B,MAA+B;AAC7D,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAOK,KAAK,OAAO,OAAO,KAAK;AAAA,mBACzB,KAAK,OAAO,OAAO,IAAI;AAAA,mBACvB,KAAK,OAAO,IAAI;AAAA;AAAA,mBAEhB,KAAK,OAAO,UAAU;AAAA,qBACpB,IAAI;AAAA;AAAA;AAAA;AAAA,EAIrB;AAAA,EAEA,MAAc,iBAAiB,KAAmC;AAC9D,UAAM,SAAS,SAAS,GAAG;AAC3B,UAAM,OAAO,QAAQ,UAAU,MAAM,wBAAwB;AAE7D,QAAI,CAAC,MAAM;AACP,YAAM,IAAI,UAAU,wDAAqD,eAAe,EAAE,IAAI,CAAC;AAAA,IACnG;AAEA,QAAI,KAAK,QAAQ;AACb,YAAM,QAAQ,MAAM,QAAQ,KAAK,OAAO,GAAG,IAAI,KAAK,OAAO,IAAI,CAAC,IAAI,KAAK,OAAO;AAChF,YAAM,IAAI,UAAU,eAAe,OAAO,OAAO,mBAAmB,IAAI,cAAc,KAAK,MAAM;AAAA,IACrG;AAEA,UAAM,MAAM,KAAK;AACjB,UAAM,MAAM,MAAM,QAAQ,KAAK,UAAU,gBAAgB,IACnD,KAAK,UAAU,iBAAiB,CAAC,IACjC,KAAK,UAAU;AAErB,QAAI,CAAC,KAAK;AACN,YAAM,IAAI,UAAU,4DAA4D,aAAa;AAAA,IACjG;AAEA,UAAM,gBAA0B,CAAC;AACjC,QAAI,IAAI,eAAe;AACnB,YAAM,WAAW,MAAM,QAAQ,IAAI,cAAc,GAAG,IAC9C,IAAI,cAAc,MAClB,CAAC,IAAI,cAAc,GAAG;AAC5B,eAAS,QAAQ,CAAC,MAAW,cAAc,KAAK,EAAE,GAAG,CAAC;AAAA,IAC1D;AAEA,WAAO;AAAA,MACH,iBAAiB,IAAI;AAAA,MACrB,YAAY,IAAI;AAAA,MAChB,gBAAgB,IAAI;AAAA,MACpB,OAAO,IAAI;AAAA,MACX,KAAK,IAAI;AAAA,MACT,gBAAgB,IAAI;AAAA,MACpB,WAAW,IAAI;AAAA,MACf,eAAe,cAAc,SAAS,IAAI,gBAAgB;AAAA,IAC9D;AAAA,EACJ;AACJ;;;AC1dO,SAAS,aACZ,aACA,YACA,OACA,WACM;AAEN,QAAM,QAAQ,YAAY;AAC1B,QAAM,cAAc,MAAM,WAAW,IAC/B,GAAG,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,MAAM,UAAU,GAAG,CAAC,CAAC,KAC1E;AAGN,QAAM,UAAU,WAAW;AAC3B,QAAM,SAAS,WAAW,eAAe,SAAS,UAAU,cAAc,EAAE,IAAI;AAGhF,QAAM,SAAqB;AAAA,IACvB,KAAK;AAAA;AAAA,IACL,OAAO;AAAA,IACP,MAAM,SAAS,YAAY,EAAE;AAAA,IAC7B,QAAQ,YAAY;AAAA,IACpB,SAAS,YAAY;AAAA,IACrB,QAAQ,YAAY;AAAA,IACpB,SAAS,WAAW,MAAM,QAAQ,CAAC,CAAC;AAAA,IACpC,QAAQ;AAAA;AAAA,IACR,KAAK;AAAA;AAAA,IACL,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,YAAY;AAAA;AAAA,IACZ,QAAQ,SAAS,YAAY,KAAK,EAAE;AAAA,EACxC;AAGA,QAAM,aAAa,KAAK,UAAU,MAAM;AAGxC,MAAI,SAAS,OAAO,WAAW,cACzB,OAAO,KAAK,UAAU,EAAE,SAAS,QAAQ,IACzC,KAAK,UAAU;AAGrB,SAAO,oCAAoC,MAAM;AACrD;","names":["TipoComprobante","Concepto","TipoDocumento"]}
|
package/package.json
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arca-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "SDK moderna en TypeScript para ARCA (ex-AFIP)",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.
|
|
7
|
-
"module": "./dist/index.
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
-
"types":
|
|
12
|
-
"import": "./dist/index.d.ts",
|
|
13
|
-
"require": "./dist/index.d.cts"
|
|
14
|
-
},
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
15
12
|
"import": "./dist/index.js",
|
|
16
13
|
"require": "./dist/index.cjs"
|
|
17
14
|
}
|
|
@@ -23,7 +20,7 @@
|
|
|
23
20
|
],
|
|
24
21
|
"scripts": {
|
|
25
22
|
"dev": "tsup src/index.ts --watch",
|
|
26
|
-
"build": "tsup
|
|
23
|
+
"build": "tsup",
|
|
27
24
|
"test": "vitest run",
|
|
28
25
|
"test:watch": "vitest",
|
|
29
26
|
"test:coverage": "vitest run --coverage",
|