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

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/dist/index.cjs CHANGED
@@ -11361,12 +11361,19 @@ var SerialChain = class {
11361
11361
  return run;
11362
11362
  }
11363
11363
  };
11364
- function coalesced(run) {
11364
+ function coalesced(run, observe) {
11365
11365
  let inFlight = false;
11366
11366
  let queued = false;
11367
11367
  const start = () => {
11368
11368
  inFlight = true;
11369
- void run().catch(() => void 0).finally(() => {
11369
+ let running;
11370
+ try {
11371
+ running = run();
11372
+ } catch {
11373
+ running = Promise.resolve();
11374
+ }
11375
+ observe?.(running);
11376
+ void running.catch(() => void 0).finally(() => {
11370
11377
  inFlight = false;
11371
11378
  if (!queued) return;
11372
11379
  queued = false;
@@ -13115,13 +13122,26 @@ var Receive = class {
13115
13122
  * the tokens just accepted, and the balance would still only move at the end.
13116
13123
  * Ordering is preserved: store() still precedes the ack (§5.7).
13117
13124
  */
13118
- async maybeRefresh(deps, pending, epochOf) {
13125
+ async maybeRefresh(ctx) {
13126
+ const { deps, pending, pageEpoch } = ctx;
13119
13127
  if (deps.refreshView === void 0) return;
13120
13128
  const now = deps.now?.() ?? Date.now();
13121
13129
  if (now - this.lastRefreshAt < REFRESH_INTERVAL_MS) return;
13122
13130
  this.lastRefreshAt = now;
13123
- await flushAcks(deps, pending, epochOf);
13124
- deps.refreshView();
13131
+ if ((await flushAcks(deps, pending, pageEpoch)).progressed) deps.refreshView();
13132
+ }
13133
+ /** One listing pass: process each entry, flushing and refreshing as it goes. */
13134
+ async drainPages(ctx, basis) {
13135
+ const { deps, pending, pageEpoch, claimable } = ctx;
13136
+ for await (const entry of deps.delivery.incoming(basis === null ? void 0 : String(basis.cursor))) {
13137
+ const claimableBefore = claimable.n;
13138
+ await this.processEntry(ctx, entry);
13139
+ if (pending.length >= ACK_BATCH_SIZE) {
13140
+ if (!(await flushAcks(deps, pending, pageEpoch)).progressed) return;
13141
+ }
13142
+ if (claimable.n > claimableBefore) await this.maybeRefresh(ctx);
13143
+ }
13144
+ await flushAcks(deps, pending, pageEpoch);
13125
13145
  }
13126
13146
  async doDrain() {
13127
13147
  const deps = this.deps;
@@ -13130,17 +13150,16 @@ var Receive = class {
13130
13150
  const stored = [];
13131
13151
  const pending = [];
13132
13152
  const pageEpoch = () => deps.delivery.incomingEpoch() ?? deps.syncEpoch();
13153
+ const claimable = { n: 0 };
13154
+ const finish = () => {
13155
+ if (claimable.n > 0) deps.refreshView?.();
13156
+ return stored;
13157
+ };
13133
13158
  try {
13134
13159
  const record = await deps.kv.get(STORE_KEYS.streamCursor("mailbox"));
13135
13160
  let basis = record !== null && record.syncEpoch === deps.syncEpoch() ? record : null;
13136
13161
  for (let pass = 0; pass < 2; pass++) {
13137
- for await (const entry of deps.delivery.incoming(basis === null ? void 0 : String(basis.cursor))) {
13138
- const storedBefore = stored.length;
13139
- await this.processEntry(deps, engine, entry, pending, stored);
13140
- if (pending.length >= ACK_BATCH_SIZE) await flushAcks(deps, pending, pageEpoch);
13141
- if (stored.length > storedBefore) await this.maybeRefresh(deps, pending, pageEpoch);
13142
- }
13143
- await flushAcks(deps, pending, pageEpoch);
13162
+ await this.drainPages({ deps, engine, pending, stored, pageEpoch, claimable }, basis);
13144
13163
  const served = deps.delivery.incomingEpoch();
13145
13164
  if (basis === null || served === null || served === basis.syncEpoch) break;
13146
13165
  await deps.kv.remove(STORE_KEYS.streamCursor("mailbox"));
@@ -13148,21 +13167,25 @@ var Receive = class {
13148
13167
  }
13149
13168
  } catch (err) {
13150
13169
  logger.warn("PaymentsV2", "receive drain interrupted \u2014 unacked entries retry next drain:", err);
13151
- if (isRetryableAckError(err)) return stored;
13170
+ if (isRetryableAckError(err)) return finish();
13152
13171
  await flushAcks(deps, pending, pageEpoch).catch((flushErr) => {
13153
13172
  logger.warn("PaymentsV2", "receive ack flush failed \u2014 cursor holds at the acked prefix:", flushErr);
13173
+ return { progressed: false };
13154
13174
  });
13155
13175
  }
13156
- return stored;
13176
+ return finish();
13157
13177
  }
13158
- async processEntry(deps, engine, entry, pending, stored) {
13178
+ async processEntry(ctx, entry) {
13179
+ const { deps, engine, pending, stored, claimable } = ctx;
13159
13180
  const screened = await screen(deps, engine, entry);
13160
13181
  if (screened.kind === "ack") {
13161
13182
  pending.push(screened.ack);
13183
+ if (screened.ack.disposition === "claimed") claimable.n += 1;
13162
13184
  return;
13163
13185
  }
13164
13186
  await deps.view.store(screened.record);
13165
13187
  pending.push(claimAck(entry));
13188
+ claimable.n += 1;
13166
13189
  const transfer = await announce(deps, entry, screened.record);
13167
13190
  stored.push(transfer);
13168
13191
  deps.emit("transfer:incoming", transfer);
@@ -13223,7 +13246,8 @@ async function announce(deps, entry, record) {
13223
13246
  };
13224
13247
  }
13225
13248
  async function flushAcks(deps, pending, epochOf) {
13226
- if (pending.length === 0) return;
13249
+ if (pending.length === 0) return { progressed: true };
13250
+ const before = pending.length;
13227
13251
  let lastAcked = null;
13228
13252
  try {
13229
13253
  const settled = await settleAcks(deps, pending);
@@ -13241,6 +13265,7 @@ async function flushAcks(deps, pending, epochOf) {
13241
13265
  await deps.kv.set(STORE_KEYS.streamCursor("mailbox"), record);
13242
13266
  }
13243
13267
  }
13268
+ return { progressed: pending.length < before };
13244
13269
  }
13245
13270
  async function settleAcks(deps, pending) {
13246
13271
  const batch = deps.delivery.ackBatch?.bind(deps.delivery);
@@ -14083,7 +14108,7 @@ function composeFacadeParts(deps, hooks) {
14083
14108
  const machineDeps = buildMachineDeps(deps, hooks, historyStore);
14084
14109
  const machineStores = createMachineStores(deps.kv);
14085
14110
  const heldStates = /* @__PURE__ */ new Map();
14086
- const refreshView = coalesced(() => view.delta());
14111
+ const refreshView = coalesced(() => view.delta(), hooks.track);
14087
14112
  return {
14088
14113
  ownPubkeyBytes,
14089
14114
  view,
@@ -14226,7 +14251,8 @@ var PaymentsFacade = class {
14226
14251
  const parts = composeFacadeParts(deps, {
14227
14252
  engine: () => this.engine(),
14228
14253
  send: (request) => this.send(request),
14229
- isActiveOp: (id) => this.activeMoneyOps.has(id)
14254
+ isActiveOp: (id) => this.activeMoneyOps.has(id),
14255
+ track: (op) => this.trackTail(op)
14230
14256
  });
14231
14257
  this.view = parts.view;
14232
14258
  this.wakeRefresh = parts.refreshView;
@@ -14366,7 +14392,6 @@ var PaymentsFacade = class {
14366
14392
  }
14367
14393
  async receive() {
14368
14394
  const transfers = await this.track(this.receiveLoop.drainOnce());
14369
- if (transfers.length > 0) this.trackTail(this.view.delta());
14370
14395
  return { transfers };
14371
14396
  }
14372
14397
  mint(coinId, amount) {
@@ -15761,6 +15786,9 @@ var WalletApiDeliveryPort = class {
15761
15786
  await this.settleClaims(acks, settled, outcomes);
15762
15787
  await this.settleRejects(acks, settled);
15763
15788
  } catch (err) {
15789
+ await this.addSeenAll(settled).catch((seenErr) => {
15790
+ logger.warn("WalletApiDelivery", `seen-set write failed for ${String(settled.length)} committed acks:`, seenErr);
15791
+ });
15764
15792
  throw asRetryable(err);
15765
15793
  }
15766
15794
  await this.addSeenAll(settled);