facturas 0.11.0 → 0.12.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 +4 -4
- package/dist/{chunk-ALH5IPSY.mjs → chunk-D5ZWXTCV.mjs} +21 -12
- package/dist/chunk-D5ZWXTCV.mjs.map +1 -0
- package/dist/{chunk-LOWV7IF6.mjs → chunk-QEJCLJDS.mjs} +614 -129
- package/dist/chunk-QEJCLJDS.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,102 @@ 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
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
);
|
|
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
|
+
async function claim(reserved) {
|
|
3039
|
+
const number = options.number ?? reserved ?? await nextNumber(wsfe, prepared.data, options);
|
|
3040
|
+
const service = options.service ?? "wsfe";
|
|
3041
|
+
const versioned = service === "wsmtxca" || prepared.data.details !== void 0;
|
|
3042
|
+
const record = {
|
|
3043
|
+
v: versioned ? 2 : 1,
|
|
3044
|
+
operation,
|
|
3045
|
+
...versioned ? { service } : {},
|
|
3046
|
+
representedTaxId,
|
|
3047
|
+
inputHash,
|
|
3048
|
+
number,
|
|
3049
|
+
salesPoint: prepared.data.salesPoint,
|
|
3050
|
+
voucherType: prepared.data.voucherType,
|
|
3051
|
+
sent: prepared.data,
|
|
3052
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3053
|
+
};
|
|
3054
|
+
if (await storeCall(() => store.add(key, JSON.stringify(record)))) {
|
|
3055
|
+
const claimed = {
|
|
3056
|
+
v: 1,
|
|
3057
|
+
key: idempotencyKey,
|
|
3058
|
+
number,
|
|
3059
|
+
claimedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3060
|
+
};
|
|
3061
|
+
const coordinated = store.withLock !== void 0;
|
|
3062
|
+
if (coordinated) {
|
|
3063
|
+
await storeCall(
|
|
3064
|
+
() => store.set(sequenceRecord, JSON.stringify(claimed))
|
|
3065
|
+
);
|
|
3066
|
+
}
|
|
3067
|
+
const outcome = await settle(
|
|
3068
|
+
runAuthorization(wsfe, prepared, options, number)
|
|
3069
|
+
);
|
|
3070
|
+
if (coordinated && outcome.kind !== "indeterminate") {
|
|
3071
|
+
await storeCall(
|
|
3072
|
+
() => store.set(
|
|
3073
|
+
sequenceRecord,
|
|
3074
|
+
JSON.stringify({ ...claimed, resolvedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
3075
|
+
)
|
|
3076
|
+
);
|
|
3077
|
+
}
|
|
3078
|
+
return outcome;
|
|
3079
|
+
}
|
|
3080
|
+
const winner = await storeCall(() => store.get(key));
|
|
3081
|
+
if (winner === null) {
|
|
3082
|
+
throw new ArcaConfigurationError(
|
|
3083
|
+
"ARCA reservation disappeared after atomic creation lost."
|
|
3084
|
+
);
|
|
3085
|
+
}
|
|
3086
|
+
return await replay(winner, true);
|
|
2967
3087
|
}
|
|
2968
|
-
|
|
2969
|
-
function replay(json) {
|
|
3088
|
+
async function replay(json, locked = false) {
|
|
2970
3089
|
const stored = readRecord(json);
|
|
2971
3090
|
if ((stored.service ?? "wsfe") !== (options.service ?? "wsfe") || stored.operation !== operation || stored.inputHash !== inputHash || stored.representedTaxId !== representedTaxId) {
|
|
2972
3091
|
throw new ArcaInputError(
|
|
@@ -2977,17 +3096,107 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
|
|
|
2977
3096
|
}
|
|
2978
3097
|
);
|
|
2979
3098
|
}
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
{
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
3099
|
+
const consult = async () => {
|
|
3100
|
+
const recorded = await storeCall(() => store.get(settled));
|
|
3101
|
+
if (recorded !== null) {
|
|
3102
|
+
return await settledOutcome(
|
|
3103
|
+
select ?? (() => wsfe),
|
|
3104
|
+
stored,
|
|
3105
|
+
readSettledRecord(recorded),
|
|
3106
|
+
options
|
|
3107
|
+
);
|
|
3108
|
+
}
|
|
3109
|
+
return await settle(
|
|
3110
|
+
runAuthorization(
|
|
3111
|
+
wsfe,
|
|
3112
|
+
{
|
|
3113
|
+
...preparedFromRecord(stored),
|
|
3114
|
+
...replayAmounts ? { amounts: replayAmounts } : {}
|
|
3115
|
+
},
|
|
3116
|
+
options,
|
|
3117
|
+
stored.number,
|
|
3118
|
+
true
|
|
3119
|
+
)
|
|
3120
|
+
);
|
|
3121
|
+
};
|
|
3122
|
+
if (locked || !store.withLock) {
|
|
3123
|
+
return await consult();
|
|
3124
|
+
}
|
|
3125
|
+
return await store.withLock(
|
|
3126
|
+
sequenceLockKey(
|
|
3127
|
+
environment,
|
|
3128
|
+
stored.representedTaxId ?? taxId,
|
|
3129
|
+
stored.salesPoint,
|
|
3130
|
+
stored.voucherType
|
|
3131
|
+
),
|
|
3132
|
+
consult
|
|
2989
3133
|
);
|
|
2990
3134
|
}
|
|
3135
|
+
async function settle(running) {
|
|
3136
|
+
return await recordConflict(store, settled, await running);
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
async function recordConflict(store, key, outcome) {
|
|
3140
|
+
if (outcome.kind !== "conflict") {
|
|
3141
|
+
return outcome;
|
|
3142
|
+
}
|
|
3143
|
+
const record = {
|
|
3144
|
+
v: 1,
|
|
3145
|
+
kind: "conflict",
|
|
3146
|
+
number: outcome.attempted.number,
|
|
3147
|
+
found: Object.fromEntries(
|
|
3148
|
+
Object.entries(outcome.found).filter(([field]) => field !== "raw")
|
|
3149
|
+
),
|
|
3150
|
+
settledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3151
|
+
};
|
|
3152
|
+
await storeCall(() => store.add(key, JSON.stringify(record)));
|
|
3153
|
+
return outcome;
|
|
3154
|
+
}
|
|
3155
|
+
function readSettledRecord(json) {
|
|
3156
|
+
try {
|
|
3157
|
+
const record = JSON.parse(json);
|
|
3158
|
+
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")) {
|
|
3159
|
+
throw new Error("Invalid settled structure");
|
|
3160
|
+
}
|
|
3161
|
+
return record;
|
|
3162
|
+
} catch (cause) {
|
|
3163
|
+
throw new ArcaConfigurationError(
|
|
3164
|
+
"Invalid ARCA settled record; preserve it for reconciliation.",
|
|
3165
|
+
{ cause }
|
|
3166
|
+
);
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
async function settledOutcome(select, reservation, settled, options) {
|
|
3170
|
+
if (settled.kind === "conflict") {
|
|
3171
|
+
return settledConflict(settled, reservation);
|
|
3172
|
+
}
|
|
3173
|
+
const outcome = await consultReservation(select, reservation, options, true);
|
|
3174
|
+
if (outcome.kind === "indeterminate" && outcome.lookup.kind === "not_found") {
|
|
3175
|
+
return {
|
|
3176
|
+
kind: "indeterminate",
|
|
3177
|
+
attempted: {
|
|
3178
|
+
salesPoint: reservation.salesPoint,
|
|
3179
|
+
voucherType: reservation.voucherType,
|
|
3180
|
+
number: settled.number
|
|
3181
|
+
},
|
|
3182
|
+
attempt: replayEvidence(reservation.service),
|
|
3183
|
+
lookup: { kind: "superseded", by: settled.by }
|
|
3184
|
+
};
|
|
3185
|
+
}
|
|
3186
|
+
return outcome;
|
|
3187
|
+
}
|
|
3188
|
+
function settledConflict(settled, reservation) {
|
|
3189
|
+
return {
|
|
3190
|
+
kind: "conflict",
|
|
3191
|
+
attempted: {
|
|
3192
|
+
salesPoint: reservation.salesPoint,
|
|
3193
|
+
voucherType: reservation.voucherType,
|
|
3194
|
+
number: settled.number
|
|
3195
|
+
},
|
|
3196
|
+
attempt: replayEvidence(reservation.service),
|
|
3197
|
+
found: settled.found,
|
|
3198
|
+
reason: "This key already recorded another voucher at the reserved number. Reconcile before issuing under a new key."
|
|
3199
|
+
};
|
|
2991
3200
|
}
|
|
2992
3201
|
function validateKeyStore(options, context) {
|
|
2993
3202
|
if (options.idempotencyKey === void 0) {
|
|
@@ -3043,6 +3252,7 @@ async function nextNumber(wsfe, data, options) {
|
|
|
3043
3252
|
const number = await wsfe.getNextVoucherNumber({
|
|
3044
3253
|
representedTaxId: options.representedTaxId,
|
|
3045
3254
|
forceRefresh: options.forceRefresh,
|
|
3255
|
+
...options.signal === void 0 ? {} : { signal: options.signal },
|
|
3046
3256
|
salesPoint: data.salesPoint,
|
|
3047
3257
|
voucherType: data.voucherType
|
|
3048
3258
|
});
|
|
@@ -3060,7 +3270,8 @@ async function nextNumber(wsfe, data, options) {
|
|
|
3060
3270
|
async function runAuthorization(wsfe, { data, voucherClass, amounts }, options, reservedNumber, replay = false) {
|
|
3061
3271
|
const auth = {
|
|
3062
3272
|
representedTaxId: options.representedTaxId,
|
|
3063
|
-
forceRefresh: options.forceRefresh
|
|
3273
|
+
forceRefresh: options.forceRefresh,
|
|
3274
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
3064
3275
|
};
|
|
3065
3276
|
const includeRaw = options.include?.raw === true;
|
|
3066
3277
|
const includeExact = options.include?.exactInput === true;
|
|
@@ -3099,7 +3310,7 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3099
3310
|
kind: "indeterminate",
|
|
3100
3311
|
attempted,
|
|
3101
3312
|
attempt: attempt2,
|
|
3102
|
-
lookup: { kind: "failed", error: toArcaSafeErrorMetadata(error) }
|
|
3313
|
+
lookup: options.signal?.aborted ? { kind: "aborted" } : { kind: "failed", error: toArcaSafeErrorMetadata(error) }
|
|
3103
3314
|
};
|
|
3104
3315
|
}
|
|
3105
3316
|
if (lookup.kind === "found") {
|
|
@@ -3121,32 +3332,13 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3121
3332
|
};
|
|
3122
3333
|
}
|
|
3123
3334
|
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
|
-
};
|
|
3335
|
+
return await resolveRejection(
|
|
3336
|
+
authorization,
|
|
3337
|
+
recovery,
|
|
3338
|
+
options,
|
|
3339
|
+
includeRaw,
|
|
3340
|
+
replay
|
|
3341
|
+
);
|
|
3150
3342
|
}
|
|
3151
3343
|
const uncertain = authorization.kind === "indeterminate" ? authorization : {
|
|
3152
3344
|
...authorization,
|
|
@@ -3166,6 +3358,33 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
|
|
|
3166
3358
|
voucher
|
|
3167
3359
|
});
|
|
3168
3360
|
}
|
|
3361
|
+
async function resolveRejection(authorization, recovery, options, includeRaw, replay) {
|
|
3362
|
+
const issues = [...authorization.errors, ...authorization.observations];
|
|
3363
|
+
const consult = options.idempotencyKey !== void 0 && (options.service === "wsmtxca" || issues.some((issue) => issue.code === "10016"));
|
|
3364
|
+
if (consult) {
|
|
3365
|
+
const recovered = await recoverInvoice({
|
|
3366
|
+
...recovery,
|
|
3367
|
+
attempt: projectEvidence(
|
|
3368
|
+
{
|
|
3369
|
+
...authorization,
|
|
3370
|
+
kind: "indeterminate",
|
|
3371
|
+
reason: "contradictory_response"
|
|
3372
|
+
},
|
|
3373
|
+
includeRaw
|
|
3374
|
+
),
|
|
3375
|
+
strangerAtNumber: !replay
|
|
3376
|
+
});
|
|
3377
|
+
if (recovered.kind === "authorized" || recovered.kind === "conflict") {
|
|
3378
|
+
return recovered;
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
return {
|
|
3382
|
+
kind: "rejected",
|
|
3383
|
+
attempted: recovery.attempted,
|
|
3384
|
+
issues: issues.map(projectIssue),
|
|
3385
|
+
authorization: projectEvidence(authorization, includeRaw)
|
|
3386
|
+
};
|
|
3387
|
+
}
|
|
3169
3388
|
async function recoverInvoice({
|
|
3170
3389
|
wsfe,
|
|
3171
3390
|
auth,
|
|
@@ -3176,8 +3395,17 @@ async function recoverInvoice({
|
|
|
3176
3395
|
exact,
|
|
3177
3396
|
voucher,
|
|
3178
3397
|
lookup: suppliedLookup,
|
|
3179
|
-
service
|
|
3398
|
+
service,
|
|
3399
|
+
strangerAtNumber = false
|
|
3180
3400
|
}) {
|
|
3401
|
+
if (suppliedLookup === void 0 && auth.signal?.aborted) {
|
|
3402
|
+
return {
|
|
3403
|
+
kind: "indeterminate",
|
|
3404
|
+
attempted,
|
|
3405
|
+
attempt,
|
|
3406
|
+
lookup: { kind: "aborted" }
|
|
3407
|
+
};
|
|
3408
|
+
}
|
|
3181
3409
|
let lookup;
|
|
3182
3410
|
try {
|
|
3183
3411
|
lookup = suppliedLookup ?? await wsfe.lookupVoucher({ ...auth, ...attempted });
|
|
@@ -3186,7 +3414,7 @@ async function recoverInvoice({
|
|
|
3186
3414
|
kind: "indeterminate",
|
|
3187
3415
|
attempted,
|
|
3188
3416
|
attempt,
|
|
3189
|
-
lookup: { kind: "failed", error: toArcaSafeErrorMetadata(error) }
|
|
3417
|
+
lookup: auth.signal?.aborted ? { kind: "aborted" } : { kind: "failed", error: toArcaSafeErrorMetadata(error) }
|
|
3190
3418
|
};
|
|
3191
3419
|
}
|
|
3192
3420
|
const raw = includeRaw ? { raw: lookup.raw } : {};
|
|
@@ -3198,6 +3426,15 @@ async function recoverInvoice({
|
|
|
3198
3426
|
lookup: { kind: "not_found", ...raw }
|
|
3199
3427
|
};
|
|
3200
3428
|
}
|
|
3429
|
+
if (strangerAtNumber) {
|
|
3430
|
+
return {
|
|
3431
|
+
kind: "conflict",
|
|
3432
|
+
attempted,
|
|
3433
|
+
attempt,
|
|
3434
|
+
found: { ...toVoucherSummary(lookup.voucher), ...raw },
|
|
3435
|
+
reason: "ARCA refused the number this call reserved and another voucher occupies it"
|
|
3436
|
+
};
|
|
3437
|
+
}
|
|
3201
3438
|
const detailsMatch = service === "wsmtxca" ? matchWsmtxcaDetails(data, attempted.number, lookup.voucher.raw) : void 0;
|
|
3202
3439
|
const matched = detailsMatch === void 0 ? matchWsfeVoucherIdentity(data, attempted.number, lookup.voucher) : detailsMatch === "match" && lookup.voucher.cae && lookup.voucher.caeExpiry ? { matches: true } : {
|
|
3203
3440
|
matches: false,
|
|
@@ -3281,6 +3518,14 @@ function projectEvidence(evidence, includeRaw) {
|
|
|
3281
3518
|
}
|
|
3282
3519
|
return projected;
|
|
3283
3520
|
}
|
|
3521
|
+
function cloneOptions(options) {
|
|
3522
|
+
assertIssueObject(options, "options");
|
|
3523
|
+
const { signal, ...rest } = options;
|
|
3524
|
+
return {
|
|
3525
|
+
...structuredClone(rest),
|
|
3526
|
+
...signal === void 0 ? {} : { signal }
|
|
3527
|
+
};
|
|
3528
|
+
}
|
|
3284
3529
|
function validateOptions(options) {
|
|
3285
3530
|
assertIssueObject(options, "options");
|
|
3286
3531
|
assertIssueKeys(
|
|
@@ -3291,10 +3536,17 @@ function validateOptions(options) {
|
|
|
3291
3536
|
"include",
|
|
3292
3537
|
"idempotencyKey",
|
|
3293
3538
|
"service",
|
|
3294
|
-
"number"
|
|
3539
|
+
"number",
|
|
3540
|
+
"signal"
|
|
3295
3541
|
],
|
|
3296
3542
|
"options"
|
|
3297
3543
|
);
|
|
3544
|
+
if (options.signal !== void 0 && (typeof options.signal !== "object" || options.signal === null || typeof options.signal.aborted !== "boolean" || typeof options.signal.addEventListener !== "function")) {
|
|
3545
|
+
throw new ArcaInputError("options.signal must be an AbortSignal.", {
|
|
3546
|
+
code: "ARCA_INPUT_INVALID_VALUE",
|
|
3547
|
+
field: "options.signal"
|
|
3548
|
+
});
|
|
3549
|
+
}
|
|
3298
3550
|
if (options.service !== void 0 && options.service !== "wsfe" && options.service !== "wsmtxca") {
|
|
3299
3551
|
throw new ArcaInputError("Unknown fiscal service", {
|
|
3300
3552
|
code: "ARCA_INPUT_INVALID_VALUE",
|
|
@@ -3348,8 +3600,8 @@ function replayEvidence(service = "wsfe") {
|
|
|
3348
3600
|
observations: []
|
|
3349
3601
|
};
|
|
3350
3602
|
}
|
|
3351
|
-
async function issueCreditNote(wsfe, input, inputOptions, context, kind = "creditNote") {
|
|
3352
|
-
const options =
|
|
3603
|
+
async function issueCreditNote(wsfe, input, inputOptions, context, kind = "creditNote", select) {
|
|
3604
|
+
const options = cloneOptions(inputOptions);
|
|
3353
3605
|
validateOptions(options);
|
|
3354
3606
|
validateKeyStore(options, context);
|
|
3355
3607
|
assertIssueObject(input, "input");
|
|
@@ -3366,7 +3618,9 @@ async function issueCreditNote(wsfe, input, inputOptions, context, kind = "credi
|
|
|
3366
3618
|
note,
|
|
3367
3619
|
() => prepareNote(wsfe, note, options, context, kind),
|
|
3368
3620
|
options,
|
|
3369
|
-
context
|
|
3621
|
+
context,
|
|
3622
|
+
void 0,
|
|
3623
|
+
select
|
|
3370
3624
|
);
|
|
3371
3625
|
}
|
|
3372
3626
|
async function previewNote(wsfe, input, inputOptions, context, kind) {
|
|
@@ -3467,11 +3721,11 @@ function exactEvidence(data, number, options, include) {
|
|
|
3467
3721
|
} : {};
|
|
3468
3722
|
}
|
|
3469
3723
|
async function recoverOperation(select, key, inputOptions, context) {
|
|
3470
|
-
const options =
|
|
3724
|
+
const options = cloneOptions(inputOptions);
|
|
3471
3725
|
assertIssueObject(options, "options");
|
|
3472
3726
|
assertIssueKeys(
|
|
3473
3727
|
options,
|
|
3474
|
-
["representedTaxId", "forceRefresh", "include"],
|
|
3728
|
+
["representedTaxId", "forceRefresh", "include", "signal"],
|
|
3475
3729
|
"options"
|
|
3476
3730
|
);
|
|
3477
3731
|
validateOptions(options);
|
|
@@ -3492,28 +3746,52 @@ async function recoverOperation(select, key, inputOptions, context) {
|
|
|
3492
3746
|
});
|
|
3493
3747
|
}
|
|
3494
3748
|
const record = readRecord(json);
|
|
3749
|
+
const store = context.store;
|
|
3750
|
+
const settled = settledKey(
|
|
3751
|
+
context.environment,
|
|
3752
|
+
context.taxId,
|
|
3753
|
+
key
|
|
3754
|
+
);
|
|
3495
3755
|
if (options.representedTaxId !== void 0 && String(options.representedTaxId) !== (record.representedTaxId ?? context?.taxId)) {
|
|
3496
3756
|
throw new ArcaInputError(
|
|
3497
3757
|
"Reservation belongs to another represented taxpayer",
|
|
3498
3758
|
{ code: "ARCA_INPUT_IDEMPOTENCY_MISMATCH" }
|
|
3499
3759
|
);
|
|
3500
3760
|
}
|
|
3761
|
+
const recorded = await storeCall(() => store.get(settled));
|
|
3762
|
+
if (recorded !== null) {
|
|
3763
|
+
return await settledOutcome(
|
|
3764
|
+
select,
|
|
3765
|
+
record,
|
|
3766
|
+
readSettledRecord(recorded),
|
|
3767
|
+
options
|
|
3768
|
+
);
|
|
3769
|
+
}
|
|
3770
|
+
return await recordConflict(
|
|
3771
|
+
store,
|
|
3772
|
+
settled,
|
|
3773
|
+
await consultReservation(select, record, options)
|
|
3774
|
+
);
|
|
3775
|
+
}
|
|
3776
|
+
function consultReservation(select, record, options, strangerAtNumber = false) {
|
|
3501
3777
|
const storedOptions = {
|
|
3502
3778
|
...options,
|
|
3503
3779
|
service: record.service ?? "wsfe",
|
|
3504
3780
|
representedTaxId: record.representedTaxId
|
|
3505
3781
|
};
|
|
3506
3782
|
const prepared = preparedFromRecord(record);
|
|
3783
|
+
const attempted = {
|
|
3784
|
+
salesPoint: record.salesPoint,
|
|
3785
|
+
voucherType: record.voucherType,
|
|
3786
|
+
number: record.number
|
|
3787
|
+
};
|
|
3507
3788
|
return recoverInvoice({
|
|
3508
3789
|
wsfe: select(storedOptions),
|
|
3509
3790
|
service: storedOptions.service,
|
|
3510
3791
|
auth: storedOptions,
|
|
3511
3792
|
data: prepared.data,
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
voucherType: record.voucherType,
|
|
3515
|
-
number: record.number
|
|
3516
|
-
},
|
|
3793
|
+
strangerAtNumber,
|
|
3794
|
+
attempted,
|
|
3517
3795
|
attempt: replayEvidence(storedOptions.service),
|
|
3518
3796
|
includeRaw: options.include?.raw === true,
|
|
3519
3797
|
exact: exactEvidence(
|
|
@@ -3523,9 +3801,7 @@ async function recoverOperation(select, key, inputOptions, context) {
|
|
|
3523
3801
|
options.include?.exactInput === true
|
|
3524
3802
|
),
|
|
3525
3803
|
voucher: (cae, caeExpiry) => ({
|
|
3526
|
-
|
|
3527
|
-
voucherType: record.voucherType,
|
|
3528
|
-
number: record.number,
|
|
3804
|
+
...attempted,
|
|
3529
3805
|
voucherClass: prepared.voucherClass,
|
|
3530
3806
|
date: prepared.data.voucherDate,
|
|
3531
3807
|
amounts: prepared.amounts,
|
|
@@ -3534,6 +3810,89 @@ async function recoverOperation(select, key, inputOptions, context) {
|
|
|
3534
3810
|
})
|
|
3535
3811
|
});
|
|
3536
3812
|
}
|
|
3813
|
+
async function runSequenceBarrier({
|
|
3814
|
+
store,
|
|
3815
|
+
environment,
|
|
3816
|
+
taxId,
|
|
3817
|
+
sequence,
|
|
3818
|
+
coordinates,
|
|
3819
|
+
select,
|
|
3820
|
+
options,
|
|
3821
|
+
supersededBy,
|
|
3822
|
+
readNext
|
|
3823
|
+
}) {
|
|
3824
|
+
const json = await storeCall(() => store.get(sequence));
|
|
3825
|
+
if (json === null) {
|
|
3826
|
+
return {};
|
|
3827
|
+
}
|
|
3828
|
+
const claimed = readSequenceRecord(json);
|
|
3829
|
+
if (claimed.resolvedAt !== void 0) {
|
|
3830
|
+
return {};
|
|
3831
|
+
}
|
|
3832
|
+
const settled = settledKey(environment, taxId, claimed.key);
|
|
3833
|
+
if (await storeCall(() => store.get(settled)) !== null) {
|
|
3834
|
+
return {};
|
|
3835
|
+
}
|
|
3836
|
+
const reservation = await storeCall(
|
|
3837
|
+
() => store.get(attemptKey(environment, taxId, claimed.key))
|
|
3838
|
+
);
|
|
3839
|
+
if (reservation === null) {
|
|
3840
|
+
return {};
|
|
3841
|
+
}
|
|
3842
|
+
const blocked = {
|
|
3843
|
+
blocked: {
|
|
3844
|
+
kind: "indeterminate",
|
|
3845
|
+
attempted: { ...coordinates, number: claimed.number },
|
|
3846
|
+
attempt: replayEvidence(options.service),
|
|
3847
|
+
lookup: { kind: "blocked", by: claimed.key }
|
|
3848
|
+
}
|
|
3849
|
+
};
|
|
3850
|
+
const outcome = await recordConflict(
|
|
3851
|
+
store,
|
|
3852
|
+
settled,
|
|
3853
|
+
await consultReservation(select, readRecord(reservation), {
|
|
3854
|
+
forceRefresh: options.forceRefresh,
|
|
3855
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
3856
|
+
})
|
|
3857
|
+
);
|
|
3858
|
+
if (outcome.kind === "authorized" || outcome.kind === "conflict") {
|
|
3859
|
+
return {};
|
|
3860
|
+
}
|
|
3861
|
+
if (outcome.kind !== "indeterminate" || outcome.lookup.kind !== "not_found") {
|
|
3862
|
+
return blocked;
|
|
3863
|
+
}
|
|
3864
|
+
const next = await readNext();
|
|
3865
|
+
if (next !== claimed.number) {
|
|
3866
|
+
return blocked;
|
|
3867
|
+
}
|
|
3868
|
+
await storeCall(
|
|
3869
|
+
() => store.add(
|
|
3870
|
+
settled,
|
|
3871
|
+
JSON.stringify({
|
|
3872
|
+
v: 1,
|
|
3873
|
+
kind: "superseded",
|
|
3874
|
+
number: claimed.number,
|
|
3875
|
+
by: supersededBy,
|
|
3876
|
+
settledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3877
|
+
})
|
|
3878
|
+
)
|
|
3879
|
+
);
|
|
3880
|
+
return { reserved: next };
|
|
3881
|
+
}
|
|
3882
|
+
function readSequenceRecord(json) {
|
|
3883
|
+
try {
|
|
3884
|
+
const record = JSON.parse(json);
|
|
3885
|
+
if (!record || record.v !== 1 || typeof record.key !== "string" || !Number.isSafeInteger(record.number)) {
|
|
3886
|
+
throw new Error("Invalid sequence structure");
|
|
3887
|
+
}
|
|
3888
|
+
return record;
|
|
3889
|
+
} catch (cause) {
|
|
3890
|
+
throw new ArcaConfigurationError(
|
|
3891
|
+
"Invalid ARCA sequence record; delete the sequence key to resume.",
|
|
3892
|
+
{ cause }
|
|
3893
|
+
);
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3537
3896
|
|
|
3538
3897
|
// src/soap/index.ts
|
|
3539
3898
|
function createSoapTransport(options) {
|
|
@@ -3572,7 +3931,8 @@ function createSoapTransport(options) {
|
|
|
3572
3931
|
retryDelay: options.config.retryDelay,
|
|
3573
3932
|
logger: options.logger,
|
|
3574
3933
|
service: request.service,
|
|
3575
|
-
operation: request.operation
|
|
3934
|
+
operation: request.operation,
|
|
3935
|
+
signal: request.signal
|
|
3576
3936
|
});
|
|
3577
3937
|
options.logger?.debug("Received ARCA SOAP response", {
|
|
3578
3938
|
service: request.service,
|
|
@@ -3688,9 +4048,56 @@ function createArcaClient(config = {}) {
|
|
|
3688
4048
|
}
|
|
3689
4049
|
|
|
3690
4050
|
// src/store/file.ts
|
|
3691
|
-
import { createHash as createHash3, randomUUID } from "crypto";
|
|
3692
|
-
import {
|
|
4051
|
+
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
4052
|
+
import {
|
|
4053
|
+
mkdir,
|
|
4054
|
+
readFile,
|
|
4055
|
+
rename,
|
|
4056
|
+
rm,
|
|
4057
|
+
stat,
|
|
4058
|
+
unlink,
|
|
4059
|
+
writeFile
|
|
4060
|
+
} from "fs/promises";
|
|
3693
4061
|
import { join } from "path";
|
|
4062
|
+
|
|
4063
|
+
// src/store/lock.ts
|
|
4064
|
+
import { randomUUID } from "crypto";
|
|
4065
|
+
var ARCA_LEASE_MS = 6e4;
|
|
4066
|
+
var RENEW_MS = 2e4;
|
|
4067
|
+
var POLL_MS = 50;
|
|
4068
|
+
var MAX_WAIT_MS = 2 * ARCA_LEASE_MS;
|
|
4069
|
+
async function withLease(key, driver, fn) {
|
|
4070
|
+
const owner = randomUUID();
|
|
4071
|
+
const deadline = Date.now() + MAX_WAIT_MS;
|
|
4072
|
+
let held = await driver.acquire(owner);
|
|
4073
|
+
while (!held) {
|
|
4074
|
+
if (Date.now() >= deadline) {
|
|
4075
|
+
throw new ArcaConfigurationError(
|
|
4076
|
+
`ARCA store lock ${key} stayed held; no work was attempted.`
|
|
4077
|
+
);
|
|
4078
|
+
}
|
|
4079
|
+
await delay2(POLL_MS + Math.floor(Math.random() * POLL_MS));
|
|
4080
|
+
held = await driver.acquire(owner);
|
|
4081
|
+
}
|
|
4082
|
+
const renewal = setInterval(() => {
|
|
4083
|
+
driver.renew(owner).catch(() => void 0);
|
|
4084
|
+
}, RENEW_MS);
|
|
4085
|
+
renewal.unref?.();
|
|
4086
|
+
try {
|
|
4087
|
+
return await fn();
|
|
4088
|
+
} finally {
|
|
4089
|
+
clearInterval(renewal);
|
|
4090
|
+
await driver.release(owner).catch(() => void 0);
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
function delay2(ms) {
|
|
4094
|
+
return new Promise((resolve) => {
|
|
4095
|
+
const timer = setTimeout(resolve, ms);
|
|
4096
|
+
timer.unref?.();
|
|
4097
|
+
});
|
|
4098
|
+
}
|
|
4099
|
+
|
|
4100
|
+
// src/store/file.ts
|
|
3694
4101
|
function createFileStore(directory) {
|
|
3695
4102
|
const path = (key) => join(directory, createHash3("sha256").update(key).digest("hex"));
|
|
3696
4103
|
const ensure = () => mkdir(directory, { recursive: true, mode: 448 });
|
|
@@ -3707,7 +4114,7 @@ function createFileStore(directory) {
|
|
|
3707
4114
|
}),
|
|
3708
4115
|
set: (key, value) => storeCall(async () => {
|
|
3709
4116
|
await ensure();
|
|
3710
|
-
const temporary = `${path(key)}.${
|
|
4117
|
+
const temporary = `${path(key)}.${randomUUID2()}.tmp`;
|
|
3711
4118
|
try {
|
|
3712
4119
|
await writeFile(temporary, value, { mode: 384, flag: "wx" });
|
|
3713
4120
|
await rename(temporary, path(key));
|
|
@@ -3735,9 +4142,85 @@ function createFileStore(directory) {
|
|
|
3735
4142
|
throw error;
|
|
3736
4143
|
}
|
|
3737
4144
|
}
|
|
4145
|
+
}),
|
|
4146
|
+
withLock: (key, fn) => (
|
|
4147
|
+
// A lock directory: mkdir is the atomic claim on every POSIX filesystem
|
|
4148
|
+
// and on Windows, and the holder file inside carries the lease.
|
|
4149
|
+
withLease(key, fileLease(`${path(key)}.lock`, ensure), fn)
|
|
4150
|
+
)
|
|
4151
|
+
};
|
|
4152
|
+
}
|
|
4153
|
+
function fileLease(directory, ensure) {
|
|
4154
|
+
const holder = join(directory, "holder");
|
|
4155
|
+
const claim = (owner) => JSON.stringify({
|
|
4156
|
+
owner,
|
|
4157
|
+
expiresAt: new Date(Date.now() + ARCA_LEASE_MS).toISOString()
|
|
4158
|
+
});
|
|
4159
|
+
const read = async () => {
|
|
4160
|
+
try {
|
|
4161
|
+
return JSON.parse(await readFile(holder, "utf8"));
|
|
4162
|
+
} catch {
|
|
4163
|
+
return null;
|
|
4164
|
+
}
|
|
4165
|
+
};
|
|
4166
|
+
return {
|
|
4167
|
+
acquire: (owner) => storeCall(async () => {
|
|
4168
|
+
await ensure();
|
|
4169
|
+
if (!await createDirectory(directory)) {
|
|
4170
|
+
if (await stillHeld(directory, await read())) {
|
|
4171
|
+
return false;
|
|
4172
|
+
}
|
|
4173
|
+
await rm(directory, { recursive: true, force: true });
|
|
4174
|
+
if (!await createDirectory(directory)) {
|
|
4175
|
+
return false;
|
|
4176
|
+
}
|
|
4177
|
+
}
|
|
4178
|
+
return await writeHolder(holder, claim(owner));
|
|
4179
|
+
}),
|
|
4180
|
+
renew: (owner) => storeCall(async () => {
|
|
4181
|
+
if ((await read())?.owner === owner) {
|
|
4182
|
+
await writeFile(holder, claim(owner), { mode: 384 });
|
|
4183
|
+
}
|
|
4184
|
+
}),
|
|
4185
|
+
release: (owner) => storeCall(async () => {
|
|
4186
|
+
if ((await read())?.owner === owner) {
|
|
4187
|
+
await rm(directory, { recursive: true, force: true });
|
|
4188
|
+
}
|
|
3738
4189
|
})
|
|
3739
4190
|
};
|
|
3740
4191
|
}
|
|
4192
|
+
async function createDirectory(directory) {
|
|
4193
|
+
try {
|
|
4194
|
+
await mkdir(directory);
|
|
4195
|
+
return true;
|
|
4196
|
+
} catch (error) {
|
|
4197
|
+
if (error.code === "EEXIST") {
|
|
4198
|
+
return false;
|
|
4199
|
+
}
|
|
4200
|
+
throw error;
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
async function writeHolder(holder, value) {
|
|
4204
|
+
try {
|
|
4205
|
+
await writeFile(holder, value, { mode: 384, flag: "wx" });
|
|
4206
|
+
return true;
|
|
4207
|
+
} catch (error) {
|
|
4208
|
+
if (error.code === "EEXIST") {
|
|
4209
|
+
return false;
|
|
4210
|
+
}
|
|
4211
|
+
throw error;
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
async function stillHeld(directory, holder) {
|
|
4215
|
+
if (holder) {
|
|
4216
|
+
return Date.parse(holder.expiresAt) > Date.now();
|
|
4217
|
+
}
|
|
4218
|
+
try {
|
|
4219
|
+
return (await stat(directory)).mtimeMs + ARCA_LEASE_MS > Date.now();
|
|
4220
|
+
} catch {
|
|
4221
|
+
return false;
|
|
4222
|
+
}
|
|
4223
|
+
}
|
|
3741
4224
|
|
|
3742
4225
|
export {
|
|
3743
4226
|
ARCA_ENVIRONMENTS,
|
|
@@ -3753,6 +4236,8 @@ export {
|
|
|
3753
4236
|
createWsaaAuthModule,
|
|
3754
4237
|
createWsaaStoreAdapter,
|
|
3755
4238
|
createArcaClient,
|
|
4239
|
+
ARCA_LEASE_MS,
|
|
4240
|
+
withLease,
|
|
3756
4241
|
createFileStore
|
|
3757
4242
|
};
|
|
3758
|
-
//# sourceMappingURL=chunk-
|
|
4243
|
+
//# sourceMappingURL=chunk-QEJCLJDS.mjs.map
|