@unicitylabs/sphere-sdk 0.14.11-dev.6 → 0.14.11-dev.7

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.
@@ -60,6 +60,10 @@ var WalletApiHttpError = class extends Error {
60
60
  if (retryAfter !== void 0) this.retryAfter = retryAfter;
61
61
  }
62
62
  };
63
+ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([0, 408, 425, 429, 500, 502, 503, 504]);
64
+ function isRetryableStatus(err) {
65
+ return err instanceof WalletApiHttpError && RETRYABLE_STATUSES.has(err.status);
66
+ }
63
67
  function isWalletApiHttpError(err, status) {
64
68
  return err instanceof WalletApiHttpError && (status === void 0 || err.status === status);
65
69
  }
@@ -4097,6 +4101,23 @@ function computeDeliveryId(tokenIdHex, stateHashHex) {
4097
4101
  return bytesToHex3(sha256(joined));
4098
4102
  }
4099
4103
  var DELIVERY_SEEN_KEY = "delivery:seen";
4104
+ var ACK_CHUNK = 50;
4105
+ function chunked(items, size) {
4106
+ const out = [];
4107
+ for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
4108
+ return out;
4109
+ }
4110
+ var RetryableAckFailure = class extends Error {
4111
+ retryable = true;
4112
+ constructor(cause) {
4113
+ super(`mailbox ack batch could not be attempted: ${String(cause)}`);
4114
+ this.name = "RetryableAckFailure";
4115
+ this.cause = cause;
4116
+ }
4117
+ };
4118
+ function asRetryable(err) {
4119
+ return isRetryableStatus(err) ? new RetryableAckFailure(err) : err;
4120
+ }
4100
4121
  var WalletApiDeliveryPort = class {
4101
4122
  custody;
4102
4123
  client;
@@ -4262,7 +4283,53 @@ var WalletApiDeliveryPort = class {
4262
4283
  } else {
4263
4284
  await this.client.reject([deliveryId], reason);
4264
4285
  }
4265
- await this.addSeen(deliveryId);
4286
+ await this.addSeenAll([deliveryId]);
4287
+ }
4288
+ /**
4289
+ * §5.7/#757 batched settle. Claims go out grouped, rejects grouped by reason
4290
+ * (the endpoint takes one reason per call), and the seen-set is written ONCE
4291
+ * after every HTTP call has succeeded — never on a throw, so an aborted batch
4292
+ * re-lists rather than being silently forgotten.
4293
+ */
4294
+ async ackBatch(acks) {
4295
+ const settled = [];
4296
+ const outcomes = [];
4297
+ try {
4298
+ await this.settleClaims(acks, settled, outcomes);
4299
+ await this.settleRejects(acks, settled);
4300
+ } catch (err) {
4301
+ throw asRetryable(err);
4302
+ }
4303
+ await this.addSeenAll(settled);
4304
+ return [...outcomes, ...settled.map((deliveryId) => ({ deliveryId, status: "settled" }))];
4305
+ }
4306
+ async settleClaims(acks, settled, outcomes) {
4307
+ const ids = acks.filter((a) => a.disposition === "claimed").map((a) => a.deliveryId);
4308
+ for (const chunk of chunked(ids, ACK_CHUNK)) {
4309
+ const result = await this.client.claim(chunk, this.custody === "inventory");
4310
+ settled.push(...result.claimed);
4311
+ settled.push(...result.alreadyClaimed.map((e) => e.entryId));
4312
+ for (const f of result.failed) {
4313
+ if (f.code === "CONFLICT") outcomes.push({ deliveryId: f.entryId, status: "conflict" });
4314
+ else throw new MailboxClaimFailedError(f.entryId, f.code);
4315
+ }
4316
+ }
4317
+ }
4318
+ async settleRejects(acks, settled) {
4319
+ const byReason = /* @__PURE__ */ new Map();
4320
+ for (const a of acks) {
4321
+ if (a.disposition !== "rejected") continue;
4322
+ const key = a.reason ?? "";
4323
+ const group = byReason.get(key) ?? [];
4324
+ group.push(a.deliveryId);
4325
+ byReason.set(key, group);
4326
+ }
4327
+ for (const [reason, group] of byReason) {
4328
+ for (const chunk of chunked(group, ACK_CHUNK)) {
4329
+ await this.client.reject(chunk, reason === "" ? void 0 : reason);
4330
+ settled.push(...chunk);
4331
+ }
4332
+ }
4266
4333
  }
4267
4334
  onWake(cb) {
4268
4335
  if (this.wake === null) return () => void 0;
@@ -4273,10 +4340,12 @@ var WalletApiDeliveryPort = class {
4273
4340
  const raw = await this.kv.get(DELIVERY_SEEN_KEY);
4274
4341
  return new Set(raw ?? []);
4275
4342
  }
4276
- addSeen(deliveryId) {
4343
+ /** SerialChain settles-not-fulfills: a rejected write never bricks later acks. */
4344
+ addSeenAll(deliveryIds) {
4345
+ if (deliveryIds.length === 0) return Promise.resolve();
4277
4346
  return this.seenChain.enqueue(async () => {
4278
4347
  const seen = await this.readSeen();
4279
- seen.add(deliveryId);
4348
+ for (const id of deliveryIds) seen.add(id);
4280
4349
  await this.kv.set(DELIVERY_SEEN_KEY, [...seen]);
4281
4350
  });
4282
4351
  }