@lapyme/arca 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -31
- package/dist/errors.d.ts +9 -1
- package/dist/errors.js +9 -0
- package/dist/errors.js.map +1 -1
- package/dist/{index-vWZOjFDO.d.ts → index-BLq3d6xg.d.ts} +1 -1
- package/dist/index.d.ts +5 -7
- package/dist/index.js +507 -213
- package/dist/index.js.map +1 -1
- package/dist/padron.d.ts +2 -2
- package/dist/padron.js +6 -3
- package/dist/padron.js.map +1 -1
- package/dist/{types-DWsYue4B.d.ts → types-SloiIQkT.d.ts} +10 -10
- package/dist/types.d.ts +1 -1
- package/dist/wsfe.d.ts +83 -8
- package/dist/wsfe.js +256 -67
- package/dist/wsfe.js.map +1 -1
- package/dist/wsmtxca.d.ts +2 -2
- package/dist/wsmtxca.js.map +1 -1
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -13,6 +13,14 @@ var ArcaConfigurationError = class extends ArcaError {
|
|
|
13
13
|
super(message, "ARCA_CONFIGURATION_ERROR", options);
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
|
+
var ArcaInputError = class extends ArcaError {
|
|
17
|
+
name = "ArcaInputError";
|
|
18
|
+
detail;
|
|
19
|
+
constructor(message, options) {
|
|
20
|
+
super(message, "ARCA_INPUT_ERROR", options);
|
|
21
|
+
this.detail = options?.detail;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
16
24
|
var ArcaTransportError = class extends ArcaError {
|
|
17
25
|
name = "ArcaTransportError";
|
|
18
26
|
statusCode;
|
|
@@ -50,15 +58,17 @@ var ARCA_ENV_VARIABLES = {
|
|
|
50
58
|
taxId: "ARCA_TAX_ID",
|
|
51
59
|
certificatePem: "ARCA_CERTIFICATE_PEM",
|
|
52
60
|
privateKeyPem: "ARCA_PRIVATE_KEY_PEM",
|
|
53
|
-
environment: "ARCA_ENVIRONMENT"
|
|
54
|
-
wsaaCacheMode: "ARCA_WSAA_CACHE_MODE",
|
|
55
|
-
wsaaCacheDirectory: "ARCA_WSAA_CACHE_DIRECTORY"
|
|
61
|
+
environment: "ARCA_ENVIRONMENT"
|
|
56
62
|
};
|
|
57
63
|
var PRIVATE_KEY_PEM_PREFIXES = [
|
|
58
64
|
"-----BEGIN PRIVATE KEY-----",
|
|
59
65
|
"-----BEGIN RSA PRIVATE KEY-----",
|
|
60
66
|
"-----BEGIN ENCRYPTED PRIVATE KEY-----"
|
|
61
67
|
];
|
|
68
|
+
var VALID_ARCA_LOG_LEVELS = ["debug", "info", "warn", "error"];
|
|
69
|
+
var DEFAULT_ARCA_TIMEOUT_MS = 3e4;
|
|
70
|
+
var DEFAULT_ARCA_RETRIES = 0;
|
|
71
|
+
var DEFAULT_ARCA_RETRY_DELAY_MS = 500;
|
|
62
72
|
function resolveArcaEnvironment(production) {
|
|
63
73
|
return production ? "production" : "test";
|
|
64
74
|
}
|
|
@@ -70,34 +80,21 @@ function createArcaClientConfigFromEnv(options = {}) {
|
|
|
70
80
|
};
|
|
71
81
|
const environmentInput = readEnv(env, variableNames.environment);
|
|
72
82
|
const environmentValue = normalizeEnvironmentValue(environmentInput);
|
|
73
|
-
const cacheModeInput = readEnv(env, variableNames.wsaaCacheMode);
|
|
74
|
-
const cacheModeValue = normalizeCacheMode(cacheModeInput);
|
|
75
83
|
const config = {
|
|
76
84
|
taxId: readEnv(env, variableNames.taxId) ?? "",
|
|
77
85
|
certificatePem: readEnv(env, variableNames.certificatePem) ?? "",
|
|
78
86
|
privateKeyPem: readEnv(env, variableNames.privateKeyPem) ?? "",
|
|
79
87
|
environment: environmentValue ?? environmentInput ?? options.defaultEnvironment ?? "test"
|
|
80
88
|
};
|
|
81
|
-
if (cacheModeValue === "disk" || cacheModeInput === "disk") {
|
|
82
|
-
config.wsaa = {
|
|
83
|
-
cache: {
|
|
84
|
-
mode: cacheModeValue ?? cacheModeInput,
|
|
85
|
-
directory: readEnv(env, variableNames.wsaaCacheDirectory) ?? ""
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
} else if (cacheModeValue === "memory" || cacheModeInput !== void 0) {
|
|
89
|
-
config.wsaa = {
|
|
90
|
-
cache: {
|
|
91
|
-
mode: cacheModeValue ?? cacheModeInput
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
89
|
assertArcaClientConfig(config);
|
|
96
90
|
return normalizeArcaClientConfig(config);
|
|
97
91
|
}
|
|
98
92
|
function assertArcaClientConfig(config) {
|
|
99
93
|
const invalidFields = [];
|
|
100
94
|
const normalized = normalizeArcaClientConfig(config);
|
|
95
|
+
const timeout = normalized.timeout ?? DEFAULT_ARCA_TIMEOUT_MS;
|
|
96
|
+
const retries = normalized.retries ?? DEFAULT_ARCA_RETRIES;
|
|
97
|
+
const retryDelay = normalized.retryDelay ?? DEFAULT_ARCA_RETRY_DELAY_MS;
|
|
101
98
|
if (!/^\d{11}$/.test(normalized.taxId)) {
|
|
102
99
|
invalidFields.push("taxId");
|
|
103
100
|
}
|
|
@@ -112,15 +109,21 @@ function assertArcaClientConfig(config) {
|
|
|
112
109
|
if (!ARCA_ENVIRONMENTS.includes(normalized.environment)) {
|
|
113
110
|
invalidFields.push("environment");
|
|
114
111
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
112
|
+
if (!Number.isFinite(timeout) || timeout <= 0) {
|
|
113
|
+
invalidFields.push("timeout");
|
|
114
|
+
}
|
|
115
|
+
if (!Number.isInteger(retries) || retries < 0) {
|
|
116
|
+
invalidFields.push("retries");
|
|
117
|
+
}
|
|
118
|
+
if (!Number.isFinite(retryDelay) || retryDelay < 0) {
|
|
119
|
+
invalidFields.push("retryDelay");
|
|
120
|
+
}
|
|
121
|
+
const loggerLevel = normalized.logger?.level;
|
|
122
|
+
if (loggerLevel !== void 0 && !VALID_ARCA_LOG_LEVELS.includes(loggerLevel)) {
|
|
123
|
+
invalidFields.push("logger.level");
|
|
124
|
+
}
|
|
125
|
+
if (normalized.logger?.log !== void 0 && typeof normalized.logger.log !== "function") {
|
|
126
|
+
invalidFields.push("logger.log");
|
|
124
127
|
}
|
|
125
128
|
if (invalidFields.length > 0) {
|
|
126
129
|
throw new ArcaConfigurationError(
|
|
@@ -191,32 +194,21 @@ function getArcaServiceConfig(service) {
|
|
|
191
194
|
}
|
|
192
195
|
function normalizeArcaClientConfig(config) {
|
|
193
196
|
const normalizedEnvironment = normalizeEnvironmentValue(String(config.environment)) ?? config.environment;
|
|
194
|
-
const
|
|
197
|
+
const normalizedLoggerLevel = normalizeLogLevelValue(config.logger?.level);
|
|
195
198
|
return {
|
|
196
199
|
taxId: config.taxId.trim(),
|
|
197
200
|
certificatePem: config.certificatePem.trim(),
|
|
198
201
|
privateKeyPem: config.privateKeyPem.trim(),
|
|
199
202
|
environment: normalizedEnvironment,
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
+
timeout: config.timeout ?? DEFAULT_ARCA_TIMEOUT_MS,
|
|
204
|
+
retries: config.retries ?? DEFAULT_ARCA_RETRIES,
|
|
205
|
+
retryDelay: config.retryDelay ?? DEFAULT_ARCA_RETRY_DELAY_MS,
|
|
206
|
+
...config.logger === void 0 ? {} : {
|
|
207
|
+
logger: {
|
|
208
|
+
...config.logger,
|
|
209
|
+
...normalizedLoggerLevel === void 0 ? {} : { level: normalizedLoggerLevel }
|
|
203
210
|
}
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
function normalizeWsaaCacheConfig(cache) {
|
|
208
|
-
if (!cache) {
|
|
209
|
-
return void 0;
|
|
210
|
-
}
|
|
211
|
-
const mode = normalizeCacheMode(cache.mode) ?? "memory";
|
|
212
|
-
if (mode !== "disk") {
|
|
213
|
-
return {
|
|
214
|
-
mode: "memory"
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
return {
|
|
218
|
-
mode,
|
|
219
|
-
directory: ("directory" in cache ? cache.directory : "").trim()
|
|
211
|
+
}
|
|
220
212
|
};
|
|
221
213
|
}
|
|
222
214
|
function normalizeEnvironmentValue(value) {
|
|
@@ -229,18 +221,69 @@ function normalizeEnvironmentValue(value) {
|
|
|
229
221
|
}
|
|
230
222
|
return void 0;
|
|
231
223
|
}
|
|
232
|
-
function
|
|
224
|
+
function readEnv(env, variableName) {
|
|
225
|
+
return env[variableName]?.trim() || void 0;
|
|
226
|
+
}
|
|
227
|
+
function normalizeLogLevelValue(value) {
|
|
233
228
|
if (!value) {
|
|
234
229
|
return void 0;
|
|
235
230
|
}
|
|
236
231
|
const normalized = value.trim().toLowerCase();
|
|
237
|
-
if (normalized
|
|
232
|
+
if (VALID_ARCA_LOG_LEVELS.includes(normalized)) {
|
|
238
233
|
return normalized;
|
|
239
234
|
}
|
|
240
|
-
return
|
|
235
|
+
return value;
|
|
241
236
|
}
|
|
242
|
-
|
|
243
|
-
|
|
237
|
+
|
|
238
|
+
// src/internal/logger.ts
|
|
239
|
+
var ARCA_LOG_LEVELS = ["debug", "info", "warn", "error"];
|
|
240
|
+
function createArcaLogger(config) {
|
|
241
|
+
const disabled = config?.disabled ?? false;
|
|
242
|
+
const level = resolveArcaLogLevel(config?.level);
|
|
243
|
+
const sink = config?.log ?? defaultArcaLog;
|
|
244
|
+
const log = (messageLevel, message, ...args) => {
|
|
245
|
+
if (disabled || !shouldLog(level, messageLevel)) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
sink(messageLevel, message, ...args);
|
|
249
|
+
};
|
|
250
|
+
return {
|
|
251
|
+
disabled,
|
|
252
|
+
level,
|
|
253
|
+
log,
|
|
254
|
+
debug(message, ...args) {
|
|
255
|
+
log("debug", message, ...args);
|
|
256
|
+
},
|
|
257
|
+
info(message, ...args) {
|
|
258
|
+
log("info", message, ...args);
|
|
259
|
+
},
|
|
260
|
+
warn(message, ...args) {
|
|
261
|
+
log("warn", message, ...args);
|
|
262
|
+
},
|
|
263
|
+
error(message, ...args) {
|
|
264
|
+
log("error", message, ...args);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function resolveArcaLogLevel(level) {
|
|
269
|
+
if (isArcaLogLevel(level)) {
|
|
270
|
+
return level;
|
|
271
|
+
}
|
|
272
|
+
const envLevel = process.env.ARCA_LOG_LEVEL?.trim().toLowerCase();
|
|
273
|
+
if (isArcaLogLevel(envLevel)) {
|
|
274
|
+
return envLevel;
|
|
275
|
+
}
|
|
276
|
+
return "warn";
|
|
277
|
+
}
|
|
278
|
+
function shouldLog(threshold, messageLevel) {
|
|
279
|
+
return ARCA_LOG_LEVELS.indexOf(messageLevel) >= ARCA_LOG_LEVELS.indexOf(threshold);
|
|
280
|
+
}
|
|
281
|
+
function isArcaLogLevel(value) {
|
|
282
|
+
return ARCA_LOG_LEVELS.includes(value);
|
|
283
|
+
}
|
|
284
|
+
function defaultArcaLog(level, message, ...args) {
|
|
285
|
+
const method = level === "debug" ? console.debug : level === "info" ? console.info : level === "warn" ? console.warn : console.error;
|
|
286
|
+
method(message, ...args);
|
|
244
287
|
}
|
|
245
288
|
|
|
246
289
|
// src/services/padron.ts
|
|
@@ -262,7 +305,7 @@ function createPadronService(options) {
|
|
|
262
305
|
const datosGenerales = record.datosGenerales;
|
|
263
306
|
return {
|
|
264
307
|
taxId: String(record.idPersona ?? ""),
|
|
265
|
-
...record.tipoPersona
|
|
308
|
+
...record.tipoPersona === void 0 ? {} : { personType: String(record.tipoPersona) },
|
|
266
309
|
...datosGenerales ? { name: extractPadronName(datosGenerales) } : {},
|
|
267
310
|
raw: record
|
|
268
311
|
};
|
|
@@ -281,7 +324,7 @@ function createPadronService(options) {
|
|
|
281
324
|
}
|
|
282
325
|
const record = raw;
|
|
283
326
|
const idPersona = record.idPersona;
|
|
284
|
-
const taxIds = Array.isArray(idPersona) ? idPersona.map(String) : idPersona
|
|
327
|
+
const taxIds = Array.isArray(idPersona) ? idPersona.map(String) : idPersona === void 0 ? [] : [String(idPersona)];
|
|
285
328
|
return {
|
|
286
329
|
taxIds,
|
|
287
330
|
raw: record
|
|
@@ -331,7 +374,10 @@ async function executePadronOperation(options, service, operation, body) {
|
|
|
331
374
|
}
|
|
332
375
|
return operationResponse.return ?? null;
|
|
333
376
|
} catch (error) {
|
|
334
|
-
if (error instanceof ArcaSoapFaultError &&
|
|
377
|
+
if (error instanceof ArcaSoapFaultError && // Public Padron A5/A13 WSDLs expose only a generic validation fault, so
|
|
378
|
+
// there is no documented not-found-specific fault code to match here.
|
|
379
|
+
// Keep the current message fallback, but treat it as fragile.
|
|
380
|
+
error.message.toLowerCase().includes("no existe")) {
|
|
335
381
|
return null;
|
|
336
382
|
}
|
|
337
383
|
throw error;
|
|
@@ -340,43 +386,68 @@ async function executePadronOperation(options, service, operation, body) {
|
|
|
340
386
|
|
|
341
387
|
// src/services/wsfe.ts
|
|
342
388
|
function createWsfeService(options) {
|
|
343
|
-
async function
|
|
344
|
-
representedTaxId,
|
|
345
|
-
salesPoint,
|
|
346
|
-
voucherType,
|
|
347
|
-
forceAuthRefresh
|
|
348
|
-
}) {
|
|
389
|
+
async function executeWsfeAuthenticatedOperation(operation, input, body = {}) {
|
|
349
390
|
const auth = await options.auth.login("wsfe", {
|
|
350
|
-
representedTaxId,
|
|
351
|
-
forceRefresh: forceAuthRefresh
|
|
391
|
+
representedTaxId: input.representedTaxId,
|
|
392
|
+
forceRefresh: input.forceAuthRefresh
|
|
352
393
|
});
|
|
353
394
|
const response = await options.soap.execute({
|
|
354
395
|
service: "wsfe",
|
|
355
|
-
operation
|
|
396
|
+
operation,
|
|
356
397
|
body: {
|
|
357
398
|
Auth: createWsfeAuth(
|
|
358
|
-
representedTaxId ?? options.config.taxId,
|
|
399
|
+
input.representedTaxId ?? options.config.taxId,
|
|
359
400
|
auth.token,
|
|
360
401
|
auth.sign
|
|
361
402
|
),
|
|
362
|
-
|
|
363
|
-
CbteTipo: voucherType
|
|
403
|
+
...body
|
|
364
404
|
}
|
|
365
405
|
});
|
|
366
|
-
|
|
406
|
+
return unwrapWsfeOperationResult(operation, response.result);
|
|
407
|
+
}
|
|
408
|
+
async function executeWsfeOperation(operation, body = {}) {
|
|
409
|
+
const response = await options.soap.execute({
|
|
410
|
+
service: "wsfe",
|
|
411
|
+
operation,
|
|
412
|
+
body
|
|
413
|
+
});
|
|
414
|
+
return unwrapWsfeOperationResult(operation, response.result);
|
|
415
|
+
}
|
|
416
|
+
async function getNextVoucherNumber({
|
|
417
|
+
representedTaxId,
|
|
418
|
+
salesPoint,
|
|
419
|
+
voucherType,
|
|
420
|
+
forceAuthRefresh
|
|
421
|
+
}) {
|
|
422
|
+
const result = await executeWsfeAuthenticatedOperation(
|
|
367
423
|
"FECompUltimoAutorizado",
|
|
368
|
-
|
|
424
|
+
{
|
|
425
|
+
representedTaxId,
|
|
426
|
+
forceAuthRefresh
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
PtoVta: salesPoint,
|
|
430
|
+
CbteTipo: voucherType
|
|
431
|
+
}
|
|
369
432
|
);
|
|
370
433
|
return Number(result.CbteNro ?? 0) + 1;
|
|
371
434
|
}
|
|
435
|
+
async function getWsfeCatalog(operation, resultKey, input) {
|
|
436
|
+
const result = await executeWsfeAuthenticatedOperation(operation, {
|
|
437
|
+
representedTaxId: input.representedTaxId,
|
|
438
|
+
forceAuthRefresh: input.forceAuthRefresh
|
|
439
|
+
});
|
|
440
|
+
return getWsfeResultEntries(result, resultKey).map(mapWsfeCatalogEntry);
|
|
441
|
+
}
|
|
372
442
|
return {
|
|
373
443
|
async createNextVoucher({ representedTaxId, data }) {
|
|
374
|
-
const
|
|
444
|
+
const normalizedInput = normalizeWsfeVoucherInput(data);
|
|
445
|
+
const voucherNumber = await getNextVoucherNumber({
|
|
375
446
|
representedTaxId,
|
|
376
|
-
salesPoint:
|
|
377
|
-
voucherType:
|
|
447
|
+
salesPoint: normalizedInput.salesPoint,
|
|
448
|
+
voucherType: normalizedInput.voucherType
|
|
378
449
|
});
|
|
379
|
-
const requestData = mapWsfeVoucherInput(
|
|
450
|
+
const requestData = mapWsfeVoucherInput(normalizedInput, voucherNumber);
|
|
380
451
|
const auth = await options.auth.login("wsfe", { representedTaxId });
|
|
381
452
|
const response = await options.soap.execute({
|
|
382
453
|
service: "wsfe",
|
|
@@ -390,8 +461,8 @@ function createWsfeService(options) {
|
|
|
390
461
|
FeCAEReq: {
|
|
391
462
|
FeCabReq: {
|
|
392
463
|
CantReg: 1,
|
|
393
|
-
PtoVta:
|
|
394
|
-
CbteTipo:
|
|
464
|
+
PtoVta: normalizedInput.salesPoint,
|
|
465
|
+
CbteTipo: normalizedInput.voucherType
|
|
395
466
|
},
|
|
396
467
|
FeDetReq: {
|
|
397
468
|
FECAEDetRequest: requestData
|
|
@@ -419,61 +490,89 @@ function createWsfeService(options) {
|
|
|
419
490
|
raw: result
|
|
420
491
|
};
|
|
421
492
|
},
|
|
422
|
-
|
|
493
|
+
getNextVoucherNumber,
|
|
494
|
+
getLastVoucher(input) {
|
|
495
|
+
return getNextVoucherNumber(input);
|
|
496
|
+
},
|
|
423
497
|
async getSalesPoints({ representedTaxId, forceAuthRefresh }) {
|
|
424
|
-
const
|
|
425
|
-
representedTaxId,
|
|
426
|
-
forceRefresh: forceAuthRefresh
|
|
427
|
-
});
|
|
428
|
-
const response = await options.soap.execute({
|
|
429
|
-
service: "wsfe",
|
|
430
|
-
operation: "FEParamGetPtosVenta",
|
|
431
|
-
body: {
|
|
432
|
-
Auth: createWsfeAuth(
|
|
433
|
-
representedTaxId ?? options.config.taxId,
|
|
434
|
-
auth.token,
|
|
435
|
-
auth.sign
|
|
436
|
-
)
|
|
437
|
-
}
|
|
438
|
-
});
|
|
439
|
-
const result = unwrapWsfeOperationResult(
|
|
498
|
+
const result = await executeWsfeAuthenticatedOperation(
|
|
440
499
|
"FEParamGetPtosVenta",
|
|
441
|
-
|
|
500
|
+
{
|
|
501
|
+
representedTaxId,
|
|
502
|
+
forceAuthRefresh
|
|
503
|
+
}
|
|
442
504
|
);
|
|
443
|
-
const
|
|
444
|
-
const rawPoints = resultGet?.PtoVenta;
|
|
505
|
+
const rawPoints = result.ResultGet?.PtoVenta;
|
|
445
506
|
if (!rawPoints) {
|
|
446
507
|
return [];
|
|
447
508
|
}
|
|
448
509
|
const entries = Array.isArray(rawPoints) ? rawPoints : [rawPoints];
|
|
449
510
|
return entries.map(mapWsfeSalesPoint);
|
|
450
511
|
},
|
|
512
|
+
getVoucherTypes(input) {
|
|
513
|
+
return getWsfeCatalog("FEParamGetTiposCbte", "CbteTipo", input);
|
|
514
|
+
},
|
|
515
|
+
getDocumentTypes(input) {
|
|
516
|
+
return getWsfeCatalog("FEParamGetTiposDoc", "DocTipo", input);
|
|
517
|
+
},
|
|
518
|
+
getConceptTypes(input) {
|
|
519
|
+
return getWsfeCatalog("FEParamGetTiposConcepto", "ConceptoTipo", input);
|
|
520
|
+
},
|
|
521
|
+
async getCurrencyTypes({ representedTaxId, forceAuthRefresh }) {
|
|
522
|
+
const result = await executeWsfeAuthenticatedOperation(
|
|
523
|
+
"FEParamGetTiposMonedas",
|
|
524
|
+
{
|
|
525
|
+
representedTaxId,
|
|
526
|
+
forceAuthRefresh
|
|
527
|
+
}
|
|
528
|
+
);
|
|
529
|
+
return getWsfeResultEntries(result, "Moneda").map(mapWsfeCurrencyType);
|
|
530
|
+
},
|
|
531
|
+
getVatRates(input) {
|
|
532
|
+
return getWsfeCatalog("FEParamGetTiposIva", "IvaTipo", input);
|
|
533
|
+
},
|
|
534
|
+
getTaxTypes(input) {
|
|
535
|
+
return getWsfeCatalog("FEParamGetTiposTributos", "TributoTipo", input);
|
|
536
|
+
},
|
|
537
|
+
getOptionalTypes(input) {
|
|
538
|
+
return getWsfeCatalog("FEParamGetTiposOpcional", "OpcionalTipo", input);
|
|
539
|
+
},
|
|
540
|
+
async getServerStatus() {
|
|
541
|
+
const result = await executeWsfeOperation("FEDummy");
|
|
542
|
+
return mapWsfeServerStatus(result);
|
|
543
|
+
},
|
|
544
|
+
async getQuotation({ currencyId, representedTaxId, forceAuthRefresh }) {
|
|
545
|
+
const result = await executeWsfeAuthenticatedOperation(
|
|
546
|
+
"FEParamGetCotizacion",
|
|
547
|
+
{
|
|
548
|
+
representedTaxId,
|
|
549
|
+
forceAuthRefresh
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
MonId: currencyId
|
|
553
|
+
}
|
|
554
|
+
);
|
|
555
|
+
const raw = result.ResultGet ?? {};
|
|
556
|
+
return mapWsfeQuotation(raw);
|
|
557
|
+
},
|
|
451
558
|
async getVoucherInfo({
|
|
452
559
|
representedTaxId,
|
|
453
560
|
number,
|
|
454
561
|
salesPoint,
|
|
455
562
|
voucherType
|
|
456
563
|
}) {
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
representedTaxId ?? options.config.taxId,
|
|
464
|
-
auth.token,
|
|
465
|
-
auth.sign
|
|
466
|
-
),
|
|
564
|
+
const result = await executeWsfeAuthenticatedOperation(
|
|
565
|
+
"FECompConsultar",
|
|
566
|
+
{
|
|
567
|
+
representedTaxId
|
|
568
|
+
},
|
|
569
|
+
{
|
|
467
570
|
FeCompConsReq: {
|
|
468
571
|
CbteNro: number,
|
|
469
572
|
PtoVta: salesPoint,
|
|
470
573
|
CbteTipo: voucherType
|
|
471
574
|
}
|
|
472
575
|
}
|
|
473
|
-
});
|
|
474
|
-
const result = unwrapWsfeOperationResult(
|
|
475
|
-
"FECompConsultar",
|
|
476
|
-
response.result
|
|
477
576
|
);
|
|
478
577
|
const raw = result.ResultGet ?? null;
|
|
479
578
|
if (!raw) {
|
|
@@ -520,8 +619,8 @@ function mapWsfeVoucherInput(input, voucherNumber) {
|
|
|
520
619
|
Tipo: v.type,
|
|
521
620
|
PtoVta: v.salesPoint,
|
|
522
621
|
Nro: v.number,
|
|
523
|
-
...v.taxId
|
|
524
|
-
...v.voucherDate
|
|
622
|
+
...v.taxId === void 0 ? {} : { Cuit: v.taxId },
|
|
623
|
+
...v.voucherDate === void 0 ? {} : { CbteFch: v.voucherDate }
|
|
525
624
|
}))
|
|
526
625
|
};
|
|
527
626
|
}
|
|
@@ -529,7 +628,7 @@ function mapWsfeVoucherInput(input, voucherNumber) {
|
|
|
529
628
|
data.Tributos = {
|
|
530
629
|
Tributo: input.taxes.map((t) => ({
|
|
531
630
|
Id: t.id,
|
|
532
|
-
...t.description
|
|
631
|
+
...t.description === void 0 ? {} : { Desc: t.description },
|
|
533
632
|
BaseImp: t.baseAmount,
|
|
534
633
|
Alic: t.rate,
|
|
535
634
|
Importe: t.amount
|
|
@@ -564,25 +663,144 @@ function mapWsfeVoucherInput(input, voucherNumber) {
|
|
|
564
663
|
}
|
|
565
664
|
return data;
|
|
566
665
|
}
|
|
666
|
+
function normalizeWsfeVoucherInput(input) {
|
|
667
|
+
const {
|
|
668
|
+
voucherDate,
|
|
669
|
+
serviceStartDate,
|
|
670
|
+
serviceEndDate,
|
|
671
|
+
paymentDueDate,
|
|
672
|
+
associatedVouchers,
|
|
673
|
+
...rest
|
|
674
|
+
} = input;
|
|
675
|
+
return {
|
|
676
|
+
...rest,
|
|
677
|
+
voucherDate: normalizeWsfeDateInput(voucherDate, "voucherDate"),
|
|
678
|
+
...serviceStartDate === void 0 ? {} : {
|
|
679
|
+
serviceStartDate: normalizeWsfeDateInput(
|
|
680
|
+
serviceStartDate,
|
|
681
|
+
"serviceStartDate"
|
|
682
|
+
)
|
|
683
|
+
},
|
|
684
|
+
...serviceEndDate === void 0 ? {} : {
|
|
685
|
+
serviceEndDate: normalizeWsfeDateInput(
|
|
686
|
+
serviceEndDate,
|
|
687
|
+
"serviceEndDate"
|
|
688
|
+
)
|
|
689
|
+
},
|
|
690
|
+
...paymentDueDate === void 0 ? {} : {
|
|
691
|
+
paymentDueDate: normalizeWsfeDateInput(
|
|
692
|
+
paymentDueDate,
|
|
693
|
+
"paymentDueDate"
|
|
694
|
+
)
|
|
695
|
+
},
|
|
696
|
+
...associatedVouchers === void 0 ? {} : {
|
|
697
|
+
associatedVouchers: associatedVouchers.map((voucher, index) => {
|
|
698
|
+
const { voucherDate: associatedVoucherDate, ...associatedRest } = voucher;
|
|
699
|
+
return {
|
|
700
|
+
...associatedRest,
|
|
701
|
+
...associatedVoucherDate === void 0 ? {} : {
|
|
702
|
+
voucherDate: normalizeWsfeDateInput(
|
|
703
|
+
associatedVoucherDate,
|
|
704
|
+
`associatedVouchers[${index}].voucherDate`
|
|
705
|
+
)
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
})
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function normalizeWsfeDateInput(value, fieldName) {
|
|
713
|
+
if (typeof value !== "string") {
|
|
714
|
+
throw new ArcaInputError(
|
|
715
|
+
`Invalid WSFE ${fieldName}: expected a YYYY-MM-DD or YYYYMMDD string`,
|
|
716
|
+
{
|
|
717
|
+
detail: { field: fieldName, value }
|
|
718
|
+
}
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
const normalizedValue = value.trim();
|
|
722
|
+
const afipMatch = normalizedValue.match(/^(\d{4})(\d{2})(\d{2})$/);
|
|
723
|
+
if (afipMatch) {
|
|
724
|
+
const [, year, month, day] = afipMatch;
|
|
725
|
+
assertValidCalendarDate(year, month, day, fieldName, normalizedValue);
|
|
726
|
+
return normalizedValue;
|
|
727
|
+
}
|
|
728
|
+
const isoMatch = normalizedValue.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
729
|
+
if (isoMatch) {
|
|
730
|
+
const [, year, month, day] = isoMatch;
|
|
731
|
+
assertValidCalendarDate(year, month, day, fieldName, normalizedValue);
|
|
732
|
+
return `${year}${month}${day}`;
|
|
733
|
+
}
|
|
734
|
+
throw new ArcaInputError(
|
|
735
|
+
`Invalid WSFE ${fieldName}: expected a YYYY-MM-DD or YYYYMMDD string`,
|
|
736
|
+
{
|
|
737
|
+
detail: { field: fieldName, value: normalizedValue }
|
|
738
|
+
}
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
function assertValidCalendarDate(yearInput, monthInput, dayInput, fieldName, value) {
|
|
742
|
+
const year = Number(yearInput);
|
|
743
|
+
const month = Number(monthInput);
|
|
744
|
+
const day = Number(dayInput);
|
|
745
|
+
const candidate = new Date(Date.UTC(year, month - 1, day));
|
|
746
|
+
if (candidate.getUTCFullYear() !== year || candidate.getUTCMonth() !== month - 1 || candidate.getUTCDate() !== day) {
|
|
747
|
+
throw new ArcaInputError(
|
|
748
|
+
`Invalid WSFE ${fieldName}: received a non-existent calendar date`,
|
|
749
|
+
{
|
|
750
|
+
detail: { field: fieldName, value }
|
|
751
|
+
}
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
567
755
|
function mapWsfeSalesPoint(raw) {
|
|
568
756
|
const record = raw;
|
|
569
757
|
return {
|
|
570
758
|
number: Number(record.Nro ?? 0),
|
|
571
|
-
...record.EmisionTipo
|
|
572
|
-
...record.Bloqueado
|
|
573
|
-
...record.FchBaja
|
|
759
|
+
...record.EmisionTipo === void 0 ? {} : { emissionType: String(record.EmisionTipo) },
|
|
760
|
+
...record.Bloqueado === void 0 ? {} : { blocked: String(record.Bloqueado) },
|
|
761
|
+
...record.FchBaja === void 0 ? {} : { deletedSince: String(record.FchBaja) }
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
function mapWsfeCatalogEntry(raw) {
|
|
765
|
+
const record = raw;
|
|
766
|
+
return {
|
|
767
|
+
id: Number(record.Id ?? 0),
|
|
768
|
+
description: String(record.Desc ?? "")
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
function mapWsfeCurrencyType(raw) {
|
|
772
|
+
const record = raw;
|
|
773
|
+
return {
|
|
774
|
+
id: String(record.Id ?? ""),
|
|
775
|
+
description: String(record.Desc ?? ""),
|
|
776
|
+
validFrom: String(record.FchDesde ?? ""),
|
|
777
|
+
validTo: String(record.FchHasta ?? "")
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
function mapWsfeServerStatus(raw) {
|
|
781
|
+
return {
|
|
782
|
+
appServer: String(raw.AppServer ?? ""),
|
|
783
|
+
dbServer: String(raw.DbServer ?? ""),
|
|
784
|
+
authServer: String(raw.AuthServer ?? "")
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
function mapWsfeQuotation(raw) {
|
|
788
|
+
return {
|
|
789
|
+
currencyId: String(raw.MonId ?? ""),
|
|
790
|
+
rate: Number(raw.MonCotiz ?? 0),
|
|
791
|
+
date: String(raw.FchCotiz ?? "")
|
|
574
792
|
};
|
|
575
793
|
}
|
|
576
794
|
function mapWsfeVoucherInfo(raw) {
|
|
577
795
|
return {
|
|
578
796
|
voucherNumber: Number(raw.CbteDesde ?? raw.CbteHasta ?? 0),
|
|
579
|
-
...raw.CbteFch
|
|
580
|
-
...raw.PtoVta
|
|
581
|
-
...raw.CbteTipo
|
|
582
|
-
...raw.ImpTotal
|
|
583
|
-
...raw.Resultado
|
|
584
|
-
...raw.CAE
|
|
585
|
-
...raw.CAEFchVto
|
|
797
|
+
...raw.CbteFch === void 0 ? {} : { voucherDate: String(raw.CbteFch) },
|
|
798
|
+
...raw.PtoVta === void 0 ? {} : { salesPoint: Number(raw.PtoVta) },
|
|
799
|
+
...raw.CbteTipo === void 0 ? {} : { voucherType: Number(raw.CbteTipo) },
|
|
800
|
+
...raw.ImpTotal === void 0 ? {} : { totalAmount: Number(raw.ImpTotal) },
|
|
801
|
+
...raw.Resultado === void 0 ? {} : { result: String(raw.Resultado) },
|
|
802
|
+
...raw.CAE === void 0 ? {} : { cae: String(raw.CAE) },
|
|
803
|
+
...raw.CAEFchVto === void 0 ? {} : { caeExpiry: String(raw.CAEFchVto) },
|
|
586
804
|
raw
|
|
587
805
|
};
|
|
588
806
|
}
|
|
@@ -654,6 +872,15 @@ function normalizeWsfeErrors(rawErrors) {
|
|
|
654
872
|
};
|
|
655
873
|
});
|
|
656
874
|
}
|
|
875
|
+
function getWsfeResultEntries(result, key) {
|
|
876
|
+
const rawEntries = result.ResultGet?.[key];
|
|
877
|
+
if (!rawEntries) {
|
|
878
|
+
return [];
|
|
879
|
+
}
|
|
880
|
+
return (Array.isArray(rawEntries) ? rawEntries : [rawEntries]).map(
|
|
881
|
+
(entry) => entry
|
|
882
|
+
);
|
|
883
|
+
}
|
|
657
884
|
|
|
658
885
|
// src/services/wsmtxca.ts
|
|
659
886
|
function createWsmtxcaService(options) {
|
|
@@ -888,13 +1115,69 @@ var legacyTlsAgent = new https.Agent({
|
|
|
888
1115
|
keepAlive: true,
|
|
889
1116
|
ciphers: "DEFAULT@SECLEVEL=0"
|
|
890
1117
|
});
|
|
891
|
-
var REQUEST_TIMEOUT_MS = 3e4;
|
|
892
1118
|
async function postXml({
|
|
893
1119
|
url,
|
|
894
1120
|
body,
|
|
895
1121
|
contentType,
|
|
896
1122
|
soapAction,
|
|
897
|
-
useLegacyTlsSecurityLevel0 = false
|
|
1123
|
+
useLegacyTlsSecurityLevel0 = false,
|
|
1124
|
+
timeout = 3e4,
|
|
1125
|
+
retries = 0,
|
|
1126
|
+
retryDelay = 500,
|
|
1127
|
+
logger,
|
|
1128
|
+
service,
|
|
1129
|
+
operation
|
|
1130
|
+
}) {
|
|
1131
|
+
const totalAttempts = retries + 1;
|
|
1132
|
+
for (let attempt = 1; attempt <= totalAttempts; attempt += 1) {
|
|
1133
|
+
try {
|
|
1134
|
+
return await postXmlOnce({
|
|
1135
|
+
url,
|
|
1136
|
+
body,
|
|
1137
|
+
contentType,
|
|
1138
|
+
soapAction,
|
|
1139
|
+
useLegacyTlsSecurityLevel0,
|
|
1140
|
+
timeout
|
|
1141
|
+
});
|
|
1142
|
+
} catch (error) {
|
|
1143
|
+
if (!(error instanceof ArcaTransportError)) {
|
|
1144
|
+
throw error;
|
|
1145
|
+
}
|
|
1146
|
+
if (attempt >= totalAttempts) {
|
|
1147
|
+
logger?.error("ARCA transport request failed", {
|
|
1148
|
+
service,
|
|
1149
|
+
operation,
|
|
1150
|
+
url,
|
|
1151
|
+
attempt,
|
|
1152
|
+
attempts: totalAttempts,
|
|
1153
|
+
error
|
|
1154
|
+
});
|
|
1155
|
+
throw error;
|
|
1156
|
+
}
|
|
1157
|
+
const nextAttempt = attempt + 1;
|
|
1158
|
+
logger?.warn(
|
|
1159
|
+
`Retrying ARCA request after transport failure (attempt ${nextAttempt}/${totalAttempts})`,
|
|
1160
|
+
{
|
|
1161
|
+
service,
|
|
1162
|
+
operation,
|
|
1163
|
+
url,
|
|
1164
|
+
attempt: nextAttempt,
|
|
1165
|
+
attempts: totalAttempts,
|
|
1166
|
+
error
|
|
1167
|
+
}
|
|
1168
|
+
);
|
|
1169
|
+
await delay(retryDelay);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
throw new ArcaTransportError("ARCA HTTP request exhausted retries");
|
|
1173
|
+
}
|
|
1174
|
+
async function postXmlOnce({
|
|
1175
|
+
url,
|
|
1176
|
+
body,
|
|
1177
|
+
contentType,
|
|
1178
|
+
soapAction,
|
|
1179
|
+
useLegacyTlsSecurityLevel0,
|
|
1180
|
+
timeout
|
|
898
1181
|
}) {
|
|
899
1182
|
const endpoint = new URL(url);
|
|
900
1183
|
const requestBody = Buffer.from(body, "utf8");
|
|
@@ -983,9 +1266,9 @@ async function postXml({
|
|
|
983
1266
|
});
|
|
984
1267
|
}
|
|
985
1268
|
);
|
|
986
|
-
request.setTimeout(
|
|
1269
|
+
request.setTimeout(timeout, () => {
|
|
987
1270
|
request.destroy(
|
|
988
|
-
new Error(`ARCA HTTP request timed out after ${
|
|
1271
|
+
new Error(`ARCA HTTP request timed out after ${timeout}ms`)
|
|
989
1272
|
);
|
|
990
1273
|
});
|
|
991
1274
|
request.on("error", (error) => {
|
|
@@ -1006,6 +1289,11 @@ function isXmlLikeResponse(body, contentType) {
|
|
|
1006
1289
|
}
|
|
1007
1290
|
return body.trimStart().startsWith("<");
|
|
1008
1291
|
}
|
|
1292
|
+
function delay(ms) {
|
|
1293
|
+
return new Promise((resolve) => {
|
|
1294
|
+
setTimeout(resolve, ms);
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1009
1297
|
|
|
1010
1298
|
// src/internal/xml.ts
|
|
1011
1299
|
import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
@@ -1093,9 +1381,9 @@ function createSoapFaultError(fault) {
|
|
|
1093
1381
|
detail: fault
|
|
1094
1382
|
});
|
|
1095
1383
|
}
|
|
1096
|
-
function getNestedString(value,
|
|
1384
|
+
function getNestedString(value, path) {
|
|
1097
1385
|
let current = value;
|
|
1098
|
-
for (const key of
|
|
1386
|
+
for (const key of path) {
|
|
1099
1387
|
if (!current || typeof current !== "object") {
|
|
1100
1388
|
return null;
|
|
1101
1389
|
}
|
|
@@ -1109,6 +1397,7 @@ function createSoapTransport(options) {
|
|
|
1109
1397
|
return {
|
|
1110
1398
|
async execute(request) {
|
|
1111
1399
|
const serviceConfig = getArcaServiceConfig(request.service);
|
|
1400
|
+
const url = serviceConfig.endpoint[options.config.environment];
|
|
1112
1401
|
const soapActionOperation = request.operation;
|
|
1113
1402
|
const bodyElementName = request.bodyElementName ?? request.operation;
|
|
1114
1403
|
const soapAction = serviceConfig.usesEmptySoapAction ? "" : `${serviceConfig.soapActionBase}${soapActionOperation}`;
|
|
@@ -1122,34 +1411,61 @@ function createSoapTransport(options) {
|
|
|
1122
1411
|
namespaceMode: request.bodyElementNamespaceMode
|
|
1123
1412
|
}
|
|
1124
1413
|
);
|
|
1125
|
-
const
|
|
1126
|
-
|
|
1127
|
-
body: xml,
|
|
1128
|
-
contentType,
|
|
1129
|
-
soapAction: serviceConfig.soapVersion === "1.1" ? soapAction : void 0,
|
|
1130
|
-
useLegacyTlsSecurityLevel0: options.config.environment === "production" && serviceConfig.useLegacyTlsSecurityLevel0 === true
|
|
1131
|
-
});
|
|
1132
|
-
const soapBody = parseSoapBody(responseXml);
|
|
1133
|
-
const [, result] = getSingleBodyEntry(soapBody);
|
|
1134
|
-
return {
|
|
1414
|
+
const startedAt = Date.now();
|
|
1415
|
+
options.logger?.debug("Sending ARCA SOAP request", {
|
|
1135
1416
|
service: request.service,
|
|
1136
1417
|
operation: request.operation,
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1418
|
+
url
|
|
1419
|
+
});
|
|
1420
|
+
try {
|
|
1421
|
+
const responseXml = await postXml({
|
|
1422
|
+
url,
|
|
1423
|
+
body: xml,
|
|
1424
|
+
contentType,
|
|
1425
|
+
soapAction: serviceConfig.soapVersion === "1.1" ? soapAction : void 0,
|
|
1426
|
+
useLegacyTlsSecurityLevel0: options.config.environment === "production" && serviceConfig.useLegacyTlsSecurityLevel0 === true,
|
|
1427
|
+
timeout: options.config.timeout,
|
|
1428
|
+
retries: options.config.retries,
|
|
1429
|
+
retryDelay: options.config.retryDelay,
|
|
1430
|
+
logger: options.logger,
|
|
1431
|
+
service: request.service,
|
|
1432
|
+
operation: request.operation
|
|
1433
|
+
});
|
|
1434
|
+
options.logger?.debug("Received ARCA SOAP response", {
|
|
1435
|
+
service: request.service,
|
|
1436
|
+
operation: request.operation,
|
|
1437
|
+
durationMs: Date.now() - startedAt
|
|
1438
|
+
});
|
|
1439
|
+
const soapBody = parseSoapBody(responseXml);
|
|
1440
|
+
const [, result] = getSingleBodyEntry(soapBody);
|
|
1441
|
+
return {
|
|
1442
|
+
service: request.service,
|
|
1443
|
+
operation: request.operation,
|
|
1444
|
+
raw: responseXml,
|
|
1445
|
+
result
|
|
1446
|
+
};
|
|
1447
|
+
} catch (error) {
|
|
1448
|
+
if (error instanceof ArcaSoapFaultError) {
|
|
1449
|
+
options.logger?.error("ARCA SOAP fault response", {
|
|
1450
|
+
service: request.service,
|
|
1451
|
+
operation: request.operation,
|
|
1452
|
+
url,
|
|
1453
|
+
faultCode: error.faultCode,
|
|
1454
|
+
error
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
throw error;
|
|
1458
|
+
}
|
|
1140
1459
|
}
|
|
1141
1460
|
};
|
|
1142
1461
|
}
|
|
1143
1462
|
|
|
1144
1463
|
// src/wsaa/index.ts
|
|
1145
1464
|
import { createHash } from "crypto";
|
|
1146
|
-
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
1147
|
-
import path from "path";
|
|
1148
1465
|
import forge from "node-forge";
|
|
1149
1466
|
function createWsaaAuthModule(options) {
|
|
1150
1467
|
const cache = /* @__PURE__ */ new Map();
|
|
1151
1468
|
const inFlight = /* @__PURE__ */ new Map();
|
|
1152
|
-
const persistedCache = createPersistedCredentialStore(options.config.wsaa?.cache);
|
|
1153
1469
|
return {
|
|
1154
1470
|
async login(service, authOptions = {}) {
|
|
1155
1471
|
const cacheKey = buildWsaaCacheKey(options.config, service);
|
|
@@ -1159,18 +1475,27 @@ function createWsaaAuthModule(options) {
|
|
|
1159
1475
|
}
|
|
1160
1476
|
const loginPromise = (async () => {
|
|
1161
1477
|
if (!authOptions.forceRefresh) {
|
|
1162
|
-
const cached =
|
|
1163
|
-
cache,
|
|
1164
|
-
cacheKey,
|
|
1165
|
-
persistedCache
|
|
1166
|
-
);
|
|
1478
|
+
const cached = getCachedCredentials(cache, cacheKey);
|
|
1167
1479
|
if (cached) {
|
|
1480
|
+
options.logger?.debug("Attempting WSAA login", {
|
|
1481
|
+
service,
|
|
1482
|
+
source: "cached"
|
|
1483
|
+
});
|
|
1168
1484
|
return cached;
|
|
1169
1485
|
}
|
|
1170
1486
|
}
|
|
1171
|
-
|
|
1487
|
+
options.logger?.debug("Attempting WSAA login", {
|
|
1488
|
+
service,
|
|
1489
|
+
source: "fresh"
|
|
1490
|
+
});
|
|
1491
|
+
const credentials = await requestCredentials(options.config, service, {
|
|
1492
|
+
logger: options.logger
|
|
1493
|
+
});
|
|
1494
|
+
options.logger?.info("WSAA login succeeded", {
|
|
1495
|
+
service,
|
|
1496
|
+
expiresAt: credentials.expiresAt
|
|
1497
|
+
});
|
|
1172
1498
|
cache.set(cacheKey, credentials);
|
|
1173
|
-
await persistedCache.write(cacheKey, credentials);
|
|
1174
1499
|
return credentials;
|
|
1175
1500
|
})();
|
|
1176
1501
|
inFlight.set(cacheKey, loginPromise);
|
|
@@ -1178,15 +1503,27 @@ function createWsaaAuthModule(options) {
|
|
|
1178
1503
|
return await loginPromise;
|
|
1179
1504
|
} catch (error) {
|
|
1180
1505
|
if (!authOptions.forceRefresh && error instanceof ArcaSoapFaultError && error.faultCode === "ns1:coe.alreadyAuthenticated") {
|
|
1181
|
-
const cached =
|
|
1182
|
-
cache,
|
|
1183
|
-
cacheKey,
|
|
1184
|
-
persistedCache
|
|
1185
|
-
);
|
|
1506
|
+
const cached = getCachedCredentials(cache, cacheKey);
|
|
1186
1507
|
if (cached) {
|
|
1508
|
+
options.logger?.warn(
|
|
1509
|
+
"Recovered WSAA coe.alreadyAuthenticated fault",
|
|
1510
|
+
{
|
|
1511
|
+
service,
|
|
1512
|
+
faultCode: error.faultCode
|
|
1513
|
+
}
|
|
1514
|
+
);
|
|
1187
1515
|
return cached;
|
|
1188
1516
|
}
|
|
1189
1517
|
}
|
|
1518
|
+
if (error instanceof ArcaSoapFaultError) {
|
|
1519
|
+
options.logger?.error("WSAA SOAP fault response", {
|
|
1520
|
+
service,
|
|
1521
|
+
operation: "loginCms",
|
|
1522
|
+
url: ARCA_WSAA_CONFIG.endpoint[options.config.environment],
|
|
1523
|
+
faultCode: error.faultCode,
|
|
1524
|
+
error
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1190
1527
|
throw error;
|
|
1191
1528
|
} finally {
|
|
1192
1529
|
inFlight.delete(cacheKey);
|
|
@@ -1194,7 +1531,7 @@ function createWsaaAuthModule(options) {
|
|
|
1194
1531
|
}
|
|
1195
1532
|
};
|
|
1196
1533
|
}
|
|
1197
|
-
async function requestCredentials(config, service) {
|
|
1534
|
+
async function requestCredentials(config, service, options) {
|
|
1198
1535
|
const loginTicketRequestXml = buildLoginTicketRequest(service);
|
|
1199
1536
|
const signedCms = signLoginTicketRequest(loginTicketRequestXml, {
|
|
1200
1537
|
certificatePem: config.certificatePem,
|
|
@@ -1210,7 +1547,13 @@ async function requestCredentials(config, service) {
|
|
|
1210
1547
|
url: ARCA_WSAA_CONFIG.endpoint[config.environment],
|
|
1211
1548
|
body: requestXml,
|
|
1212
1549
|
contentType: 'text/xml; charset="utf-8"',
|
|
1213
|
-
soapAction: ARCA_WSAA_CONFIG.soapActionBase
|
|
1550
|
+
soapAction: ARCA_WSAA_CONFIG.soapActionBase,
|
|
1551
|
+
timeout: config.timeout,
|
|
1552
|
+
retries: config.retries,
|
|
1553
|
+
retryDelay: config.retryDelay,
|
|
1554
|
+
logger: options?.logger,
|
|
1555
|
+
service: "wsaa",
|
|
1556
|
+
operation: "loginCms"
|
|
1214
1557
|
});
|
|
1215
1558
|
const soapBody = parseSoapBody(responseXml);
|
|
1216
1559
|
const [, response] = getSingleBodyEntry(soapBody);
|
|
@@ -1233,64 +1576,13 @@ function getCertificateFingerprint(config) {
|
|
|
1233
1576
|
function isCredentialValid(credentials) {
|
|
1234
1577
|
return new Date(credentials.expiresAt).getTime() - Date.now() > 6e4;
|
|
1235
1578
|
}
|
|
1236
|
-
|
|
1579
|
+
function getCachedCredentials(cache, cacheKey) {
|
|
1237
1580
|
const localCached = cache.get(cacheKey);
|
|
1238
1581
|
if (localCached && isCredentialValid(localCached)) {
|
|
1239
1582
|
return localCached;
|
|
1240
1583
|
}
|
|
1241
|
-
const persisted = await persistedCache.read(cacheKey);
|
|
1242
|
-
if (persisted) {
|
|
1243
|
-
cache.set(cacheKey, persisted);
|
|
1244
|
-
return persisted;
|
|
1245
|
-
}
|
|
1246
1584
|
return null;
|
|
1247
1585
|
}
|
|
1248
|
-
function createPersistedCredentialStore(cacheConfig) {
|
|
1249
|
-
if (!cacheConfig || cacheConfig.mode !== "disk") {
|
|
1250
|
-
return {
|
|
1251
|
-
async read() {
|
|
1252
|
-
return null;
|
|
1253
|
-
},
|
|
1254
|
-
async write() {
|
|
1255
|
-
}
|
|
1256
|
-
};
|
|
1257
|
-
}
|
|
1258
|
-
const cacheDir = path.resolve(cacheConfig.directory);
|
|
1259
|
-
return {
|
|
1260
|
-
async read(cacheKey) {
|
|
1261
|
-
try {
|
|
1262
|
-
const serialized = await readFile(
|
|
1263
|
-
getSharedCacheFilePath(cacheDir, cacheKey),
|
|
1264
|
-
"utf8"
|
|
1265
|
-
);
|
|
1266
|
-
const parsed = JSON.parse(serialized);
|
|
1267
|
-
if (typeof parsed.token !== "string" || typeof parsed.sign !== "string" || typeof parsed.expiresAt !== "string") {
|
|
1268
|
-
return null;
|
|
1269
|
-
}
|
|
1270
|
-
const credentials = {
|
|
1271
|
-
token: parsed.token,
|
|
1272
|
-
sign: parsed.sign,
|
|
1273
|
-
expiresAt: parsed.expiresAt
|
|
1274
|
-
};
|
|
1275
|
-
return isCredentialValid(credentials) ? credentials : null;
|
|
1276
|
-
} catch {
|
|
1277
|
-
return null;
|
|
1278
|
-
}
|
|
1279
|
-
},
|
|
1280
|
-
async write(cacheKey, credentials) {
|
|
1281
|
-
await mkdir(cacheDir, { recursive: true });
|
|
1282
|
-
await writeFile(
|
|
1283
|
-
getSharedCacheFilePath(cacheDir, cacheKey),
|
|
1284
|
-
JSON.stringify(credentials),
|
|
1285
|
-
"utf8"
|
|
1286
|
-
);
|
|
1287
|
-
}
|
|
1288
|
-
};
|
|
1289
|
-
}
|
|
1290
|
-
function getSharedCacheFilePath(cacheDir, cacheKey) {
|
|
1291
|
-
const fileName = `${createHash("sha256").update(cacheKey).digest("hex")}.json`;
|
|
1292
|
-
return path.join(cacheDir, fileName);
|
|
1293
|
-
}
|
|
1294
1586
|
function buildLoginTicketRequest(service) {
|
|
1295
1587
|
const uniqueId = Math.floor(Date.now() / 1e3);
|
|
1296
1588
|
const generationTime = new Date(Date.now() - 5 * 6e4).toISOString().replace(".000Z", "Z");
|
|
@@ -1359,8 +1651,9 @@ function parseLoginTicketResponse(xml) {
|
|
|
1359
1651
|
function createArcaClient(config) {
|
|
1360
1652
|
assertArcaClientConfig(config);
|
|
1361
1653
|
const normalizedConfig = normalizeArcaClientConfig(config);
|
|
1362
|
-
const
|
|
1363
|
-
const
|
|
1654
|
+
const logger = createArcaLogger(normalizedConfig.logger);
|
|
1655
|
+
const auth = createWsaaAuthModule({ config: normalizedConfig, logger });
|
|
1656
|
+
const soap = createSoapTransport({ config: normalizedConfig, logger });
|
|
1364
1657
|
return {
|
|
1365
1658
|
config: normalizedConfig,
|
|
1366
1659
|
wsfe: createWsfeService({ config: normalizedConfig, auth, soap }),
|
|
@@ -1373,6 +1666,7 @@ export {
|
|
|
1373
1666
|
ARCA_ENV_VARIABLES,
|
|
1374
1667
|
ArcaConfigurationError,
|
|
1375
1668
|
ArcaError,
|
|
1669
|
+
ArcaInputError,
|
|
1376
1670
|
ArcaServiceError,
|
|
1377
1671
|
ArcaSoapFaultError,
|
|
1378
1672
|
ArcaTransportError,
|