@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.
@@ -356,6 +356,11 @@ type AckOutcome = {
356
356
  readonly deliveryId: string;
357
357
  readonly status: 'conflict';
358
358
  };
359
+ /** ackBatch failed in a way a per-entry retry would hit too — do not fall back. */
360
+ interface RetryableAckError extends Error {
361
+ readonly retryable: true;
362
+ }
363
+ declare function isRetryableAckError(err: unknown): err is RetryableAckError;
359
364
 
360
365
  interface IntentPayload {
361
366
  v: 2;
@@ -1174,4 +1179,4 @@ declare class PaymentsFacade implements PaymentsV2 {
1174
1179
  declare const HEARTBEAT_SEED_MS = 5000;
1175
1180
  declare const HEARTBEAT_CAP_MS = 120000;
1176
1181
 
1177
- export { ATTENTION_MINT_UNRESOLVED, ATTENTION_RECIPIENT_NETWORK_UNVERIFIED, ATTENTION_RESEED_REJECTED, type ApplyDeltaResult, type CheckpointReseeder, type ConnectionStatus, type DeliverOptions, type DeliveryJournalEntry, type DeliveryPort, type DeliveryReceipt, type DeterministicMintCapable, type FacadeClient, type FacadeSession, HEARTBEAT_CAP_MS, HEARTBEAT_SEED_MS, type HistoryEntry, type HistoryPage, type IncomingDelivery, type IntentBackstopEntry, type IntentPayload, type InventoryAsset, type InventoryItem, type InventoryPage, MAX_RESELECT, type MintJournalEntry, type MintResult, type OpOutcome, type OutcomeClass, type PaymentRequestStatus, type PaymentRequestView, PaymentsFacade, type PaymentsFacadeDeps, type PaymentsRequestsApi, type PaymentsV2, type PaymentsV2Events, type PendingTransfer, type PlannedOp, type RecipientInfo, STORE_KEYS, type ScopedKV, type SendRequest, type SettlingLink, type ShortfallEntry, type StoragePort, type StreamCursor, type StreamName, createScopedKV, supportsDeterministicMint };
1182
+ export { ATTENTION_MINT_UNRESOLVED, ATTENTION_RECIPIENT_NETWORK_UNVERIFIED, ATTENTION_RESEED_REJECTED, type AckOutcome, type AckRequest, type ApplyDeltaResult, type CheckpointReseeder, type ConnectionStatus, type DeliverOptions, type DeliveryJournalEntry, type DeliveryPort, type DeliveryReceipt, type DeterministicMintCapable, type FacadeClient, type FacadeSession, HEARTBEAT_CAP_MS, HEARTBEAT_SEED_MS, type HistoryEntry, type HistoryPage, type IncomingDelivery, type IntentBackstopEntry, type IntentPayload, type InventoryAsset, type InventoryItem, type InventoryPage, MAX_RESELECT, type MintJournalEntry, type MintResult, type OpOutcome, type OutcomeClass, type PaymentRequestStatus, type PaymentRequestView, PaymentsFacade, type PaymentsFacadeDeps, type PaymentsRequestsApi, type PaymentsV2, type PaymentsV2Events, type PendingTransfer, type PlannedOp, type RecipientInfo, type RetryableAckError, STORE_KEYS, type ScopedKV, type SendRequest, type SettlingLink, type ShortfallEntry, type StoragePort, type StreamCursor, type StreamName, createScopedKV, isRetryableAckError, supportsDeterministicMint };
@@ -356,6 +356,11 @@ type AckOutcome = {
356
356
  readonly deliveryId: string;
357
357
  readonly status: 'conflict';
358
358
  };
359
+ /** ackBatch failed in a way a per-entry retry would hit too — do not fall back. */
360
+ interface RetryableAckError extends Error {
361
+ readonly retryable: true;
362
+ }
363
+ declare function isRetryableAckError(err: unknown): err is RetryableAckError;
359
364
 
360
365
  interface IntentPayload {
361
366
  v: 2;
@@ -1174,4 +1179,4 @@ declare class PaymentsFacade implements PaymentsV2 {
1174
1179
  declare const HEARTBEAT_SEED_MS = 5000;
1175
1180
  declare const HEARTBEAT_CAP_MS = 120000;
1176
1181
 
1177
- export { ATTENTION_MINT_UNRESOLVED, ATTENTION_RECIPIENT_NETWORK_UNVERIFIED, ATTENTION_RESEED_REJECTED, type ApplyDeltaResult, type CheckpointReseeder, type ConnectionStatus, type DeliverOptions, type DeliveryJournalEntry, type DeliveryPort, type DeliveryReceipt, type DeterministicMintCapable, type FacadeClient, type FacadeSession, HEARTBEAT_CAP_MS, HEARTBEAT_SEED_MS, type HistoryEntry, type HistoryPage, type IncomingDelivery, type IntentBackstopEntry, type IntentPayload, type InventoryAsset, type InventoryItem, type InventoryPage, MAX_RESELECT, type MintJournalEntry, type MintResult, type OpOutcome, type OutcomeClass, type PaymentRequestStatus, type PaymentRequestView, PaymentsFacade, type PaymentsFacadeDeps, type PaymentsRequestsApi, type PaymentsV2, type PaymentsV2Events, type PendingTransfer, type PlannedOp, type RecipientInfo, STORE_KEYS, type ScopedKV, type SendRequest, type SettlingLink, type ShortfallEntry, type StoragePort, type StreamCursor, type StreamName, createScopedKV, supportsDeterministicMint };
1182
+ export { ATTENTION_MINT_UNRESOLVED, ATTENTION_RECIPIENT_NETWORK_UNVERIFIED, ATTENTION_RESEED_REJECTED, type AckOutcome, type AckRequest, type ApplyDeltaResult, type CheckpointReseeder, type ConnectionStatus, type DeliverOptions, type DeliveryJournalEntry, type DeliveryPort, type DeliveryReceipt, type DeterministicMintCapable, type FacadeClient, type FacadeSession, HEARTBEAT_CAP_MS, HEARTBEAT_SEED_MS, type HistoryEntry, type HistoryPage, type IncomingDelivery, type IntentBackstopEntry, type IntentPayload, type InventoryAsset, type InventoryItem, type InventoryPage, MAX_RESELECT, type MintJournalEntry, type MintResult, type OpOutcome, type OutcomeClass, type PaymentRequestStatus, type PaymentRequestView, PaymentsFacade, type PaymentsFacadeDeps, type PaymentsRequestsApi, type PaymentsV2, type PaymentsV2Events, type PendingTransfer, type PlannedOp, type RecipientInfo, type RetryableAckError, STORE_KEYS, type ScopedKV, type SendRequest, type SettlingLink, type ShortfallEntry, type StoragePort, type StreamCursor, type StreamName, createScopedKV, isRetryableAckError, supportsDeterministicMint };
@@ -1,3 +1,8 @@
1
+ // modules/payments-v2/ports.ts
2
+ function isRetryableAckError(err) {
3
+ return err !== null && typeof err === "object" && err.retryable === true;
4
+ }
5
+
1
6
  // modules/payments-v2/stores.ts
2
7
  function createScopedKV(storage, network, chainPubkey) {
3
8
  const prefix = `pv2:${network}:${chainPubkey}:`;
@@ -536,12 +541,19 @@ var SerialChain = class {
536
541
  return run;
537
542
  }
538
543
  };
539
- function coalesced(run) {
544
+ function coalesced(run, observe) {
540
545
  let inFlight = false;
541
546
  let queued = false;
542
547
  const start = () => {
543
548
  inFlight = true;
544
- void run().catch(() => void 0).finally(() => {
549
+ let running;
550
+ try {
551
+ running = run();
552
+ } catch {
553
+ running = Promise.resolve();
554
+ }
555
+ observe?.(running);
556
+ void running.catch(() => void 0).finally(() => {
545
557
  inFlight = false;
546
558
  if (!queued) return;
547
559
  queued = false;
@@ -3187,11 +3199,6 @@ var logger = {
3187
3199
  }
3188
3200
  };
3189
3201
 
3190
- // modules/payments-v2/ports.ts
3191
- function isRetryableAckError(err) {
3192
- return err !== null && typeof err === "object" && err.retryable === true;
3193
- }
3194
-
3195
3202
  // modules/payments-v2/receive/Receive.ts
3196
3203
  var ACK_BATCH_SIZE = 200;
3197
3204
  var REFRESH_INTERVAL_MS = 2500;
@@ -3240,13 +3247,26 @@ var Receive = class {
3240
3247
  * the tokens just accepted, and the balance would still only move at the end.
3241
3248
  * Ordering is preserved: store() still precedes the ack (§5.7).
3242
3249
  */
3243
- async maybeRefresh(deps, pending, epochOf) {
3250
+ async maybeRefresh(ctx) {
3251
+ const { deps, pending, pageEpoch } = ctx;
3244
3252
  if (deps.refreshView === void 0) return;
3245
3253
  const now = deps.now?.() ?? Date.now();
3246
3254
  if (now - this.lastRefreshAt < REFRESH_INTERVAL_MS) return;
3247
3255
  this.lastRefreshAt = now;
3248
- await flushAcks(deps, pending, epochOf);
3249
- deps.refreshView();
3256
+ if ((await flushAcks(deps, pending, pageEpoch)).progressed) deps.refreshView();
3257
+ }
3258
+ /** One listing pass: process each entry, flushing and refreshing as it goes. */
3259
+ async drainPages(ctx, basis) {
3260
+ const { deps, pending, pageEpoch, claimable } = ctx;
3261
+ for await (const entry of deps.delivery.incoming(basis === null ? void 0 : String(basis.cursor))) {
3262
+ const claimableBefore = claimable.n;
3263
+ await this.processEntry(ctx, entry);
3264
+ if (pending.length >= ACK_BATCH_SIZE) {
3265
+ if (!(await flushAcks(deps, pending, pageEpoch)).progressed) return;
3266
+ }
3267
+ if (claimable.n > claimableBefore) await this.maybeRefresh(ctx);
3268
+ }
3269
+ await flushAcks(deps, pending, pageEpoch);
3250
3270
  }
3251
3271
  async doDrain() {
3252
3272
  const deps = this.deps;
@@ -3255,17 +3275,16 @@ var Receive = class {
3255
3275
  const stored = [];
3256
3276
  const pending = [];
3257
3277
  const pageEpoch = () => deps.delivery.incomingEpoch() ?? deps.syncEpoch();
3278
+ const claimable = { n: 0 };
3279
+ const finish = () => {
3280
+ if (claimable.n > 0) deps.refreshView?.();
3281
+ return stored;
3282
+ };
3258
3283
  try {
3259
3284
  const record = await deps.kv.get(STORE_KEYS.streamCursor("mailbox"));
3260
3285
  let basis = record !== null && record.syncEpoch === deps.syncEpoch() ? record : null;
3261
3286
  for (let pass = 0; pass < 2; pass++) {
3262
- for await (const entry of deps.delivery.incoming(basis === null ? void 0 : String(basis.cursor))) {
3263
- const storedBefore = stored.length;
3264
- await this.processEntry(deps, engine, entry, pending, stored);
3265
- if (pending.length >= ACK_BATCH_SIZE) await flushAcks(deps, pending, pageEpoch);
3266
- if (stored.length > storedBefore) await this.maybeRefresh(deps, pending, pageEpoch);
3267
- }
3268
- await flushAcks(deps, pending, pageEpoch);
3287
+ await this.drainPages({ deps, engine, pending, stored, pageEpoch, claimable }, basis);
3269
3288
  const served = deps.delivery.incomingEpoch();
3270
3289
  if (basis === null || served === null || served === basis.syncEpoch) break;
3271
3290
  await deps.kv.remove(STORE_KEYS.streamCursor("mailbox"));
@@ -3273,21 +3292,25 @@ var Receive = class {
3273
3292
  }
3274
3293
  } catch (err) {
3275
3294
  logger.warn("PaymentsV2", "receive drain interrupted \u2014 unacked entries retry next drain:", err);
3276
- if (isRetryableAckError(err)) return stored;
3295
+ if (isRetryableAckError(err)) return finish();
3277
3296
  await flushAcks(deps, pending, pageEpoch).catch((flushErr) => {
3278
3297
  logger.warn("PaymentsV2", "receive ack flush failed \u2014 cursor holds at the acked prefix:", flushErr);
3298
+ return { progressed: false };
3279
3299
  });
3280
3300
  }
3281
- return stored;
3301
+ return finish();
3282
3302
  }
3283
- async processEntry(deps, engine, entry, pending, stored) {
3303
+ async processEntry(ctx, entry) {
3304
+ const { deps, engine, pending, stored, claimable } = ctx;
3284
3305
  const screened = await screen(deps, engine, entry);
3285
3306
  if (screened.kind === "ack") {
3286
3307
  pending.push(screened.ack);
3308
+ if (screened.ack.disposition === "claimed") claimable.n += 1;
3287
3309
  return;
3288
3310
  }
3289
3311
  await deps.view.store(screened.record);
3290
3312
  pending.push(claimAck(entry));
3313
+ claimable.n += 1;
3291
3314
  const transfer = await announce(deps, entry, screened.record);
3292
3315
  stored.push(transfer);
3293
3316
  deps.emit("transfer:incoming", transfer);
@@ -3348,7 +3371,8 @@ async function announce(deps, entry, record) {
3348
3371
  };
3349
3372
  }
3350
3373
  async function flushAcks(deps, pending, epochOf) {
3351
- if (pending.length === 0) return;
3374
+ if (pending.length === 0) return { progressed: true };
3375
+ const before = pending.length;
3352
3376
  let lastAcked = null;
3353
3377
  try {
3354
3378
  const settled = await settleAcks(deps, pending);
@@ -3366,6 +3390,7 @@ async function flushAcks(deps, pending, epochOf) {
3366
3390
  await deps.kv.set(STORE_KEYS.streamCursor("mailbox"), record);
3367
3391
  }
3368
3392
  }
3393
+ return { progressed: pending.length < before };
3369
3394
  }
3370
3395
  async function settleAcks(deps, pending) {
3371
3396
  const batch = deps.delivery.ackBatch?.bind(deps.delivery);
@@ -4208,7 +4233,7 @@ function composeFacadeParts(deps, hooks) {
4208
4233
  const machineDeps = buildMachineDeps(deps, hooks, historyStore);
4209
4234
  const machineStores = createMachineStores(deps.kv);
4210
4235
  const heldStates = /* @__PURE__ */ new Map();
4211
- const refreshView = coalesced(() => view.delta());
4236
+ const refreshView = coalesced(() => view.delta(), hooks.track);
4212
4237
  return {
4213
4238
  ownPubkeyBytes,
4214
4239
  view,
@@ -4351,7 +4376,8 @@ var PaymentsFacade = class {
4351
4376
  const parts = composeFacadeParts(deps, {
4352
4377
  engine: () => this.engine(),
4353
4378
  send: (request) => this.send(request),
4354
- isActiveOp: (id) => this.activeMoneyOps.has(id)
4379
+ isActiveOp: (id) => this.activeMoneyOps.has(id),
4380
+ track: (op) => this.trackTail(op)
4355
4381
  });
4356
4382
  this.view = parts.view;
4357
4383
  this.wakeRefresh = parts.refreshView;
@@ -4491,7 +4517,6 @@ var PaymentsFacade = class {
4491
4517
  }
4492
4518
  async receive() {
4493
4519
  const transfers = await this.track(this.receiveLoop.drainOnce());
4494
- if (transfers.length > 0) this.trackTail(this.view.delta());
4495
4520
  return { transfers };
4496
4521
  }
4497
4522
  mint(coinId, amount) {
@@ -4968,6 +4993,7 @@ export {
4968
4993
  PaymentsFacade,
4969
4994
  STORE_KEYS,
4970
4995
  createScopedKV,
4996
+ isRetryableAckError,
4971
4997
  supportsDeterministicMint
4972
4998
  };
4973
4999
  /*! Bundled license information: