facturas 0.20.4 → 0.21.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.
Files changed (39) hide show
  1. package/dist/{chunk-B2M64SIK.mjs → chunk-4PSLYA2B.mjs} +2 -2
  2. package/dist/{chunk-6U4NRHFP.mjs → chunk-FQD2PZXE.mjs} +88 -23
  3. package/dist/chunk-FQD2PZXE.mjs.map +1 -0
  4. package/dist/chunk-HLSFMWRT.mjs +38 -0
  5. package/dist/chunk-HLSFMWRT.mjs.map +1 -0
  6. package/dist/{chunk-WG6TAMBB.mjs → chunk-HSGTCGUX.mjs} +83 -30
  7. package/dist/chunk-HSGTCGUX.mjs.map +1 -0
  8. package/dist/{chunk-NT2A6YMO.mjs → chunk-Q7IURW54.mjs} +12 -1
  9. package/dist/chunk-Q7IURW54.mjs.map +1 -0
  10. package/dist/{chunk-UOIZQMA6.mjs → chunk-QHNI4ZIA.mjs} +3 -3
  11. package/dist/{chunk-CYXS3F34.mjs → chunk-WEEV76IC.mjs} +284 -161
  12. package/dist/chunk-WEEV76IC.mjs.map +1 -0
  13. package/dist/cli.d.ts +4 -4
  14. package/dist/cli.mjs +7 -7
  15. package/dist/{client-BgWH_jKT.d.ts → client-CP1PQD_j.d.ts} +2 -2
  16. package/dist/errors.d.ts +2 -2
  17. package/dist/errors.mjs +3 -1
  18. package/dist/{fiscal-evidence-Bg4o9nxt.d.ts → fiscal-evidence-Cp-ntg5p.d.ts} +20 -4
  19. package/dist/{index-DAC3edtw.d.ts → index-abxokwjJ.d.ts} +1 -1
  20. package/dist/index.d.ts +5 -5
  21. package/dist/index.mjs +32 -13
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/padron.d.ts +2 -2
  24. package/dist/padron.mjs +3 -3
  25. package/dist/{types-Su7QwloQ.d.ts → types-DhrPnj3r.d.ts} +12 -3
  26. package/dist/types.d.ts +1 -1
  27. package/dist/wsfe.d.ts +3 -3
  28. package/dist/wsfe.mjs +4 -4
  29. package/dist/wsmtxca.d.ts +3 -3
  30. package/dist/wsmtxca.mjs +4 -4
  31. package/package.json +1 -1
  32. package/dist/chunk-6U4NRHFP.mjs.map +0 -1
  33. package/dist/chunk-CYXS3F34.mjs.map +0 -1
  34. package/dist/chunk-KDLY2XJO.mjs +0 -19
  35. package/dist/chunk-KDLY2XJO.mjs.map +0 -1
  36. package/dist/chunk-NT2A6YMO.mjs.map +0 -1
  37. package/dist/chunk-WG6TAMBB.mjs.map +0 -1
  38. /package/dist/{chunk-B2M64SIK.mjs.map → chunk-4PSLYA2B.mjs.map} +0 -0
  39. /package/dist/{chunk-UOIZQMA6.mjs.map → chunk-QHNI4ZIA.mjs.map} +0 -0
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-C2KX652X.mjs";
11
11
  import {
12
12
  createPadronService
13
- } from "./chunk-UOIZQMA6.mjs";
13
+ } from "./chunk-QHNI4ZIA.mjs";
14
14
  import {
15
15
  arcaMinorUnitsToNumber,
16
16
  assertArcaMinorUnits,
@@ -22,22 +22,23 @@ import {
22
22
  roundHalfEvenRatio,
23
23
  serializeArcaExchangeRate,
24
24
  toIsoDate
25
- } from "./chunk-6U4NRHFP.mjs";
25
+ } from "./chunk-FQD2PZXE.mjs";
26
26
  import {
27
27
  createWsmtxcaService
28
- } from "./chunk-WG6TAMBB.mjs";
28
+ } from "./chunk-HSGTCGUX.mjs";
29
29
  import {
30
30
  ArcaConfigurationError,
31
31
  ArcaError,
32
32
  ArcaInputError,
33
33
  ArcaInvalidSoapResponseError,
34
+ ArcaLockTimeoutError,
34
35
  ArcaServiceError,
35
36
  ArcaSoapFaultError,
36
37
  ArcaTransportError,
37
38
  createResponseBodyDiagnostic,
38
39
  createSafeErrorDiagnostic,
39
40
  toArcaSafeErrorMetadata
40
- } from "./chunk-NT2A6YMO.mjs";
41
+ } from "./chunk-Q7IURW54.mjs";
41
42
 
42
43
  // src/config.ts
43
44
  var ARCA_ENVIRONMENTS = ["production", "test"];
@@ -2701,7 +2702,7 @@ async function withWsaaSessionStoreLock(config, key, service, fn) {
2701
2702
  if (entered) {
2702
2703
  throw error;
2703
2704
  }
2704
- if (error instanceof ArcaConfigurationError) {
2705
+ if (error instanceof ArcaConfigurationError || error instanceof ArcaLockTimeoutError) {
2705
2706
  throw error;
2706
2707
  }
2707
2708
  throw new ArcaConfigurationError(
@@ -3876,48 +3877,39 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
3876
3877
  return await replay(existing);
3877
3878
  }
3878
3879
  const prepared = withinDateWindow(await prepare(), options);
3879
- const sequence = {
3880
- coordinates: {
3881
- salesPoint: prepared.data.salesPoint,
3882
- voucherType: prepared.data.voucherType
3883
- },
3884
- // The sequence belongs to the taxpayer whose numbering ARCA advances.
3885
- taxId: String(options.representedTaxId ?? context.taxId)
3886
- };
3887
- const sequenceRecord = sequenceKey(
3888
- environment,
3889
- sequence.taxId,
3890
- sequence.coordinates.salesPoint,
3891
- sequence.coordinates.voucherType
3892
- );
3880
+ const sequence = sequenceOf(environment, taxId, {
3881
+ representedTaxId,
3882
+ salesPoint: prepared.data.salesPoint,
3883
+ voucherType: prepared.data.voucherType
3884
+ });
3893
3885
  if (!store.withLock) {
3894
3886
  return await claim();
3895
3887
  }
3896
3888
  return await store.withLock(
3897
- sequenceLockKey(
3898
- environment,
3899
- sequence.taxId,
3900
- sequence.coordinates.salesPoint,
3901
- sequence.coordinates.voucherType
3902
- ),
3889
+ sequence.lock,
3903
3890
  async () => {
3891
+ const prior = await storeCall(() => store.get(key));
3892
+ if (prior !== null) {
3893
+ return await replay(prior, true);
3894
+ }
3904
3895
  const barrier = await runSequenceBarrier({
3905
3896
  store,
3906
3897
  environment,
3907
3898
  taxId,
3908
- sequence: sequenceRecord,
3899
+ sequence: sequence.marker,
3909
3900
  sequenceTaxId: sequence.taxId,
3910
- coordinates: sequence.coordinates,
3911
3901
  select: select ?? (() => wsfe),
3912
3902
  options,
3913
3903
  supersededBy: idempotencyKey,
3914
- readNext: () => nextNumber(wsfe, prepared.data, options)
3904
+ readNext: () => nextNumber(wsfe, prepared.data, { ...options, number: void 0 })
3915
3905
  });
3916
3906
  return "blocked" in barrier ? {
3917
3907
  ...barrier.blocked,
3918
3908
  ...requestEvidence(prepared.data, void 0, options)
3919
3909
  } : await claim(barrier.reserved);
3920
- }
3910
+ },
3911
+ // The deadline also bounds the wait: giving up there reserved nothing.
3912
+ { signal: options.abortSignal }
3921
3913
  );
3922
3914
  function reservation(number) {
3923
3915
  const service = options.service ?? "wsfe";
@@ -3937,10 +3929,6 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
3937
3929
  }
3938
3930
  async function claim(reserved) {
3939
3931
  const coordinated = store.withLock !== void 0;
3940
- const prior = coordinated ? await storeCall(() => store.get(key)) : null;
3941
- if (prior !== null) {
3942
- return await replay(prior, true);
3943
- }
3944
3932
  const number = options.number ?? reserved ?? await nextNumber(wsfe, prepared.data, options);
3945
3933
  const record = reservation(number);
3946
3934
  const claimed = {
@@ -3951,21 +3939,20 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
3951
3939
  claimedAt: (/* @__PURE__ */ new Date()).toISOString()
3952
3940
  };
3953
3941
  if (coordinated) {
3954
- await storeCall(() => store.set(sequenceRecord, JSON.stringify(claimed)));
3942
+ await storeCall(
3943
+ () => store.set(sequence.marker, JSON.stringify(claimed))
3944
+ );
3955
3945
  }
3956
3946
  if (await storeCall(() => store.add(key, JSON.stringify(record)))) {
3957
- const outcome = await settle2(
3958
- runAuthorization(wsfe, prepared, options, issuer, number)
3947
+ return await settle2(
3948
+ record,
3949
+ runAuthorization(wsfe, prepared, options, issuer, number, {
3950
+ strangerAtNumber: true
3951
+ }),
3952
+ // Without the lock, a retry of this key may write the number while
3953
+ // this claim is still in flight: its 10016 is not marked.
3954
+ { written: claimed, everyRejected: true, racing: !coordinated }
3959
3955
  );
3960
- if (coordinated && outcome.kind !== "indeterminate") {
3961
- await storeCall(
3962
- () => store.set(
3963
- sequenceRecord,
3964
- JSON.stringify({ ...claimed, resolvedAt: (/* @__PURE__ */ new Date()).toISOString() })
3965
- )
3966
- );
3967
- }
3968
- return outcome;
3969
3956
  }
3970
3957
  const winner = await storeCall(() => store.get(key));
3971
3958
  if (winner === null) {
@@ -3986,51 +3973,157 @@ async function runOperation(wsfe, operation, input, prepare, options, context, r
3986
3973
  }
3987
3974
  );
3988
3975
  }
3989
- const consult = async () => {
3990
- const recorded = await storeCall(() => store.get(settled));
3991
- if (recorded !== null) {
3992
- return await settledOutcome(
3993
- select ?? (() => wsfe),
3994
- stored,
3995
- readSettledRecord(recorded),
3996
- options,
3997
- taxId,
3998
- (owner, other) => storeCall(() => store.get(settledKey(environment, owner, other)))
3999
- );
3976
+ return await consultKey(
3977
+ { store, environment, taxId, idempotencyKey },
3978
+ stored,
3979
+ locked,
3980
+ select ?? (() => wsfe),
3981
+ options,
3982
+ resend
3983
+ );
3984
+ }
3985
+ async function resend(current) {
3986
+ const rejected = current.rejectedAt !== void 0;
3987
+ if (rejected) {
3988
+ const held = await clearRejection(current);
3989
+ if (held !== void 0) {
3990
+ return held;
4000
3991
  }
4001
- return await settle2(
4002
- runAuthorization(
4003
- wsfe,
4004
- {
4005
- ...preparedFromRecord(stored),
4006
- ...replayAmounts ? { amounts: replayAmounts } : {}
4007
- },
4008
- options,
4009
- issuer,
4010
- stored.number,
4011
- true
4012
- )
4013
- );
4014
- };
4015
- if (locked || !store.withLock) {
4016
- return await consult();
4017
3992
  }
4018
- return await store.withLock(
4019
- sequenceLockKey(
4020
- environment,
4021
- stored.representedTaxId ?? taxId,
4022
- stored.salesPoint,
4023
- stored.voucherType
3993
+ return await settle2(
3994
+ current,
3995
+ runAuthorization(
3996
+ wsfe,
3997
+ {
3998
+ ...preparedFromRecord(current),
3999
+ ...replayAmounts ? { amounts: replayAmounts } : {}
4000
+ },
4001
+ options,
4002
+ issuer,
4003
+ current.number,
4004
+ { lookupFirst: !rejected, strangerAtNumber: rejected }
4024
4005
  ),
4025
- consult
4006
+ { everyRejected: rejected, presumed: rejected, racing: rejected }
4007
+ );
4008
+ }
4009
+ async function settle2(record, running, {
4010
+ written,
4011
+ everyRejected = false,
4012
+ presumed = false,
4013
+ racing = false
4014
+ } = {}) {
4015
+ const uncoordinated = !store.withLock;
4016
+ const outcome = await recordConflict(
4017
+ store,
4018
+ settled,
4019
+ await running,
4020
+ !(presumed && uncoordinated)
4021
+ );
4022
+ const marks = outcome.kind === "rejected" && everyRejected && !(uncoordinated && racing && mayHoldNumber(outcome.issues, record.service));
4023
+ if (marks) {
4024
+ const rejected = { ...record, rejectedAt: (/* @__PURE__ */ new Date()).toISOString() };
4025
+ await storeCall(() => store.set(key, JSON.stringify(rejected)));
4026
+ }
4027
+ if (!store.withLock || outcome.kind === "indeterminate" || outcome.kind === "rejected" && !marks) {
4028
+ return outcome;
4029
+ }
4030
+ const { marker } = sequenceOf(environment, taxId, record);
4031
+ const json = written ? null : await storeCall(() => store.get(marker));
4032
+ const claimed = written ?? (json === null ? null : readSequenceRecord(json));
4033
+ if (claimed !== null && isOwnClaim(claimed) && claimed.number === record.number && claimed.resolvedAt === void 0) {
4034
+ const resolved = { ...claimed, resolvedAt: (/* @__PURE__ */ new Date()).toISOString() };
4035
+ await storeCall(() => store.set(marker, JSON.stringify(resolved)));
4036
+ }
4037
+ return outcome;
4038
+ }
4039
+ async function clearRejection(record) {
4040
+ const checked = await settle2(
4041
+ record,
4042
+ consultReservation(select ?? (() => wsfe), record, options, taxId, true),
4043
+ { presumed: true }
4026
4044
  );
4045
+ if (checked.kind !== "indeterminate" || checked.lookup.kind !== "not_found") {
4046
+ return checked;
4047
+ }
4048
+ const holder = await reclaim(record);
4049
+ if (holder !== void 0) {
4050
+ return withheld(
4051
+ record,
4052
+ { kind: "blocked", by: holder.key, byTaxId: holder.issuerTaxId },
4053
+ options
4054
+ );
4055
+ }
4056
+ const { rejectedAt: _, ...pending } = record;
4057
+ await storeCall(() => store.set(key, JSON.stringify(pending)));
4027
4058
  }
4028
- async function settle2(running) {
4029
- return await recordConflict(store, settled, await running);
4059
+ async function reclaim(record) {
4060
+ if (!store.withLock) {
4061
+ return;
4062
+ }
4063
+ const { marker } = sequenceOf(environment, taxId, record);
4064
+ const json = await storeCall(() => store.get(marker));
4065
+ const claimed = json === null ? null : readSequenceRecord(json);
4066
+ if (claimed !== null && !isOwnClaim(claimed) && claimed.resolvedAt === void 0) {
4067
+ return { key: claimed.key, issuerTaxId: claimed.issuerTaxId ?? taxId };
4068
+ }
4069
+ const reopened = {
4070
+ v: 1,
4071
+ key: idempotencyKey,
4072
+ issuerTaxId: taxId,
4073
+ number: record.number,
4074
+ claimedAt: (/* @__PURE__ */ new Date()).toISOString()
4075
+ };
4076
+ await storeCall(() => store.set(marker, JSON.stringify(reopened)));
4030
4077
  }
4078
+ function isOwnClaim(claimed) {
4079
+ return claimed.key === idempotencyKey && (claimed.issuerTaxId ?? taxId) === taxId;
4080
+ }
4081
+ }
4082
+ function sequenceOf(environment, taxId, {
4083
+ representedTaxId,
4084
+ salesPoint,
4085
+ voucherType
4086
+ }) {
4087
+ const owner = representedTaxId ?? taxId;
4088
+ return {
4089
+ taxId: owner,
4090
+ marker: sequenceKey(environment, owner, salesPoint, voucherType),
4091
+ lock: sequenceLockKey(environment, owner, salesPoint, voucherType)
4092
+ };
4031
4093
  }
4032
- async function recordConflict(store, key, outcome) {
4033
- if (outcome.kind !== "conflict") {
4094
+ async function consultKey({ store, environment, taxId, idempotencyKey }, stored, locked, select, options, unrecorded) {
4095
+ const consult = async (current) => {
4096
+ const recorded = await storeCall(
4097
+ () => store.get(settledKey(environment, taxId, idempotencyKey))
4098
+ );
4099
+ if (recorded === null) {
4100
+ return await unrecorded(current);
4101
+ }
4102
+ return await settledOutcome(
4103
+ select,
4104
+ current,
4105
+ readSettledRecord(recorded),
4106
+ options,
4107
+ taxId,
4108
+ (owner, other) => storeCall(() => store.get(settledKey(environment, owner, other)))
4109
+ );
4110
+ };
4111
+ if (locked || !store.withLock) {
4112
+ return await consult(stored);
4113
+ }
4114
+ return await store.withLock(
4115
+ sequenceOf(environment, taxId, stored).lock,
4116
+ async () => {
4117
+ const json = await storeCall(
4118
+ () => store.get(attemptKey(environment, taxId, idempotencyKey))
4119
+ );
4120
+ return await consult(json === null ? stored : readRecord(json));
4121
+ },
4122
+ { signal: options.abortSignal }
4123
+ );
4124
+ }
4125
+ async function recordConflict(store, key, outcome, durable = true) {
4126
+ if (outcome.kind !== "conflict" || !durable) {
4034
4127
  return outcome;
4035
4128
  }
4036
4129
  const record = {
@@ -4077,26 +4170,33 @@ async function settledOutcome(select, reservation, settled, options, taxId, sett
4077
4170
  if (!empty && (outcome.kind !== "conflict" || await successionDisputed(settled, taxId, settledFor))) {
4078
4171
  return outcome;
4079
4172
  }
4173
+ return withheld(
4174
+ reservation,
4175
+ { kind: "superseded", by: settled.by, byTaxId: settled.byTaxId ?? taxId },
4176
+ options,
4177
+ settled.number
4178
+ );
4179
+ }
4180
+ function withheld(reservation, lookup, options, number = reservation.number) {
4080
4181
  return {
4081
4182
  kind: "indeterminate",
4082
4183
  attempted: {
4083
4184
  salesPoint: reservation.salesPoint,
4084
4185
  voucherType: reservation.voucherType,
4085
- number: settled.number
4186
+ number
4086
4187
  },
4087
4188
  attempt: replayEvidence(reservation.service),
4088
- lookup: {
4089
- kind: "superseded",
4090
- by: settled.by,
4091
- byTaxId: settled.byTaxId ?? taxId
4092
- },
4093
- ...requestEvidence(
4094
- preparedFromRecord(reservation).data,
4095
- reservation.number,
4096
- { ...options, service: reservation.service ?? "wsfe" }
4097
- )
4189
+ lookup,
4190
+ ...reservationEvidence(reservation, options)
4098
4191
  };
4099
4192
  }
4193
+ function reservationEvidence(reservation, options) {
4194
+ return requestEvidence(
4195
+ preparedFromRecord(reservation).data,
4196
+ reservation.number,
4197
+ { ...options, service: reservation.service ?? "wsfe" }
4198
+ );
4199
+ }
4100
4200
  async function successionDisputed(superseded, taxId, settledFor) {
4101
4201
  let owner = superseded.byTaxId ?? taxId;
4102
4202
  let next = superseded.by;
@@ -4125,11 +4225,7 @@ function settledConflict(settled, reservation, options) {
4125
4225
  attempt: replayEvidence(reservation.service),
4126
4226
  found: settled.found,
4127
4227
  reason: "This key already recorded another voucher at the reserved number. Reconcile before issuing under a new key.",
4128
- ...requestEvidence(
4129
- preparedFromRecord(reservation).data,
4130
- reservation.number,
4131
- { ...options, service: reservation.service ?? "wsfe" }
4132
- )
4228
+ ...reservationEvidence(reservation, options)
4133
4229
  };
4134
4230
  }
4135
4231
  function validateKeyStore(options, context) {
@@ -4151,7 +4247,7 @@ function validateKeyStore(options, context) {
4151
4247
  function readRecord(json) {
4152
4248
  try {
4153
4249
  const record = JSON.parse(json);
4154
- if (!record || record.v !== 1 && record.v !== 2 || record.v === 2 && record.service === void 0 || !["issue", "creditNote", "debitNote"].includes(record.operation) || typeof record.inputHash !== "string" || !record.sent || !Number.isSafeInteger(record.number) || record.number < 1 || record.number > 99999999 || record.sent.salesPoint !== record.salesPoint || record.sent.voucherType !== record.voucherType) {
4250
+ if (!record || record.v !== 1 && record.v !== 2 || record.v === 2 && record.service === void 0 || !["issue", "creditNote", "debitNote"].includes(record.operation) || typeof record.inputHash !== "string" || !record.sent || record.rejectedAt !== void 0 && typeof record.rejectedAt !== "string" || !Number.isSafeInteger(record.number) || record.number < 1 || record.number > 99999999 || record.sent.salesPoint !== record.salesPoint || record.sent.voucherType !== record.voucherType) {
4155
4251
  throw new Error("Invalid reservation structure");
4156
4252
  }
4157
4253
  normalizeWsfeVoucherInput(record.sent);
@@ -4201,7 +4297,7 @@ async function nextNumber(wsfe, data, options) {
4201
4297
  }
4202
4298
  return number;
4203
4299
  }
4204
- async function runAuthorization(wsfe, { data, voucherClass, amounts }, options, taxId, reservedNumber, replay = false) {
4300
+ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options, taxId, reservedNumber, { lookupFirst = false, strangerAtNumber = false } = {}) {
4205
4301
  const auth = {
4206
4302
  representedTaxId: options.representedTaxId,
4207
4303
  forceRefresh: options.forceRefresh,
@@ -4231,7 +4327,7 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
4231
4327
  voucher,
4232
4328
  service: options.service
4233
4329
  };
4234
- if (replay) {
4330
+ if (lookupFirst) {
4235
4331
  const attempt2 = replayEvidence(options.service);
4236
4332
  let lookup;
4237
4333
  try {
@@ -4277,7 +4373,7 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
4277
4373
  recovery,
4278
4374
  options,
4279
4375
  includeRawResponse,
4280
- replay
4376
+ strangerAtNumber
4281
4377
  );
4282
4378
  }
4283
4379
  const uncertain = authorization.kind === "indeterminate" ? authorization : {
@@ -4298,9 +4394,9 @@ async function runAuthorization(wsfe, { data, voucherClass, amounts }, options,
4298
4394
  voucher
4299
4395
  });
4300
4396
  }
4301
- async function resolveRejection(authorization, recovery, options, includeRawResponse, replay) {
4397
+ async function resolveRejection(authorization, recovery, options, includeRawResponse, strangerAtNumber) {
4302
4398
  const issues = [...authorization.errors, ...authorization.observations];
4303
- const consult = options.idempotencyKey !== void 0 && (options.service === "wsmtxca" || issues.some((issue) => issue.code === "10016"));
4399
+ const consult = options.idempotencyKey !== void 0 && mayHoldNumber(issues, options.service);
4304
4400
  if (consult) {
4305
4401
  const recovered = await recoverInvoice({
4306
4402
  ...recovery,
@@ -4312,7 +4408,7 @@ async function resolveRejection(authorization, recovery, options, includeRawResp
4312
4408
  },
4313
4409
  includeRawResponse
4314
4410
  ),
4315
- strangerAtNumber: !replay
4411
+ strangerAtNumber
4316
4412
  });
4317
4413
  if (recovered.kind === "authorized" || recovered.kind === "conflict") {
4318
4414
  return recovered;
@@ -4326,6 +4422,9 @@ async function resolveRejection(authorization, recovery, options, includeRawResp
4326
4422
  ...recovery.includedRequest
4327
4423
  };
4328
4424
  }
4425
+ function mayHoldNumber(issues, service = "wsfe") {
4426
+ return service === "wsmtxca" || issues.some((issue) => issue.code === "10016");
4427
+ }
4329
4428
  async function recoverInvoice({
4330
4429
  wsfe,
4331
4430
  auth,
@@ -4376,7 +4475,7 @@ async function recoverInvoice({
4376
4475
  attempted,
4377
4476
  attempt,
4378
4477
  found: { ...toVoucherSummary(lookup.voucher), ...rawResponse },
4379
- reason: "ARCA refused the number this call reserved and another voucher occupies it",
4478
+ reason: "This key never wrote the number it reserved, and another voucher occupies it",
4380
4479
  ...includedRequest
4381
4480
  };
4382
4481
  }
@@ -4816,14 +4915,10 @@ async function recoverOperation(select, key, inputOptions, context) {
4816
4915
  );
4817
4916
  validateOptions(options);
4818
4917
  validateKeyStore({ ...options, idempotencyKey: key }, context);
4918
+ const scope = keyScope(context, key);
4919
+ const { store, environment, taxId } = scope;
4819
4920
  const json = await storeCall(
4820
- () => context.store.get(
4821
- attemptKey(
4822
- context.environment,
4823
- context.taxId,
4824
- key
4825
- )
4826
- )
4921
+ () => store.get(attemptKey(environment, taxId, key))
4827
4922
  );
4828
4923
  if (json === null) {
4829
4924
  throw new ArcaInputError("No reservation exists for this idempotency key", {
@@ -4832,44 +4927,40 @@ async function recoverOperation(select, key, inputOptions, context) {
4832
4927
  });
4833
4928
  }
4834
4929
  const record = readRecord(json);
4835
- const store = context.store;
4836
- const settled = settledKey(
4837
- context.environment,
4838
- context.taxId,
4839
- key
4840
- );
4841
- if (options.representedTaxId !== void 0 && String(options.representedTaxId) !== (record.representedTaxId ?? context?.taxId)) {
4930
+ if (options.representedTaxId !== void 0 && String(options.representedTaxId) !== (record.representedTaxId ?? taxId)) {
4842
4931
  throw new ArcaInputError(
4843
4932
  "Reservation belongs to another represented taxpayer",
4844
4933
  { code: "ARCA_INPUT_IDEMPOTENCY_MISMATCH" }
4845
4934
  );
4846
4935
  }
4847
- const recorded = await storeCall(() => store.get(settled));
4848
- if (recorded !== null) {
4849
- return await settledOutcome(
4850
- select,
4851
- record,
4852
- readSettledRecord(recorded),
4853
- options,
4854
- context.taxId,
4855
- (owner, other) => storeCall(
4856
- () => store.get(
4857
- settledKey(context.environment, owner, other)
4858
- )
4859
- )
4860
- );
4861
- }
4862
- return await recordConflict(
4863
- store,
4864
- settled,
4865
- await consultReservation(
4866
- select,
4867
- record,
4868
- options,
4869
- context.taxId
4870
- )
4936
+ return await consultKey(
4937
+ scope,
4938
+ record,
4939
+ false,
4940
+ select,
4941
+ options,
4942
+ async (current) => {
4943
+ const rejected = current.rejectedAt !== void 0;
4944
+ return await recordConflict(
4945
+ store,
4946
+ settledKey(environment, taxId, key),
4947
+ await consultReservation(select, current, options, taxId, rejected),
4948
+ !rejected || store.withLock !== void 0
4949
+ );
4950
+ }
4871
4951
  );
4872
4952
  }
4953
+ function keyScope(context, idempotencyKey) {
4954
+ if (!context?.store) {
4955
+ throw new ArcaConfigurationError("idempotencyKey requires a store.");
4956
+ }
4957
+ return {
4958
+ store: context.store,
4959
+ environment: context.environment,
4960
+ taxId: context.taxId,
4961
+ idempotencyKey
4962
+ };
4963
+ }
4873
4964
  function consultReservation(select, record, options, taxId, strangerAtNumber = false) {
4874
4965
  const storedOptions = {
4875
4966
  ...options,
@@ -4995,7 +5086,6 @@ async function runSequenceBarrier({
4995
5086
  taxId,
4996
5087
  sequence,
4997
5088
  sequenceTaxId,
4998
- coordinates,
4999
5089
  select,
5000
5090
  options,
5001
5091
  supersededBy,
@@ -5021,13 +5111,15 @@ async function runSequenceBarrier({
5021
5111
  return {};
5022
5112
  }
5023
5113
  const record = readRecord(reservation);
5114
+ if (record.rejectedAt !== void 0) {
5115
+ return {};
5116
+ }
5024
5117
  const blocked = {
5025
- blocked: {
5026
- kind: "indeterminate",
5027
- attempted: { ...coordinates, number: record.number },
5028
- attempt: replayEvidence(options.service),
5029
- lookup: { kind: "blocked", by: claimed.key, byTaxId: owner }
5030
- }
5118
+ blocked: withheld(
5119
+ record,
5120
+ { kind: "blocked", by: claimed.key, byTaxId: owner },
5121
+ options
5122
+ )
5031
5123
  };
5032
5124
  const outcome = await recordConflict(
5033
5125
  store,
@@ -5054,6 +5146,9 @@ async function runSequenceBarrier({
5054
5146
  if (outcome.kind !== "indeterminate" || outcome.lookup.kind !== "not_found") {
5055
5147
  return blocked;
5056
5148
  }
5149
+ if (options.number !== void 0 && options.number !== record.number) {
5150
+ return blocked;
5151
+ }
5057
5152
  const next = await readNext();
5058
5153
  if (next !== record.number) {
5059
5154
  return blocked;
@@ -5344,19 +5439,26 @@ var ARCA_LEASE_MS = 6e4;
5344
5439
  var RENEW_MS = 2e4;
5345
5440
  var POLL_MS = 50;
5346
5441
  var MAX_WAIT_MS = 2 * ARCA_LEASE_MS;
5347
- async function withLease(key, driver, fn) {
5442
+ async function withLease(key, driver, fn, { signal } = {}) {
5348
5443
  const owner = randomUUID();
5349
5444
  const deadline = Date.now() + MAX_WAIT_MS;
5445
+ throwIfAborted(key, signal);
5350
5446
  let held = await driver.acquire(owner);
5351
5447
  while (!held) {
5352
5448
  if (Date.now() >= deadline) {
5353
- throw new ArcaConfigurationError(
5354
- `ARCA store lock ${key} stayed held; no work was attempted.`
5449
+ throw new ArcaLockTimeoutError(
5450
+ `ARCA store lock ${key} stayed held; no work was attempted.`,
5451
+ { reason: "held" }
5355
5452
  );
5356
5453
  }
5357
- await delay2(POLL_MS + Math.floor(Math.random() * POLL_MS));
5454
+ await delay2(POLL_MS + Math.floor(Math.random() * POLL_MS), signal);
5455
+ throwIfAborted(key, signal);
5358
5456
  held = await driver.acquire(owner);
5359
5457
  }
5458
+ if (signal?.aborted) {
5459
+ await driver.release(owner).catch(() => void 0);
5460
+ throwIfAborted(key, signal);
5461
+ }
5360
5462
  const renewal = setInterval(() => {
5361
5463
  driver.renew(owner).catch(() => void 0);
5362
5464
  }, RENEW_MS);
@@ -5368,10 +5470,29 @@ async function withLease(key, driver, fn) {
5368
5470
  await driver.release(owner).catch(() => void 0);
5369
5471
  }
5370
5472
  }
5371
- function delay2(ms) {
5473
+ function throwIfAborted(key, signal) {
5474
+ if (signal?.aborted) {
5475
+ throw new ArcaLockTimeoutError(
5476
+ `Stopped waiting for ARCA store lock ${key}: the caller's signal aborted; no work was attempted.`,
5477
+ { reason: "aborted", cause: signal.reason }
5478
+ );
5479
+ }
5480
+ }
5481
+ function delay2(ms, signal) {
5482
+ if (signal?.aborted) {
5483
+ return Promise.resolve();
5484
+ }
5372
5485
  return new Promise((resolve) => {
5373
- const timer = setTimeout(resolve, ms);
5486
+ const wake = () => {
5487
+ clearTimeout(timer);
5488
+ resolve();
5489
+ };
5490
+ const timer = setTimeout(() => {
5491
+ signal?.removeEventListener("abort", wake);
5492
+ resolve();
5493
+ }, ms);
5374
5494
  timer.unref?.();
5495
+ signal?.addEventListener("abort", wake, { once: true });
5375
5496
  });
5376
5497
  }
5377
5498
 
@@ -5421,10 +5542,10 @@ function createFileStore(directory) {
5421
5542
  }
5422
5543
  }
5423
5544
  }),
5424
- withLock: (key, fn) => (
5545
+ withLock: (key, fn, options) => (
5425
5546
  // A lock directory: mkdir is the atomic claim on every POSIX filesystem
5426
5547
  // and on Windows, and the holder file inside carries the lease.
5427
- withLease(key, fileLease(`${path(key)}.lock`, ensure), fn)
5548
+ withLease(key, fileLease(`${path(key)}.lock`, ensure), fn, options)
5428
5549
  )
5429
5550
  };
5430
5551
  }
@@ -5600,12 +5721,14 @@ export {
5600
5721
  arcaQrPayload,
5601
5722
  voucherDateWindow,
5602
5723
  matchWsfeVoucherIdentity,
5724
+ abortable,
5603
5725
  createMemoryWsaaSessionStore,
5604
5726
  createWsaaAuthModule,
5605
5727
  createWsaaStoreAdapter,
5606
5728
  createArcaClient,
5607
5729
  ARCA_LEASE_MS,
5608
5730
  withLease,
5731
+ throwIfAborted,
5609
5732
  createFileStore
5610
5733
  };
5611
- //# sourceMappingURL=chunk-CYXS3F34.mjs.map
5734
+ //# sourceMappingURL=chunk-WEEV76IC.mjs.map