facturas 0.11.0 → 0.12.1
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 +4 -4
- package/dist/{chunk-LOWV7IF6.mjs → chunk-2PYF5JFG.mjs} +619 -128
- package/dist/chunk-2PYF5JFG.mjs.map +1 -0
- package/dist/{chunk-ALH5IPSY.mjs → chunk-D5ZWXTCV.mjs} +21 -12
- package/dist/chunk-D5ZWXTCV.mjs.map +1 -0
- package/dist/{chunk-5KCTRQJX.mjs → chunk-QHYJ424J.mjs} +27 -13
- package/dist/chunk-QHYJ424J.mjs.map +1 -0
- package/dist/{chunk-LR4M2AMG.mjs → chunk-VGNLXU4T.mjs} +2 -2
- package/dist/cli.d.ts +5 -5
- package/dist/cli.mjs +3 -3
- package/dist/{client-BZxeScuK.d.ts → client-DbK78fyQ.d.ts} +76 -55
- package/dist/{decimal-B0HtrMFY.d.ts → decimal-xie0oMOQ.d.ts} +7 -3
- package/dist/errors.d.ts +2 -2
- package/dist/{fiscal-evidence-B1Qe6XMg.d.ts → fiscal-evidence-Dpj-Iao8.d.ts} +1 -1
- package/dist/{index-D8LGFvxV.d.ts → index-CT1ffEAL.d.ts} +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.mjs +83 -13
- package/dist/index.mjs.map +1 -1
- package/dist/padron.d.ts +2 -2
- package/dist/{types-r2nf55gh.d.ts → types-SdBQAflw.d.ts} +3 -0
- package/dist/types.d.ts +1 -1
- package/dist/wsfe.d.ts +5 -5
- package/dist/wsfe.mjs +2 -2
- package/dist/wsmtxca.d.ts +7 -3
- package/dist/wsmtxca.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-5KCTRQJX.mjs.map +0 -1
- package/dist/chunk-ALH5IPSY.mjs.map +0 -1
- package/dist/chunk-LOWV7IF6.mjs.map +0 -1
- /package/dist/{chunk-LR4M2AMG.mjs.map → chunk-VGNLXU4T.mjs.map} +0 -0
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
normalizeWsfeDateInput,
|
|
12
12
|
normalizeWsfeVoucherInput,
|
|
13
13
|
serializeArcaExchangeRate
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-QHYJ424J.mjs";
|
|
15
15
|
import {
|
|
16
16
|
ARCA_CURRENCY_IDS,
|
|
17
17
|
ARCA_DOCUMENT_TYPES,
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from "./chunk-GX5ERXT6.mjs";
|
|
24
24
|
import {
|
|
25
25
|
createWsmtxcaService
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-D5ZWXTCV.mjs";
|
|
27
27
|
import {
|
|
28
28
|
ArcaConfigurationError,
|
|
29
29
|
ArcaError,
|
|
@@ -278,6 +278,15 @@ import { createHash } from "crypto";
|
|
|
278
278
|
function attemptKey(environment, taxId, key) {
|
|
279
279
|
return `arca:v1:attempt:${environment}:${taxId}:${key}`;
|
|
280
280
|
}
|
|
281
|
+
function sequenceKey(environment, taxId, salesPoint, voucherType) {
|
|
282
|
+
return `arca:v1:sequence:${environment}:${taxId}:${salesPoint}:${voucherType}`;
|
|
283
|
+
}
|
|
284
|
+
function sequenceLockKey(environment, taxId, salesPoint, voucherType) {
|
|
285
|
+
return `arca:v1:lock:sequence:${environment}:${taxId}:${salesPoint}:${voucherType}`;
|
|
286
|
+
}
|
|
287
|
+
function settledKey(environment, taxId, key) {
|
|
288
|
+
return `arca:v1:settled:${environment}:${taxId}:${key}`;
|
|
289
|
+
}
|
|
281
290
|
function canonicalHash(input) {
|
|
282
291
|
return createHash("sha256").update(JSON.stringify(canonical(input))).digest("hex");
|
|
283
292
|
}
|
|
@@ -723,6 +732,27 @@ function isWsaaCredentialValid(credentials) {
|
|
|
723
732
|
import { createHash as createHash2 } from "crypto";
|
|
724
733
|
import forge from "node-forge";
|
|
725
734
|
|
|
735
|
+
// src/internal/abort.ts
|
|
736
|
+
function abortedError(signal) {
|
|
737
|
+
return new ArcaTransportError("The ARCA call was aborted", {
|
|
738
|
+
cause: signal?.reason
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
function abortable(promise, signal) {
|
|
742
|
+
if (!signal) {
|
|
743
|
+
return promise;
|
|
744
|
+
}
|
|
745
|
+
if (signal.aborted) {
|
|
746
|
+
promise.catch(() => void 0);
|
|
747
|
+
return Promise.reject(abortedError(signal));
|
|
748
|
+
}
|
|
749
|
+
return new Promise((resolve, reject) => {
|
|
750
|
+
const onAbort = () => reject(abortedError(signal));
|
|
751
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
752
|
+
promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
|
|
726
756
|
// src/internal/http.ts
|
|
727
757
|
import https from "https";
|
|
728
758
|
var defaultAgent = new https.Agent({
|
|
@@ -743,7 +773,8 @@ async function postXmlWithMetadata({
|
|
|
743
773
|
retryDelay = 500,
|
|
744
774
|
logger,
|
|
745
775
|
service,
|
|
746
|
-
operation
|
|
776
|
+
operation,
|
|
777
|
+
signal
|
|
747
778
|
}) {
|
|
748
779
|
const totalAttempts = retries + 1;
|
|
749
780
|
for (let attempt = 1; attempt <= totalAttempts; attempt += 1) {
|
|
@@ -754,13 +785,14 @@ async function postXmlWithMetadata({
|
|
|
754
785
|
contentType,
|
|
755
786
|
soapAction,
|
|
756
787
|
useLegacyTlsSecurityLevel0,
|
|
757
|
-
timeout
|
|
788
|
+
timeout,
|
|
789
|
+
signal
|
|
758
790
|
});
|
|
759
791
|
} catch (error) {
|
|
760
792
|
if (!(error instanceof ArcaTransportError)) {
|
|
761
793
|
throw error;
|
|
762
794
|
}
|
|
763
|
-
if (attempt >= totalAttempts) {
|
|
795
|
+
if (attempt >= totalAttempts || signal?.aborted) {
|
|
764
796
|
logger?.error("ARCA transport request failed", {
|
|
765
797
|
service,
|
|
766
798
|
operation,
|
|
@@ -794,10 +826,16 @@ async function postXmlOnce({
|
|
|
794
826
|
contentType,
|
|
795
827
|
soapAction,
|
|
796
828
|
useLegacyTlsSecurityLevel0,
|
|
797
|
-
timeout
|
|
829
|
+
timeout,
|
|
830
|
+
signal
|
|
798
831
|
}) {
|
|
799
832
|
const endpoint = new URL(url);
|
|
800
833
|
const requestBody = Buffer.from(body, "utf8");
|
|
834
|
+
if (signal?.aborted) {
|
|
835
|
+
throw new ArcaTransportError("ARCA HTTP request was aborted", {
|
|
836
|
+
cause: signal.reason
|
|
837
|
+
});
|
|
838
|
+
}
|
|
801
839
|
return await new Promise((resolve, reject) => {
|
|
802
840
|
let settled = false;
|
|
803
841
|
const settleResolve = (response) => {
|
|
@@ -908,6 +946,15 @@ async function postXmlOnce({
|
|
|
908
946
|
})
|
|
909
947
|
);
|
|
910
948
|
});
|
|
949
|
+
const abort = () => {
|
|
950
|
+
const cause = new Error("ARCA HTTP request was aborted");
|
|
951
|
+
settleReject(
|
|
952
|
+
new ArcaTransportError("ARCA HTTP request was aborted", { cause })
|
|
953
|
+
);
|
|
954
|
+
request.destroy(cause);
|
|
955
|
+
};
|
|
956
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
957
|
+
request.on("close", () => signal?.removeEventListener("abort", abort));
|
|
911
958
|
request.write(requestBody);
|
|
912
959
|
request.end();
|
|
913
960
|
});
|
|
@@ -1143,34 +1190,37 @@ function createWsaaAuthModule(options) {
|
|
|
1143
1190
|
}
|
|
1144
1191
|
return {
|
|
1145
1192
|
login(service, authOptions = {}) {
|
|
1146
|
-
|
|
1147
|
-
const cacheKey = serializeWsaaSessionKey(sessionKey);
|
|
1148
|
-
if (authOptions.forceRefresh) {
|
|
1149
|
-
const runningForced2 = forcedInFlight.get(cacheKey);
|
|
1150
|
-
if (runningForced2) {
|
|
1151
|
-
return runningForced2;
|
|
1152
|
-
}
|
|
1153
|
-
const runningOrdinary2 = ordinaryInFlight.get(cacheKey);
|
|
1154
|
-
return trackLogin(forcedInFlight, cacheKey, async () => {
|
|
1155
|
-
await runningOrdinary2?.catch(() => void 0);
|
|
1156
|
-
return await performLogin(service, sessionKey, cacheKey, true);
|
|
1157
|
-
});
|
|
1158
|
-
}
|
|
1159
|
-
const runningOrdinary = ordinaryInFlight.get(cacheKey);
|
|
1160
|
-
if (runningOrdinary) {
|
|
1161
|
-
return runningOrdinary;
|
|
1162
|
-
}
|
|
1163
|
-
const runningForced = forcedInFlight.get(cacheKey);
|
|
1164
|
-
if (runningForced) {
|
|
1165
|
-
return runningForced;
|
|
1166
|
-
}
|
|
1167
|
-
return trackLogin(
|
|
1168
|
-
ordinaryInFlight,
|
|
1169
|
-
cacheKey,
|
|
1170
|
-
() => performLogin(service, sessionKey, cacheKey, false)
|
|
1171
|
-
);
|
|
1193
|
+
return abortable(runLogin(service, authOptions), authOptions.signal);
|
|
1172
1194
|
}
|
|
1173
1195
|
};
|
|
1196
|
+
function runLogin(service, authOptions) {
|
|
1197
|
+
const sessionKey = buildWsaaSessionKey(options.config, service);
|
|
1198
|
+
const cacheKey = serializeWsaaSessionKey(sessionKey);
|
|
1199
|
+
if (authOptions.forceRefresh) {
|
|
1200
|
+
const runningForced2 = forcedInFlight.get(cacheKey);
|
|
1201
|
+
if (runningForced2) {
|
|
1202
|
+
return runningForced2;
|
|
1203
|
+
}
|
|
1204
|
+
const runningOrdinary2 = ordinaryInFlight.get(cacheKey);
|
|
1205
|
+
return trackLogin(forcedInFlight, cacheKey, async () => {
|
|
1206
|
+
await runningOrdinary2?.catch(() => void 0);
|
|
1207
|
+
return await performLogin(service, sessionKey, cacheKey, true);
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
const runningOrdinary = ordinaryInFlight.get(cacheKey);
|
|
1211
|
+
if (runningOrdinary) {
|
|
1212
|
+
return runningOrdinary;
|
|
1213
|
+
}
|
|
1214
|
+
const runningForced = forcedInFlight.get(cacheKey);
|
|
1215
|
+
if (runningForced) {
|
|
1216
|
+
return runningForced;
|
|
1217
|
+
}
|
|
1218
|
+
return trackLogin(
|
|
1219
|
+
ordinaryInFlight,
|
|
1220
|
+
cacheKey,
|
|
1221
|
+
() => performLogin(service, sessionKey, cacheKey, false)
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1174
1224
|
}
|
|
1175
1225
|
async function requestCredentials(config, service, options) {
|
|
1176
1226
|
const loginTicketRequestXml = buildLoginTicketRequest(service);
|
|
@@ -2094,6 +2144,7 @@ function createWsmtxcaIssuanceService(wsmtxca) {
|
|
|
2094
2144
|
issue: (input) => wsmtxca.issue({
|
|
2095
2145
|
representedTaxId: input.representedTaxId,
|
|
2096
2146
|
forceRefresh: input.forceRefresh,
|
|
2147
|
+
signal: input.signal,
|
|
2097
2148
|
data: wsmtxcaRequest(input.data, input.voucherNumber)
|
|
2098
2149
|
}),
|
|
2099
2150
|
lookupVoucher: async (input) => {
|
|
@@ -2821,7 +2872,8 @@ function createVouchersService(wsfe, context, wsmtxca) {
|
|
|
2821
2872
|
input,
|
|
2822
2873
|
options ?? {},
|
|
2823
2874
|
context,
|
|
2824
|
-
"debitNote"
|
|
2875
|
+
"debitNote",
|
|
2876
|
+
select
|
|
2825
2877
|
),
|
|
2826
2878
|
previewCreditNote: async (input, options) => previewNote(
|
|
2827
2879
|
select(options),
|
|
@@ -2842,7 +2894,9 @@ function createVouchersService(wsfe, context, wsmtxca) {
|
|
|
2842
2894
|
select(options),
|
|
2843
2895
|
input,
|
|
2844
2896
|
options === void 0 ? {} : options,
|
|
2845
|
-
context
|
|
2897
|
+
context,
|
|
2898
|
+
"creditNote",
|
|
2899
|
+
select
|
|
2846
2900
|
);
|
|
2847
2901
|
return result;
|
|
2848
2902
|
},
|
|
@@ -2851,7 +2905,8 @@ function createVouchersService(wsfe, context, wsmtxca) {
|
|
|
2851
2905
|
select(options),
|
|
2852
2906
|
input,
|
|
2853
2907
|
options === void 0 ? {} : options,
|
|
2854
|
-
context
|
|
2908
|
+
context,
|
|
2909
|
+
select
|
|
2855
2910
|
);
|
|
2856
2911
|
return result;
|
|
2857
2912
|
},
|
|
@@ -2904,8 +2959,8 @@ function toPreview(prepared, options) {
|
|
|
2904
2959
|
...options.service === "wsmtxca" ? { service: "wsmtxca" } : {}
|
|
2905
2960
|
};
|
|
2906
2961
|
}
|
|
2907
|
-
async function issueInvoice(wsfe, input, inputOptions, context) {
|
|
2908
|
-
const options =
|
|
2962
|
+
async function issueInvoice(wsfe, input, inputOptions, context, select) {
|
|
2963
|
+
const options = cloneOptions(inputOptions);
|
|
2909
2964
|
validateOptions(options);
|
|
2910
2965
|
validateKeyStore(options, context);
|
|
2911
2966
|
const prepared = prepareInvoice(input, options);
|
|
@@ -2916,19 +2971,18 @@ async function issueInvoice(wsfe, input, inputOptions, context) {
|
|
|
2916
2971
|
() => Promise.resolve(prepared),
|
|
2917
2972
|
options,
|
|
2918
2973
|
context,
|
|
2919
|
-
prepared.amounts
|
|
2974
|
+
prepared.amounts,
|
|
2975
|
+
select
|
|
2920
2976
|
);
|
|
2921
2977
|
}
|
|
2922
|
-
async function runOperation(wsfe, operation, input, prepare, options, context, replayAmounts) {
|
|
2923
|
-
|
|
2924
|
-
if (options.idempotencyKey === void 0 || !store || !context) {
|
|
2978
|
+
async function runOperation(wsfe, operation, input, prepare, options, context, replayAmounts, select) {
|
|
2979
|
+
if (options.idempotencyKey === void 0 || !context?.store) {
|
|
2925
2980
|
return runAuthorization(wsfe, await prepare(), options);
|
|
2926
2981
|
}
|
|
2927
|
-
const
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
);
|
|
2982
|
+
const store = context.store;
|
|
2983
|
+
const { environment, taxId } = context;
|
|
2984
|
+
const idempotencyKey = options.idempotencyKey;
|
|
2985
|
+
const key = attemptKey(environment, taxId, idempotencyKey);
|
|
2932
2986
|
const representedTaxId = options.representedTaxId === void 0 ? void 0 : String(options.representedTaxId);
|
|
2933
2987
|
const inputHash = canonicalHash({
|
|
2934
2988
|
input,
|
|
@@ -2936,37 +2990,107 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
|
|
|
2936
2990
|
...options.service === "wsmtxca" ? { service: "wsmtxca" } : {},
|
|
2937
2991
|
...options.number === void 0 ? {} : { number: options.number }
|
|
2938
2992
|
});
|
|
2993
|
+
const settled = settledKey(environment, taxId, idempotencyKey);
|
|
2939
2994
|
const existing = await storeCall(() => store.get(key));
|
|
2940
2995
|
if (existing !== null) {
|
|
2941
|
-
return replay(existing);
|
|
2996
|
+
return await replay(existing);
|
|
2942
2997
|
}
|
|
2943
2998
|
const prepared = await prepare();
|
|
2944
|
-
const
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
representedTaxId,
|
|
2952
|
-
inputHash,
|
|
2953
|
-
number,
|
|
2954
|
-
salesPoint: prepared.data.salesPoint,
|
|
2955
|
-
voucherType: prepared.data.voucherType,
|
|
2956
|
-
sent: prepared.data,
|
|
2957
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2999
|
+
const sequence = {
|
|
3000
|
+
coordinates: {
|
|
3001
|
+
salesPoint: prepared.data.salesPoint,
|
|
3002
|
+
voucherType: prepared.data.voucherType
|
|
3003
|
+
},
|
|
3004
|
+
// The sequence belongs to the taxpayer whose numbering ARCA advances.
|
|
3005
|
+
taxId: String(options.representedTaxId ?? context.taxId)
|
|
2958
3006
|
};
|
|
2959
|
-
|
|
2960
|
-
|
|
3007
|
+
const sequenceRecord = sequenceKey(
|
|
3008
|
+
environment,
|
|
3009
|
+
sequence.taxId,
|
|
3010
|
+
sequence.coordinates.salesPoint,
|
|
3011
|
+
sequence.coordinates.voucherType
|
|
3012
|
+
);
|
|
3013
|
+
if (!store.withLock) {
|
|
3014
|
+
return await claim();
|
|
3015
|
+
}
|
|
3016
|
+
return await store.withLock(
|
|
3017
|
+
sequenceLockKey(
|
|
3018
|
+
environment,
|
|
3019
|
+
sequence.taxId,
|
|
3020
|
+
sequence.coordinates.salesPoint,
|
|
3021
|
+
sequence.coordinates.voucherType
|
|
3022
|
+
),
|
|
3023
|
+
async () => {
|
|
3024
|
+
const barrier = await runSequenceBarrier({
|
|
3025
|
+
store,
|
|
3026
|
+
environment,
|
|
3027
|
+
taxId,
|
|
3028
|
+
sequence: sequenceRecord,
|
|
3029
|
+
coordinates: sequence.coordinates,
|
|
3030
|
+
select: select ?? (() => wsfe),
|
|
3031
|
+
options,
|
|
3032
|
+
supersededBy: idempotencyKey,
|
|
3033
|
+
readNext: () => nextNumber(wsfe, prepared.data, options)
|
|
3034
|
+
});
|
|
3035
|
+
return "blocked" in barrier ? barrier.blocked : await claim(barrier.reserved);
|
|
3036
|
+
}
|
|
3037
|
+
);
|
|
3038
|
+
function reservation(number) {
|
|
3039
|
+
const service = options.service ?? "wsfe";
|
|
3040
|
+
const versioned = service === "wsmtxca" || prepared.data.details !== void 0;
|
|
3041
|
+
return {
|
|
3042
|
+
v: versioned ? 2 : 1,
|
|
3043
|
+
operation,
|
|
3044
|
+
...versioned ? { service } : {},
|
|
3045
|
+
representedTaxId,
|
|
3046
|
+
inputHash,
|
|
3047
|
+
number,
|
|
3048
|
+
salesPoint: prepared.data.salesPoint,
|
|
3049
|
+
voucherType: prepared.data.voucherType,
|
|
3050
|
+
sent: prepared.data,
|
|
3051
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3052
|
+
};
|
|
2961
3053
|
}
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
3054
|
+
async function claim(reserved) {
|
|
3055
|
+
const coordinated = store.withLock !== void 0;
|
|
3056
|
+
const prior = coordinated ? await storeCall(() => store.get(key)) : null;
|
|
3057
|
+
if (prior !== null) {
|
|
3058
|
+
return await replay(prior, true);
|
|
3059
|
+
}
|
|
3060
|
+
const number = options.number ?? reserved ?? await nextNumber(wsfe, prepared.data, options);
|
|
3061
|
+
const record = reservation(number);
|
|
3062
|
+
const claimed = {
|
|
3063
|
+
v: 1,
|
|
3064
|
+
key: idempotencyKey,
|
|
3065
|
+
number,
|
|
3066
|
+
claimedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3067
|
+
};
|
|
3068
|
+
if (coordinated) {
|
|
3069
|
+
await storeCall(() => store.set(sequenceRecord, JSON.stringify(claimed)));
|
|
3070
|
+
}
|
|
3071
|
+
if (await storeCall(() => store.add(key, JSON.stringify(record)))) {
|
|
3072
|
+
const outcome = await settle(
|
|
3073
|
+
runAuthorization(wsfe, prepared, options, number)
|
|
3074
|
+
);
|
|
3075
|
+
if (coordinated && outcome.kind !== "indeterminate") {
|
|
3076
|
+
await storeCall(
|
|
3077
|
+
() => store.set(
|
|
3078
|
+
sequenceRecord,
|
|
3079
|
+
JSON.stringify({ ...claimed, resolvedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
3080
|
+
)
|
|
3081
|
+
);
|
|
3082
|
+
}
|
|
3083
|
+
return outcome;
|
|
3084
|
+
}
|
|
3085
|
+
const winner = await storeCall(() => store.get(key));
|
|
3086
|
+
if (winner === null) {
|
|
3087
|
+
throw new ArcaConfigurationError(
|
|
3088
|
+
"ARCA reservation disappeared after atomic creation lost."
|
|
3089
|
+
);
|
|
3090
|
+
}
|
|
3091
|
+
return await replay(winner, true);
|
|
2967
3092
|
}
|
|
2968
|
-
|
|
2969
|
-
function replay(json) {
|
|
3093
|
+
async function replay(json, locked = false) {
|
|
2970
3094
|
const stored = readRecord(json);
|
|
2971
3095
|
if ((stored.service ?? "wsfe") !== (options.service ?? "wsfe") || stored.operation !== operation || stored.inputHash !== inputHash || stored.representedTaxId !== representedTaxId) {
|
|
2972
3096
|
throw new ArcaInputError(
|
|
@@ -2977,17 +3101,107 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
|
|
|
2977
3101
|
}
|
|
2978
3102
|
);
|
|
2979
3103
|
}
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
{
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
3104
|
+
const consult = async () => {
|
|
3105
|
+
const recorded = await storeCall(() => store.get(settled));
|
|
3106
|
+
if (recorded !== null) {
|
|
3107
|
+
return await settledOutcome(
|
|
3108
|
+
select ?? (() => wsfe),
|
|
3109
|
+
stored,
|
|
3110
|
+
readSettledRecord(recorded),
|
|
3111
|
+
options
|
|
3112
|
+
);
|
|
3113
|
+
}
|
|
3114
|
+
return await settle(
|
|
3115
|
+
runAuthorization(
|
|
3116
|
+
wsfe,
|
|
3117
|
+
{
|
|
3118
|
+
...preparedFromRecord(stored),
|
|
3119
|
+
...replayAmounts ? { amounts: replayAmounts } : {}
|
|
3120
|
+
},
|
|
3121
|
+
options,
|
|
3122
|
+
stored.number,
|
|
3123
|
+
true
|
|
3124
|
+
)
|
|
3125
|
+
);
|
|
3126
|
+
};
|
|
3127
|
+
if (locked || !store.withLock) {
|
|
3128
|
+
return await consult();
|
|
3129
|
+
}
|
|
3130
|
+
return await store.withLock(
|
|
3131
|
+
sequenceLockKey(
|
|
3132
|
+
environment,
|
|
3133
|
+
stored.representedTaxId ?? taxId,
|
|
3134
|
+
stored.salesPoint,
|
|
3135
|
+
stored.voucherType
|
|
3136
|
+
),
|
|
3137
|
+
consult
|
|
2989
3138
|
);
|
|
2990
3139
|
}
|
|
3140
|
+
async function settle(running) {
|
|
3141
|
+
return await recordConflict(store, settled, await running);
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
async function recordConflict(store, key, outcome) {
|
|
3145
|
+
if (outcome.kind !== "conflict") {
|
|
3146
|
+
return outcome;
|
|
3147
|
+
}
|
|
3148
|
+
const record = {
|
|
3149
|
+
v: 1,
|
|
3150
|
+
kind: "conflict",
|
|
3151
|
+
number: outcome.attempted.number,
|
|
3152
|
+
found: Object.fromEntries(
|
|
3153
|
+
Object.entries(outcome.found).filter(([field]) => field !== "raw")
|
|
3154
|
+
),
|
|
3155
|
+
settledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3156
|
+
};
|
|
3157
|
+
await storeCall(() => store.add(key, JSON.stringify(record)));
|
|
3158
|
+
return outcome;
|
|
3159
|
+
}
|
|
3160
|
+
function readSettledRecord(json) {
|
|
3161
|
+
try {
|
|
3162
|
+
const record = JSON.parse(json);
|
|
3163
|
+
if (!record || record.v !== 1 || !Number.isSafeInteger(record.number) || (record.kind === "conflict" ? !record.found || typeof record.found !== "object" : record.kind !== "superseded" || typeof record.by !== "string")) {
|
|
3164
|
+
throw new Error("Invalid settled structure");
|
|
3165
|
+
}
|
|
3166
|
+
return record;
|
|
3167
|
+
} catch (cause) {
|
|
3168
|
+
throw new ArcaConfigurationError(
|
|
3169
|
+
"Invalid ARCA settled record; preserve it for reconciliation.",
|
|
3170
|
+
{ cause }
|
|
3171
|
+
);
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3174
|
+
async function settledOutcome(select, reservation, settled, options) {
|
|
3175
|
+
if (settled.kind === "conflict") {
|
|
3176
|
+
return settledConflict(settled, reservation);
|
|
3177
|
+
}
|
|
3178
|
+
const outcome = await consultReservation(select, reservation, options, true);
|
|
3179
|
+
if (outcome.kind === "indeterminate" && outcome.lookup.kind === "not_found") {
|
|
3180
|
+
return {
|
|
3181
|
+
kind: "indeterminate",
|
|
3182
|
+
attempted: {
|
|
3183
|
+
salesPoint: reservation.salesPoint,
|
|
3184
|
+
voucherType: reservation.voucherType,
|
|
3185
|
+
number: settled.number
|
|
3186
|
+
},
|
|
3187
|
+
attempt: replayEvidence(reservation.service),
|
|
3188
|
+
lookup: { kind: "superseded", by: settled.by }
|
|
3189
|
+
};
|
|
3190
|
+
}
|
|
3191
|
+
return outcome;
|
|
3192
|
+
}
|
|
3193
|
+
function settledConflict(settled, reservation) {
|
|
3194
|
+
return {
|
|
3195
|
+
kind: "conflict",
|
|
3196
|
+
attempted: {
|
|
3197
|
+
salesPoint: reservation.salesPoint,
|
|
3198
|
+
voucherType: reservation.voucherType,
|
|
3199
|
+
number: settled.number
|
|
3200
|
+
},
|
|
3201
|
+
attempt: replayEvidence(reservation.service),
|
|
3202
|
+
found: settled.found,
|
|
3203
|
+
reason: "This key already recorded another voucher at the reserved number. Reconcile before issuing under a new key."
|
|
3204
|
+
};
|
|
2991
3205
|
}
|
|
2992
3206
|
function validateKeyStore(options, context) {
|
|
2993
3207
|
if (options.idempotencyKey === void 0) {
|
|
@@ -3043,6 +3257,7 @@ async function nextNumber(wsfe, data, options) {
|
|
|
3043
3257
|
const number = await wsfe.getNextVoucherNumber({
|
|
3044
3258
|
representedTaxId: options.representedTaxId,
|
|
3045
3259
|
forceRefresh: options.forceRefresh,
|
|
3260
|
+
...options.signal === void 0 ? {} : { signal: options.signal },
|
|
3046
3261
|
salesPoint: data.salesPoint,
|
|
3047
3262
|
voucherType: data.voucherType
|
|
3048
3263
|
});
|
|
@@ -3060,7 +3275,8 @@ async function nextNumber(wsfe, data, options) {
|
|
|
3060
3275
|
async function runAuthorization(wsfe, { data, voucherClass, amounts }, options, reservedNumber, replay = false) {
|
|
3061
3276
|
const auth = {
|
|
3062
3277
|
representedTaxId: options.representedTaxId,
|
|
3063
|
-
forceRefresh: options.forceRefresh
|
|
3278
|
+
forceRefresh: options.forceRefresh,
|
|
3279
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
3064
3280
|
};
|
|
3065
3281
|
const includeRaw = options.include?.raw === true;
|
|
3066
3282
|
const includeExact = options.include?.exactInput === true;
|
|
@@ -3099,7 +3315,7 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3099
3315
|
kind: "indeterminate",
|
|
3100
3316
|
attempted,
|
|
3101
3317
|
attempt: attempt2,
|
|
3102
|
-
lookup: { kind: "failed", error: toArcaSafeErrorMetadata(error) }
|
|
3318
|
+
lookup: options.signal?.aborted ? { kind: "aborted" } : { kind: "failed", error: toArcaSafeErrorMetadata(error) }
|
|
3103
3319
|
};
|
|
3104
3320
|
}
|
|
3105
3321
|
if (lookup.kind === "found") {
|
|
@@ -3121,32 +3337,13 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3121
3337
|
};
|
|
3122
3338
|
}
|
|
3123
3339
|
if (authorization.kind === "rejected") {
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
...authorization,
|
|
3132
|
-
kind: "indeterminate",
|
|
3133
|
-
reason: "contradictory_response"
|
|
3134
|
-
},
|
|
3135
|
-
includeRaw
|
|
3136
|
-
)
|
|
3137
|
-
});
|
|
3138
|
-
if (recovered.kind === "authorized" || recovered.kind === "conflict") {
|
|
3139
|
-
return recovered;
|
|
3140
|
-
}
|
|
3141
|
-
}
|
|
3142
|
-
return {
|
|
3143
|
-
kind: "rejected",
|
|
3144
|
-
attempted,
|
|
3145
|
-
issues: [...authorization.errors, ...authorization.observations].map(
|
|
3146
|
-
projectIssue
|
|
3147
|
-
),
|
|
3148
|
-
authorization: projectEvidence(authorization, includeRaw)
|
|
3149
|
-
};
|
|
3340
|
+
return await resolveRejection(
|
|
3341
|
+
authorization,
|
|
3342
|
+
recovery,
|
|
3343
|
+
options,
|
|
3344
|
+
includeRaw,
|
|
3345
|
+
replay
|
|
3346
|
+
);
|
|
3150
3347
|
}
|
|
3151
3348
|
const uncertain = authorization.kind === "indeterminate" ? authorization : {
|
|
3152
3349
|
...authorization,
|
|
@@ -3166,6 +3363,33 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3166
3363
|
voucher
|
|
3167
3364
|
});
|
|
3168
3365
|
}
|
|
3366
|
+
async function resolveRejection(authorization, recovery, options, includeRaw, replay) {
|
|
3367
|
+
const issues = [...authorization.errors, ...authorization.observations];
|
|
3368
|
+
const consult = options.idempotencyKey !== void 0 && (options.service === "wsmtxca" || issues.some((issue) => issue.code === "10016"));
|
|
3369
|
+
if (consult) {
|
|
3370
|
+
const recovered = await recoverInvoice({
|
|
3371
|
+
...recovery,
|
|
3372
|
+
attempt: projectEvidence(
|
|
3373
|
+
{
|
|
3374
|
+
...authorization,
|
|
3375
|
+
kind: "indeterminate",
|
|
3376
|
+
reason: "contradictory_response"
|
|
3377
|
+
},
|
|
3378
|
+
includeRaw
|
|
3379
|
+
),
|
|
3380
|
+
strangerAtNumber: !replay
|
|
3381
|
+
});
|
|
3382
|
+
if (recovered.kind === "authorized" || recovered.kind === "conflict") {
|
|
3383
|
+
return recovered;
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
return {
|
|
3387
|
+
kind: "rejected",
|
|
3388
|
+
attempted: recovery.attempted,
|
|
3389
|
+
issues: issues.map(projectIssue),
|
|
3390
|
+
authorization: projectEvidence(authorization, includeRaw)
|
|
3391
|
+
};
|
|
3392
|
+
}
|
|
3169
3393
|
async function recoverInvoice({
|
|
3170
3394
|
wsfe,
|
|
3171
3395
|
auth,
|
|
@@ -3176,8 +3400,17 @@ async function recoverInvoice({
|
|
|
3176
3400
|
exact,
|
|
3177
3401
|
voucher,
|
|
3178
3402
|
lookup: suppliedLookup,
|
|
3179
|
-
service
|
|
3403
|
+
service,
|
|
3404
|
+
strangerAtNumber = false
|
|
3180
3405
|
}) {
|
|
3406
|
+
if (suppliedLookup === void 0 && auth.signal?.aborted) {
|
|
3407
|
+
return {
|
|
3408
|
+
kind: "indeterminate",
|
|
3409
|
+
attempted,
|
|
3410
|
+
attempt,
|
|
3411
|
+
lookup: { kind: "aborted" }
|
|
3412
|
+
};
|
|
3413
|
+
}
|
|
3181
3414
|
let lookup;
|
|
3182
3415
|
try {
|
|
3183
3416
|
lookup = suppliedLookup ?? await wsfe.lookupVoucher({ ...auth, ...attempted });
|
|
@@ -3186,7 +3419,7 @@ async function recoverInvoice({
|
|
|
3186
3419
|
kind: "indeterminate",
|
|
3187
3420
|
attempted,
|
|
3188
3421
|
attempt,
|
|
3189
|
-
lookup: { kind: "failed", error: toArcaSafeErrorMetadata(error) }
|
|
3422
|
+
lookup: auth.signal?.aborted ? { kind: "aborted" } : { kind: "failed", error: toArcaSafeErrorMetadata(error) }
|
|
3190
3423
|
};
|
|
3191
3424
|
}
|
|
3192
3425
|
const raw = includeRaw ? { raw: lookup.raw } : {};
|
|
@@ -3198,6 +3431,15 @@ async function recoverInvoice({
|
|
|
3198
3431
|
lookup: { kind: "not_found", ...raw }
|
|
3199
3432
|
};
|
|
3200
3433
|
}
|
|
3434
|
+
if (strangerAtNumber) {
|
|
3435
|
+
return {
|
|
3436
|
+
kind: "conflict",
|
|
3437
|
+
attempted,
|
|
3438
|
+
attempt,
|
|
3439
|
+
found: { ...toVoucherSummary(lookup.voucher), ...raw },
|
|
3440
|
+
reason: "ARCA refused the number this call reserved and another voucher occupies it"
|
|
3441
|
+
};
|
|
3442
|
+
}
|
|
3201
3443
|
const detailsMatch = service === "wsmtxca" ? matchWsmtxcaDetails(data, attempted.number, lookup.voucher.raw) : void 0;
|
|
3202
3444
|
const matched = detailsMatch === void 0 ? matchWsfeVoucherIdentity(data, attempted.number, lookup.voucher) : detailsMatch === "match" && lookup.voucher.cae && lookup.voucher.caeExpiry ? { matches: true } : {
|
|
3203
3445
|
matches: false,
|
|
@@ -3281,6 +3523,14 @@ function projectEvidence(evidence, includeRaw) {
|
|
|
3281
3523
|
}
|
|
3282
3524
|
return projected;
|
|
3283
3525
|
}
|
|
3526
|
+
function cloneOptions(options) {
|
|
3527
|
+
assertIssueObject(options, "options");
|
|
3528
|
+
const { signal, ...rest } = options;
|
|
3529
|
+
return {
|
|
3530
|
+
...structuredClone(rest),
|
|
3531
|
+
...signal === void 0 ? {} : { signal }
|
|
3532
|
+
};
|
|
3533
|
+
}
|
|
3284
3534
|
function validateOptions(options) {
|
|
3285
3535
|
assertIssueObject(options, "options");
|
|
3286
3536
|
assertIssueKeys(
|
|
@@ -3291,10 +3541,17 @@ function validateOptions(options) {
|
|
|
3291
3541
|
"include",
|
|
3292
3542
|
"idempotencyKey",
|
|
3293
3543
|
"service",
|
|
3294
|
-
"number"
|
|
3544
|
+
"number",
|
|
3545
|
+
"signal"
|
|
3295
3546
|
],
|
|
3296
3547
|
"options"
|
|
3297
3548
|
);
|
|
3549
|
+
if (options.signal !== void 0 && (typeof options.signal !== "object" || options.signal === null || typeof options.signal.aborted !== "boolean" || typeof options.signal.addEventListener !== "function")) {
|
|
3550
|
+
throw new ArcaInputError("options.signal must be an AbortSignal.", {
|
|
3551
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
3552
|
+
field: "options.signal"
|
|
3553
|
+
});
|
|
3554
|
+
}
|
|
3298
3555
|
if (options.service !== void 0 && options.service !== "wsfe" && options.service !== "wsmtxca") {
|
|
3299
3556
|
throw new ArcaInputError("Unknown fiscal service", {
|
|
3300
3557
|
code: "ARCA_INPUT_INVALID_VALUE",
|
|
@@ -3348,8 +3605,8 @@ function replayEvidence(service = "wsfe") {
|
|
|
3348
3605
|
observations: []
|
|
3349
3606
|
};
|
|
3350
3607
|
}
|
|
3351
|
-
async function issueCreditNote(wsfe, input, inputOptions, context, kind = "creditNote") {
|
|
3352
|
-
const options =
|
|
3608
|
+
async function issueCreditNote(wsfe, input, inputOptions, context, kind = "creditNote", select) {
|
|
3609
|
+
const options = cloneOptions(inputOptions);
|
|
3353
3610
|
validateOptions(options);
|
|
3354
3611
|
validateKeyStore(options, context);
|
|
3355
3612
|
assertIssueObject(input, "input");
|
|
@@ -3366,7 +3623,9 @@ async function issueCreditNote(wsfe, input, inputOptions, context, kind = "credi
|
|
|
3366
3623
|
note,
|
|
3367
3624
|
() => prepareNote(wsfe, note, options, context, kind),
|
|
3368
3625
|
options,
|
|
3369
|
-
context
|
|
3626
|
+
context,
|
|
3627
|
+
void 0,
|
|
3628
|
+
select
|
|
3370
3629
|
);
|
|
3371
3630
|
}
|
|
3372
3631
|
async function previewNote(wsfe, input, inputOptions, context, kind) {
|
|
@@ -3467,11 +3726,11 @@ function exactEvidence(data, number, options, include) {
|
|
|
3467
3726
|
} : {};
|
|
3468
3727
|
}
|
|
3469
3728
|
async function recoverOperation(select, key, inputOptions, context) {
|
|
3470
|
-
const options =
|
|
3729
|
+
const options = cloneOptions(inputOptions);
|
|
3471
3730
|
assertIssueObject(options, "options");
|
|
3472
3731
|
assertIssueKeys(
|
|
3473
3732
|
options,
|
|
3474
|
-
["representedTaxId", "forceRefresh", "include"],
|
|
3733
|
+
["representedTaxId", "forceRefresh", "include", "signal"],
|
|
3475
3734
|
"options"
|
|
3476
3735
|
);
|
|
3477
3736
|
validateOptions(options);
|
|
@@ -3492,28 +3751,52 @@ async function recoverOperation(select, key, inputOptions, context) {
|
|
|
3492
3751
|
});
|
|
3493
3752
|
}
|
|
3494
3753
|
const record = readRecord(json);
|
|
3754
|
+
const store = context.store;
|
|
3755
|
+
const settled = settledKey(
|
|
3756
|
+
context.environment,
|
|
3757
|
+
context.taxId,
|
|
3758
|
+
key
|
|
3759
|
+
);
|
|
3495
3760
|
if (options.representedTaxId !== void 0 && String(options.representedTaxId) !== (record.representedTaxId ?? context?.taxId)) {
|
|
3496
3761
|
throw new ArcaInputError(
|
|
3497
3762
|
"Reservation belongs to another represented taxpayer",
|
|
3498
3763
|
{ code: "ARCA_INPUT_IDEMPOTENCY_MISMATCH" }
|
|
3499
3764
|
);
|
|
3500
3765
|
}
|
|
3766
|
+
const recorded = await storeCall(() => store.get(settled));
|
|
3767
|
+
if (recorded !== null) {
|
|
3768
|
+
return await settledOutcome(
|
|
3769
|
+
select,
|
|
3770
|
+
record,
|
|
3771
|
+
readSettledRecord(recorded),
|
|
3772
|
+
options
|
|
3773
|
+
);
|
|
3774
|
+
}
|
|
3775
|
+
return await recordConflict(
|
|
3776
|
+
store,
|
|
3777
|
+
settled,
|
|
3778
|
+
await consultReservation(select, record, options)
|
|
3779
|
+
);
|
|
3780
|
+
}
|
|
3781
|
+
function consultReservation(select, record, options, strangerAtNumber = false) {
|
|
3501
3782
|
const storedOptions = {
|
|
3502
3783
|
...options,
|
|
3503
3784
|
service: record.service ?? "wsfe",
|
|
3504
3785
|
representedTaxId: record.representedTaxId
|
|
3505
3786
|
};
|
|
3506
3787
|
const prepared = preparedFromRecord(record);
|
|
3788
|
+
const attempted = {
|
|
3789
|
+
salesPoint: record.salesPoint,
|
|
3790
|
+
voucherType: record.voucherType,
|
|
3791
|
+
number: record.number
|
|
3792
|
+
};
|
|
3507
3793
|
return recoverInvoice({
|
|
3508
3794
|
wsfe: select(storedOptions),
|
|
3509
3795
|
service: storedOptions.service,
|
|
3510
3796
|
auth: storedOptions,
|
|
3511
3797
|
data: prepared.data,
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
voucherType: record.voucherType,
|
|
3515
|
-
number: record.number
|
|
3516
|
-
},
|
|
3798
|
+
strangerAtNumber,
|
|
3799
|
+
attempted,
|
|
3517
3800
|
attempt: replayEvidence(storedOptions.service),
|
|
3518
3801
|
includeRaw: options.include?.raw === true,
|
|
3519
3802
|
exact: exactEvidence(
|
|
@@ -3523,9 +3806,7 @@ async function recoverOperation(select, key, inputOptions, context) {
|
|
|
3523
3806
|
options.include?.exactInput === true
|
|
3524
3807
|
),
|
|
3525
3808
|
voucher: (cae, caeExpiry) => ({
|
|
3526
|
-
|
|
3527
|
-
voucherType: record.voucherType,
|
|
3528
|
-
number: record.number,
|
|
3809
|
+
...attempted,
|
|
3529
3810
|
voucherClass: prepared.voucherClass,
|
|
3530
3811
|
date: prepared.data.voucherDate,
|
|
3531
3812
|
amounts: prepared.amounts,
|
|
@@ -3534,6 +3815,90 @@ async function recoverOperation(select, key, inputOptions, context) {
|
|
|
3534
3815
|
})
|
|
3535
3816
|
});
|
|
3536
3817
|
}
|
|
3818
|
+
async function runSequenceBarrier({
|
|
3819
|
+
store,
|
|
3820
|
+
environment,
|
|
3821
|
+
taxId,
|
|
3822
|
+
sequence,
|
|
3823
|
+
coordinates,
|
|
3824
|
+
select,
|
|
3825
|
+
options,
|
|
3826
|
+
supersededBy,
|
|
3827
|
+
readNext
|
|
3828
|
+
}) {
|
|
3829
|
+
const json = await storeCall(() => store.get(sequence));
|
|
3830
|
+
if (json === null) {
|
|
3831
|
+
return {};
|
|
3832
|
+
}
|
|
3833
|
+
const claimed = readSequenceRecord(json);
|
|
3834
|
+
if (claimed.resolvedAt !== void 0) {
|
|
3835
|
+
return {};
|
|
3836
|
+
}
|
|
3837
|
+
const settled = settledKey(environment, taxId, claimed.key);
|
|
3838
|
+
if (await storeCall(() => store.get(settled)) !== null) {
|
|
3839
|
+
return {};
|
|
3840
|
+
}
|
|
3841
|
+
const reservation = await storeCall(
|
|
3842
|
+
() => store.get(attemptKey(environment, taxId, claimed.key))
|
|
3843
|
+
);
|
|
3844
|
+
if (reservation === null) {
|
|
3845
|
+
return {};
|
|
3846
|
+
}
|
|
3847
|
+
const record = readRecord(reservation);
|
|
3848
|
+
const blocked = {
|
|
3849
|
+
blocked: {
|
|
3850
|
+
kind: "indeterminate",
|
|
3851
|
+
attempted: { ...coordinates, number: record.number },
|
|
3852
|
+
attempt: replayEvidence(options.service),
|
|
3853
|
+
lookup: { kind: "blocked", by: claimed.key }
|
|
3854
|
+
}
|
|
3855
|
+
};
|
|
3856
|
+
const outcome = await recordConflict(
|
|
3857
|
+
store,
|
|
3858
|
+
settled,
|
|
3859
|
+
await consultReservation(select, record, {
|
|
3860
|
+
forceRefresh: options.forceRefresh,
|
|
3861
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
3862
|
+
})
|
|
3863
|
+
);
|
|
3864
|
+
if (outcome.kind === "authorized" || outcome.kind === "conflict") {
|
|
3865
|
+
return {};
|
|
3866
|
+
}
|
|
3867
|
+
if (outcome.kind !== "indeterminate" || outcome.lookup.kind !== "not_found") {
|
|
3868
|
+
return blocked;
|
|
3869
|
+
}
|
|
3870
|
+
const next = await readNext();
|
|
3871
|
+
if (next !== record.number) {
|
|
3872
|
+
return blocked;
|
|
3873
|
+
}
|
|
3874
|
+
await storeCall(
|
|
3875
|
+
() => store.add(
|
|
3876
|
+
settled,
|
|
3877
|
+
JSON.stringify({
|
|
3878
|
+
v: 1,
|
|
3879
|
+
kind: "superseded",
|
|
3880
|
+
number: record.number,
|
|
3881
|
+
by: supersededBy,
|
|
3882
|
+
settledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3883
|
+
})
|
|
3884
|
+
)
|
|
3885
|
+
);
|
|
3886
|
+
return { reserved: next };
|
|
3887
|
+
}
|
|
3888
|
+
function readSequenceRecord(json) {
|
|
3889
|
+
try {
|
|
3890
|
+
const record = JSON.parse(json);
|
|
3891
|
+
if (!record || record.v !== 1 || typeof record.key !== "string" || !Number.isSafeInteger(record.number)) {
|
|
3892
|
+
throw new Error("Invalid sequence structure");
|
|
3893
|
+
}
|
|
3894
|
+
return record;
|
|
3895
|
+
} catch (cause) {
|
|
3896
|
+
throw new ArcaConfigurationError(
|
|
3897
|
+
"Invalid ARCA sequence record; delete the sequence key to resume.",
|
|
3898
|
+
{ cause }
|
|
3899
|
+
);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3537
3902
|
|
|
3538
3903
|
// src/soap/index.ts
|
|
3539
3904
|
function createSoapTransport(options) {
|
|
@@ -3572,7 +3937,8 @@ function createSoapTransport(options) {
|
|
|
3572
3937
|
retryDelay: options.config.retryDelay,
|
|
3573
3938
|
logger: options.logger,
|
|
3574
3939
|
service: request.service,
|
|
3575
|
-
operation: request.operation
|
|
3940
|
+
operation: request.operation,
|
|
3941
|
+
signal: request.signal
|
|
3576
3942
|
});
|
|
3577
3943
|
options.logger?.debug("Received ARCA SOAP response", {
|
|
3578
3944
|
service: request.service,
|
|
@@ -3688,9 +4054,56 @@ function createArcaClient(config = {}) {
|
|
|
3688
4054
|
}
|
|
3689
4055
|
|
|
3690
4056
|
// src/store/file.ts
|
|
3691
|
-
import { createHash as createHash3, randomUUID } from "crypto";
|
|
3692
|
-
import {
|
|
4057
|
+
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
4058
|
+
import {
|
|
4059
|
+
mkdir,
|
|
4060
|
+
readFile,
|
|
4061
|
+
rename,
|
|
4062
|
+
rm,
|
|
4063
|
+
stat,
|
|
4064
|
+
unlink,
|
|
4065
|
+
writeFile
|
|
4066
|
+
} from "fs/promises";
|
|
3693
4067
|
import { join } from "path";
|
|
4068
|
+
|
|
4069
|
+
// src/store/lock.ts
|
|
4070
|
+
import { randomUUID } from "crypto";
|
|
4071
|
+
var ARCA_LEASE_MS = 6e4;
|
|
4072
|
+
var RENEW_MS = 2e4;
|
|
4073
|
+
var POLL_MS = 50;
|
|
4074
|
+
var MAX_WAIT_MS = 2 * ARCA_LEASE_MS;
|
|
4075
|
+
async function withLease(key, driver, fn) {
|
|
4076
|
+
const owner = randomUUID();
|
|
4077
|
+
const deadline = Date.now() + MAX_WAIT_MS;
|
|
4078
|
+
let held = await driver.acquire(owner);
|
|
4079
|
+
while (!held) {
|
|
4080
|
+
if (Date.now() >= deadline) {
|
|
4081
|
+
throw new ArcaConfigurationError(
|
|
4082
|
+
`ARCA store lock ${key} stayed held; no work was attempted.`
|
|
4083
|
+
);
|
|
4084
|
+
}
|
|
4085
|
+
await delay2(POLL_MS + Math.floor(Math.random() * POLL_MS));
|
|
4086
|
+
held = await driver.acquire(owner);
|
|
4087
|
+
}
|
|
4088
|
+
const renewal = setInterval(() => {
|
|
4089
|
+
driver.renew(owner).catch(() => void 0);
|
|
4090
|
+
}, RENEW_MS);
|
|
4091
|
+
renewal.unref?.();
|
|
4092
|
+
try {
|
|
4093
|
+
return await fn();
|
|
4094
|
+
} finally {
|
|
4095
|
+
clearInterval(renewal);
|
|
4096
|
+
await driver.release(owner).catch(() => void 0);
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4099
|
+
function delay2(ms) {
|
|
4100
|
+
return new Promise((resolve) => {
|
|
4101
|
+
const timer = setTimeout(resolve, ms);
|
|
4102
|
+
timer.unref?.();
|
|
4103
|
+
});
|
|
4104
|
+
}
|
|
4105
|
+
|
|
4106
|
+
// src/store/file.ts
|
|
3694
4107
|
function createFileStore(directory) {
|
|
3695
4108
|
const path = (key) => join(directory, createHash3("sha256").update(key).digest("hex"));
|
|
3696
4109
|
const ensure = () => mkdir(directory, { recursive: true, mode: 448 });
|
|
@@ -3707,7 +4120,7 @@ function createFileStore(directory) {
|
|
|
3707
4120
|
}),
|
|
3708
4121
|
set: (key, value) => storeCall(async () => {
|
|
3709
4122
|
await ensure();
|
|
3710
|
-
const temporary = `${path(key)}.${
|
|
4123
|
+
const temporary = `${path(key)}.${randomUUID2()}.tmp`;
|
|
3711
4124
|
try {
|
|
3712
4125
|
await writeFile(temporary, value, { mode: 384, flag: "wx" });
|
|
3713
4126
|
await rename(temporary, path(key));
|
|
@@ -3735,9 +4148,85 @@ function createFileStore(directory) {
|
|
|
3735
4148
|
throw error;
|
|
3736
4149
|
}
|
|
3737
4150
|
}
|
|
4151
|
+
}),
|
|
4152
|
+
withLock: (key, fn) => (
|
|
4153
|
+
// A lock directory: mkdir is the atomic claim on every POSIX filesystem
|
|
4154
|
+
// and on Windows, and the holder file inside carries the lease.
|
|
4155
|
+
withLease(key, fileLease(`${path(key)}.lock`, ensure), fn)
|
|
4156
|
+
)
|
|
4157
|
+
};
|
|
4158
|
+
}
|
|
4159
|
+
function fileLease(directory, ensure) {
|
|
4160
|
+
const holder = join(directory, "holder");
|
|
4161
|
+
const claim = (owner) => JSON.stringify({
|
|
4162
|
+
owner,
|
|
4163
|
+
expiresAt: new Date(Date.now() + ARCA_LEASE_MS).toISOString()
|
|
4164
|
+
});
|
|
4165
|
+
const read = async () => {
|
|
4166
|
+
try {
|
|
4167
|
+
return JSON.parse(await readFile(holder, "utf8"));
|
|
4168
|
+
} catch {
|
|
4169
|
+
return null;
|
|
4170
|
+
}
|
|
4171
|
+
};
|
|
4172
|
+
return {
|
|
4173
|
+
acquire: (owner) => storeCall(async () => {
|
|
4174
|
+
await ensure();
|
|
4175
|
+
if (!await createDirectory(directory)) {
|
|
4176
|
+
if (await stillHeld(directory, await read())) {
|
|
4177
|
+
return false;
|
|
4178
|
+
}
|
|
4179
|
+
await rm(directory, { recursive: true, force: true });
|
|
4180
|
+
if (!await createDirectory(directory)) {
|
|
4181
|
+
return false;
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
return await writeHolder(holder, claim(owner));
|
|
4185
|
+
}),
|
|
4186
|
+
renew: (owner) => storeCall(async () => {
|
|
4187
|
+
if ((await read())?.owner === owner) {
|
|
4188
|
+
await writeFile(holder, claim(owner), { mode: 384 });
|
|
4189
|
+
}
|
|
4190
|
+
}),
|
|
4191
|
+
release: (owner) => storeCall(async () => {
|
|
4192
|
+
if ((await read())?.owner === owner) {
|
|
4193
|
+
await rm(directory, { recursive: true, force: true });
|
|
4194
|
+
}
|
|
3738
4195
|
})
|
|
3739
4196
|
};
|
|
3740
4197
|
}
|
|
4198
|
+
async function createDirectory(directory) {
|
|
4199
|
+
try {
|
|
4200
|
+
await mkdir(directory);
|
|
4201
|
+
return true;
|
|
4202
|
+
} catch (error) {
|
|
4203
|
+
if (error.code === "EEXIST") {
|
|
4204
|
+
return false;
|
|
4205
|
+
}
|
|
4206
|
+
throw error;
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
async function writeHolder(holder, value) {
|
|
4210
|
+
try {
|
|
4211
|
+
await writeFile(holder, value, { mode: 384, flag: "wx" });
|
|
4212
|
+
return true;
|
|
4213
|
+
} catch (error) {
|
|
4214
|
+
if (error.code === "EEXIST") {
|
|
4215
|
+
return false;
|
|
4216
|
+
}
|
|
4217
|
+
throw error;
|
|
4218
|
+
}
|
|
4219
|
+
}
|
|
4220
|
+
async function stillHeld(directory, holder) {
|
|
4221
|
+
if (holder) {
|
|
4222
|
+
return Date.parse(holder.expiresAt) > Date.now();
|
|
4223
|
+
}
|
|
4224
|
+
try {
|
|
4225
|
+
return (await stat(directory)).mtimeMs + ARCA_LEASE_MS > Date.now();
|
|
4226
|
+
} catch {
|
|
4227
|
+
return false;
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
3741
4230
|
|
|
3742
4231
|
export {
|
|
3743
4232
|
ARCA_ENVIRONMENTS,
|
|
@@ -3753,6 +4242,8 @@ export {
|
|
|
3753
4242
|
createWsaaAuthModule,
|
|
3754
4243
|
createWsaaStoreAdapter,
|
|
3755
4244
|
createArcaClient,
|
|
4245
|
+
ARCA_LEASE_MS,
|
|
4246
|
+
withLease,
|
|
3756
4247
|
createFileStore
|
|
3757
4248
|
};
|
|
3758
|
-
//# sourceMappingURL=chunk-
|
|
4249
|
+
//# sourceMappingURL=chunk-2PYF5JFG.mjs.map
|