@unicitylabs/sphere-sdk 0.14.0-dev.5 → 0.14.0-dev.6

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.
@@ -31,6 +31,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var payments_v2_exports = {};
32
32
  __export(payments_v2_exports, {
33
33
  ATTENTION_MINT_UNRESOLVED: () => ATTENTION_MINT_UNRESOLVED,
34
+ HEARTBEAT_CAP_MS: () => HEARTBEAT_CAP_MS,
35
+ HEARTBEAT_SEED_MS: () => HEARTBEAT_SEED_MS,
34
36
  MAX_RESELECT: () => MAX_RESELECT,
35
37
  PaymentsFacade: () => PaymentsFacade,
36
38
  STORE_KEYS: () => STORE_KEYS,
@@ -590,6 +592,10 @@ function isValidationReject(err) {
590
592
  if (e === null) return false;
591
593
  return e.status === 422 || e.statusCode === 422 || e.code === "VALIDATION" || e.code === "VALIDATION_FAILED";
592
594
  }
595
+ function retryAfterMsOf(err) {
596
+ const retryAfter = err !== null && typeof err === "object" ? err.retryAfter : void 0;
597
+ return typeof retryAfter === "number" && Number.isFinite(retryAfter) && retryAfter >= 0 ? retryAfter : null;
598
+ }
593
599
  var chainRegistry = /* @__PURE__ */ new WeakMap();
594
600
  var replayRegistry = /* @__PURE__ */ new WeakMap();
595
601
  function registryFor(registry, kv, storeKey, make) {
@@ -1266,6 +1272,217 @@ function validatePayload(raw) {
1266
1272
  return p;
1267
1273
  }
1268
1274
 
1275
+ // modules/payments-v2/convergence.ts
1276
+ var HEARTBEAT_SEED_MS = 5e3;
1277
+ var HEARTBEAT_CAP_MS = 12e4;
1278
+ var ConvergenceHeartbeat = class {
1279
+ constructor(deps) {
1280
+ this.deps = deps;
1281
+ }
1282
+ timer = null;
1283
+ delayMs = HEARTBEAT_SEED_MS;
1284
+ notBefore = 0;
1285
+ running = false;
1286
+ start() {
1287
+ this.running = true;
1288
+ this.delayMs = HEARTBEAT_SEED_MS;
1289
+ this.notBefore = 0;
1290
+ }
1291
+ /** §7 quiescence unchanged: a mid-backoff stop() cancels cleanly. */
1292
+ stop() {
1293
+ this.running = false;
1294
+ this.clearTimer();
1295
+ }
1296
+ /** Reset to seed on progress, ×2 to the cap otherwise; idle stops rescheduling. */
1297
+ settle(outcome, pending) {
1298
+ if (outcome.retryAfterMs !== null) {
1299
+ this.notBefore = Math.max(this.notBefore, this.deps.now() + outcome.retryAfterMs);
1300
+ }
1301
+ if (!pending) {
1302
+ this.delayMs = HEARTBEAT_SEED_MS;
1303
+ return;
1304
+ }
1305
+ const delay = outcome.progress ? HEARTBEAT_SEED_MS : this.delayMs;
1306
+ this.delayMs = Math.min(delay * 2, HEARTBEAT_CAP_MS);
1307
+ this.schedule(delay);
1308
+ }
1309
+ /** New pending work (keep-open park / journaled delivery / mint hold): prompt retry. */
1310
+ arm() {
1311
+ if (!this.running) return;
1312
+ this.delayMs = Math.min(HEARTBEAT_SEED_MS * 2, HEARTBEAT_CAP_MS);
1313
+ this.schedule(HEARTBEAT_SEED_MS);
1314
+ }
1315
+ /** connection:status recovery: the backoff resets; a pending tick re-fires at the seed. */
1316
+ recovered() {
1317
+ this.delayMs = HEARTBEAT_SEED_MS;
1318
+ if (this.timer !== null) this.arm();
1319
+ }
1320
+ schedule(delayMs) {
1321
+ this.clearTimer();
1322
+ if (!this.running) return;
1323
+ const floored = Math.max(delayMs, this.notBefore - this.deps.now());
1324
+ this.timer = setTimeout(() => {
1325
+ this.timer = null;
1326
+ this.deps.onTick();
1327
+ }, floored);
1328
+ }
1329
+ clearTimer() {
1330
+ if (this.timer !== null) clearTimeout(this.timer);
1331
+ this.timer = null;
1332
+ }
1333
+ };
1334
+ var Converger = class {
1335
+ constructor(deps) {
1336
+ this.deps = deps;
1337
+ }
1338
+ /** Resume open intents, then drain the delivery + mint journals (all idempotent). */
1339
+ async convergeOnce() {
1340
+ let progress = false;
1341
+ let retryAfterMs = null;
1342
+ try {
1343
+ progress = await this.resumePass();
1344
+ } catch (err) {
1345
+ retryAfterMs = retryAfterMsOf(err);
1346
+ }
1347
+ try {
1348
+ const stats = await this.deps.stores.deliveryJournal.replay(this.replayDeps());
1349
+ progress = progress || stats.delivered > 0;
1350
+ } catch (err) {
1351
+ retryAfterMs ??= retryAfterMsOf(err);
1352
+ }
1353
+ try {
1354
+ progress = await this.deps.replayMints() > 0 || progress;
1355
+ } catch (err) {
1356
+ retryAfterMs ??= retryAfterMsOf(err);
1357
+ }
1358
+ return { progress, retryAfterMs };
1359
+ }
1360
+ /** §4 heartbeat predicate — read fresh from the §6 stores, never cached. */
1361
+ async pendingWork() {
1362
+ const [backstop, journal, mints] = await Promise.all([
1363
+ this.deps.stores.backstop.list(),
1364
+ this.deps.stores.deliveryJournal.list(),
1365
+ this.deps.stores.mintJournal.list()
1366
+ ]);
1367
+ return backstop.length > 0 || journal.some((e) => e.undeliverable !== true) || mints.length > 0;
1368
+ }
1369
+ async resumePass() {
1370
+ const report = await resumeAll(this.filteredMachineDeps());
1371
+ const progress = report.resumed.length > 0 || report.conflicted.length > 0;
1372
+ await this.deps.reconcile({
1373
+ resumed: report.resumed,
1374
+ conflicted: report.conflicted,
1375
+ open: report.failed
1376
+ });
1377
+ if (progress) await this.deps.refreshView().catch(() => void 0);
1378
+ return progress;
1379
+ }
1380
+ filteredMachineDeps() {
1381
+ const intents = this.deps.machineDeps.intents;
1382
+ return {
1383
+ ...this.deps.machineDeps,
1384
+ intents: {
1385
+ ...intents,
1386
+ listOpen: async () => (await intents.listOpen()).filter((intent) => !this.deps.isActiveOp(intent.transferId))
1387
+ }
1388
+ };
1389
+ }
1390
+ replayDeps() {
1391
+ return {
1392
+ delivery: this.deps.delivery,
1393
+ now: () => this.deps.now(),
1394
+ attention: (transferId, code, detail) => {
1395
+ this.deps.emit(
1396
+ "transfer:attention",
1397
+ detail === void 0 ? { transferId, code } : { transferId, code, detail }
1398
+ );
1399
+ }
1400
+ };
1401
+ }
1402
+ };
1403
+ async function derivePendingTransfers(stores, decryptPayload) {
1404
+ const [backstop, journal, shortfalls] = await Promise.all([
1405
+ stores.backstop.list(),
1406
+ stores.deliveryJournal.list(),
1407
+ stores.shortfalls.list()
1408
+ ]);
1409
+ const byTransfer = groupJournal(journal);
1410
+ const out = [];
1411
+ const seen = /* @__PURE__ */ new Set();
1412
+ for (const entry of backstop) {
1413
+ if (entry.disposition !== "open") continue;
1414
+ seen.add(entry.transferId);
1415
+ out.push(await openRow(entry, byTransfer.get(entry.transferId) ?? [], decryptPayload));
1416
+ }
1417
+ for (const [transferId, entries] of byTransfer) {
1418
+ if (seen.has(transferId)) continue;
1419
+ seen.add(transferId);
1420
+ out.push(journalOnlyRow(transferId, entries));
1421
+ }
1422
+ for (const entry of shortfalls) {
1423
+ if (!seen.has(entry.transferId)) out.push(shortfallRow(entry));
1424
+ }
1425
+ return out;
1426
+ }
1427
+ function groupJournal(journal) {
1428
+ const byTransfer = /* @__PURE__ */ new Map();
1429
+ for (const entry of journal) {
1430
+ const mine = byTransfer.get(entry.transferId);
1431
+ if (mine === void 0) byTransfer.set(entry.transferId, [entry]);
1432
+ else mine.push(entry);
1433
+ }
1434
+ return byTransfer;
1435
+ }
1436
+ async function openRow(entry, journal, decryptPayload) {
1437
+ let payload = null;
1438
+ try {
1439
+ const raw = await decryptPayload(entry.payloadEnvelope);
1440
+ if (raw !== null && typeof raw === "object") payload = raw;
1441
+ } catch {
1442
+ payload = null;
1443
+ }
1444
+ const total = Array.isArray(payload?.direct) ? payload.direct.length + (payload.split !== void 0 ? 1 : 0) : 0;
1445
+ const certified = distinctOpCount(journal);
1446
+ return {
1447
+ transferId: entry.transferId,
1448
+ kind: "open",
1449
+ recipient: typeof payload?.recipient === "string" ? payload.recipient : "",
1450
+ coinId: typeof payload?.coinId === "string" ? payload.coinId : "",
1451
+ amount: typeof payload?.amount === "string" ? payload.amount : "",
1452
+ legs: { certified, total: Math.max(total, certified) },
1453
+ deliveryPending: journal.length > 0,
1454
+ createdAt: entry.createdAt
1455
+ };
1456
+ }
1457
+ function journalOnlyRow(transferId, entries) {
1458
+ const certified = distinctOpCount(entries);
1459
+ return {
1460
+ transferId,
1461
+ kind: "open",
1462
+ recipient: entries[0].recipientPubkey,
1463
+ coinId: "",
1464
+ amount: "",
1465
+ legs: { certified, total: certified },
1466
+ deliveryPending: true,
1467
+ createdAt: 0
1468
+ };
1469
+ }
1470
+ function shortfallRow(entry) {
1471
+ return {
1472
+ transferId: entry.transferId,
1473
+ kind: "shortfall",
1474
+ recipient: entry.recipient,
1475
+ coinId: entry.coinId,
1476
+ amount: entry.remainingAmount,
1477
+ legs: { certified: entry.committedTokenIds.length, total: entry.committedTokenIds.length },
1478
+ deliveryPending: false,
1479
+ createdAt: entry.createdAt
1480
+ };
1481
+ }
1482
+ function distinctOpCount(entries) {
1483
+ return new Set(entries.map((entry) => entry.opIndex)).size;
1484
+ }
1485
+
1269
1486
  // node_modules/@noble/ciphers/utils.js
1270
1487
  function isBytes2(a) {
1271
1488
  return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
@@ -3597,6 +3814,21 @@ var PaymentsFacade = class {
3597
3814
  this.ownPubkeyBytes = parts.ownPubkeyBytes;
3598
3815
  this.requests = parts.requests;
3599
3816
  deps.deliveryPort.bindDeliveryKeys((blob) => this.engine().deliveryKeys(blob));
3817
+ this.heartbeat = new ConvergenceHeartbeat({
3818
+ now: () => this.nowMs(),
3819
+ onTick: () => this.trackTail(this.runConvergencePass())
3820
+ });
3821
+ this.converger = new Converger({
3822
+ machineDeps: this.machineDeps,
3823
+ stores: this.machineStores,
3824
+ delivery: deps.deliveryPort,
3825
+ reconcile: (report) => this.requests.reconcile(report),
3826
+ refreshView: () => this.view.delta(),
3827
+ replayMints: () => this.replayMints(),
3828
+ isActiveOp: (id) => this.activeMoneyOps.has(id),
3829
+ emit: deps.emit,
3830
+ now: () => this.nowMs()
3831
+ });
3600
3832
  }
3601
3833
  view;
3602
3834
  ledger;
@@ -3613,10 +3845,18 @@ var PaymentsFacade = class {
3613
3845
  pendingOps = /* @__PURE__ */ new Set();
3614
3846
  unsubscribers = [];
3615
3847
  started = false;
3848
+ // §7 heartbeat: in-memory timing cell + pass runner (see convergence.ts).
3849
+ heartbeat;
3850
+ converger;
3851
+ /** Resume single-flight (§7): ticks, start() and resumeNow() coalesce onto ONE pass. */
3852
+ resumeFlight = new SingleFlight();
3853
+ /** §7 ownership: ids with an in-process machine attempt — the pass never adopts one. */
3854
+ activeMoneyOps = /* @__PURE__ */ new Set();
3616
3855
  // ── lifecycle ──────────────────────────────────────────────────────────────
3617
3856
  async start() {
3618
3857
  if (this.started) return;
3619
3858
  this.started = true;
3859
+ this.heartbeat.start();
3620
3860
  await this.deps.session.start();
3621
3861
  this.unsubscribers.push(
3622
3862
  this.deps.session.subscribeStream("mailbox", () => {
@@ -3629,20 +3869,11 @@ var PaymentsFacade = class {
3629
3869
  this.trackTail(this.requests.drainIncoming());
3630
3870
  })
3631
3871
  );
3632
- this.trackTail(this.runResume());
3633
- this.trackTail(
3634
- this.machineStores.deliveryJournal.replay({
3635
- delivery: this.deps.deliveryPort,
3636
- now: this.deps.now ?? Date.now,
3637
- attention: (transferId, code, detail) => {
3638
- this.deps.emit(
3639
- "transfer:attention",
3640
- detail === void 0 ? { transferId, code } : { transferId, code, detail }
3641
- );
3642
- }
3643
- })
3644
- );
3645
- this.trackTail(this.replayMints());
3872
+ const statusUnsub = this.deps.session.subscribeStatus?.((status) => {
3873
+ if (status === "connected") this.heartbeat.recovered();
3874
+ });
3875
+ if (statusUnsub !== void 0) this.unsubscribers.push(statusUnsub);
3876
+ this.trackTail(this.runConvergencePass());
3646
3877
  this.trackTail(this.seedHeldStates());
3647
3878
  await this.view.fullPull().catch(() => void 0);
3648
3879
  this.receiveLoop.start(this.deps.receivePollMs);
@@ -3650,6 +3881,7 @@ var PaymentsFacade = class {
3650
3881
  /** §7 same-address restart gate: resolves only after in-flight ops settle. */
3651
3882
  async stop() {
3652
3883
  this.started = false;
3884
+ this.heartbeat.stop();
3653
3885
  this.receiveLoop.stop();
3654
3886
  for (const unsubscribe of this.unsubscribers) unsubscribe();
3655
3887
  this.unsubscribers = [];
@@ -3675,6 +3907,10 @@ var PaymentsFacade = class {
3675
3907
  history(page) {
3676
3908
  return this.historyStore.page(page ?? {});
3677
3909
  }
3910
+ /** §4 pending-transfers UI surface — derived on read, never cached (convergence.ts). */
3911
+ pendingTransfers() {
3912
+ return derivePendingTransfers(this.machineStores, this.machineDeps.decryptPayload);
3913
+ }
3678
3914
  // ── money movement ─────────────────────────────────────────────────────────
3679
3915
  send(request) {
3680
3916
  return this.track(this.sendWithPolicy(request));
@@ -3687,6 +3923,22 @@ var PaymentsFacade = class {
3687
3923
  mint(coinId, amount) {
3688
3924
  return this.track(this.mintInner(coinId, amount));
3689
3925
  }
3926
+ // §4 retry verb: coalesces onto the running pass — NEVER re-issue send() (#631/#676).
3927
+ async resumeNow() {
3928
+ if (!this.started) return;
3929
+ await this.track(this.runConvergencePass());
3930
+ }
3931
+ /** One single-flighted pass + its reschedule: concurrent callers coalesce. */
3932
+ runConvergencePass() {
3933
+ return this.resumeFlight.run(async () => {
3934
+ const outcome = await this.converger.convergeOnce();
3935
+ const pending = await this.converger.pendingWork().catch(() => true);
3936
+ this.heartbeat.settle(outcome, pending);
3937
+ });
3938
+ }
3939
+ nowMs() {
3940
+ return (this.deps.now ?? Date.now)();
3941
+ }
3690
3942
  // ── send policy (§5.5 + old-loop parity; the machine stays policy-free) ────
3691
3943
  async sendWithPolicy(request) {
3692
3944
  const recipient = await this.requireSameNetworkRecipient(request.recipient);
@@ -3736,12 +3988,15 @@ var PaymentsFacade = class {
3736
3988
  return recipient;
3737
3989
  }
3738
3990
  async runAttempt(ctx) {
3991
+ this.activeMoneyOps.add(ctx.transferId);
3739
3992
  try {
3740
3993
  const { deliveryPending } = await this.machine.run(ctx.plan);
3741
3994
  this.settleSuccess(ctx, ctx.sourceIds);
3742
3995
  return { kind: "success", deliveryPending };
3743
3996
  } catch (err) {
3744
- return this.disposeFailedAttempt(ctx, err);
3997
+ return await this.disposeFailedAttempt(ctx, err);
3998
+ } finally {
3999
+ this.activeMoneyOps.delete(ctx.transferId);
3745
4000
  }
3746
4001
  }
3747
4002
  /**
@@ -3750,7 +4005,10 @@ var PaymentsFacade = class {
3750
4005
  * the remainder under a NEW transferId — never the full amount.
3751
4006
  */
3752
4007
  async consumePartial(ctx, disposition, run, attempt) {
3753
- run.pendingAny = run.pendingAny || disposition.deliveryPending;
4008
+ if (disposition.deliveryPending) {
4009
+ run.pendingAny = true;
4010
+ this.heartbeat.arm();
4011
+ }
3754
4012
  const partial = disposition.entry;
3755
4013
  run.delivered.push(...partial.committedTokenIds);
3756
4014
  run.firstPartialId ??= partial.transferId;
@@ -3908,6 +4166,7 @@ var PaymentsFacade = class {
3908
4166
  /** Open intent: sources stay reserved + in-flight (§5.2) until resume adopts them. */
3909
4167
  settleKeepOpen(ctx) {
3910
4168
  this.queue.clearExpectedChange(ctx.transferId);
4169
+ this.heartbeat.arm();
3911
4170
  }
3912
4171
  settlePartial(ctx, partial) {
3913
4172
  this.ledger.cancel(ctx.transferId);
@@ -3944,6 +4203,7 @@ var PaymentsFacade = class {
3944
4203
  for (const id of run.settledShortfalls.splice(0)) {
3945
4204
  await this.machineStores.shortfalls.removeByKey(id).catch(() => void 0);
3946
4205
  }
4206
+ if (run.pendingAny) this.heartbeat.arm();
3947
4207
  const result = {
3948
4208
  id: transferId,
3949
4209
  status: run.pendingAny ? "confirmed" : "delivered",
@@ -3988,21 +4248,29 @@ var PaymentsFacade = class {
3988
4248
  if (!/^(?:[0-9a-f]{2})+$/.test(coinId) || amount <= 0n) {
3989
4249
  return { success: false, error: "coinId must be even-length lowercase hex and amount positive" };
3990
4250
  }
3991
- const engine = this.engine();
3992
4251
  const mintId = this.newId();
3993
- const params = this.mintParams(coinId, amount);
4252
+ this.activeMoneyOps.add(mintId);
4253
+ try {
4254
+ return await this.mintUnderJournal(mintId, coinId, amount);
4255
+ } finally {
4256
+ this.activeMoneyOps.delete(mintId);
4257
+ }
4258
+ }
4259
+ async mintUnderJournal(mintId, coinId, amount) {
4260
+ const engine = this.engine();
3994
4261
  const entry = {
3995
4262
  mintId,
3996
4263
  coinId,
3997
4264
  amount: amount.toString(),
3998
4265
  tokenId: "",
3999
- createdAt: (this.deps.now ?? Date.now)()
4266
+ createdAt: this.nowMs()
4000
4267
  };
4001
4268
  await this.machineStores.mintJournal.upsert(entry);
4002
4269
  let token;
4003
4270
  try {
4004
- token = await engine.mint(params, { transferId: mintId });
4271
+ token = await engine.mint(this.mintParams(coinId, amount), { transferId: mintId });
4005
4272
  } catch (err) {
4273
+ this.heartbeat.arm();
4006
4274
  return { success: false, error: messageOf(err) };
4007
4275
  }
4008
4276
  if (token.blob.tokenId !== entry.tokenId) {
@@ -4011,6 +4279,7 @@ var PaymentsFacade = class {
4011
4279
  try {
4012
4280
  await this.finalizeMint(engine, mintId, token, coinId, amount.toString());
4013
4281
  } catch (err) {
4282
+ this.heartbeat.arm();
4014
4283
  return { success: false, tokenId: token.blob.tokenId, error: messageOf(err) };
4015
4284
  }
4016
4285
  return { success: true, tokenId: token.blob.tokenId };
@@ -4033,18 +4302,22 @@ var PaymentsFacade = class {
4033
4302
  this.heldStates.set(token.blob.tokenId, (await engine.deliveryKeys(bytes)).stateHash);
4034
4303
  this.trackTail(this.view.delta());
4035
4304
  }
4305
+ /** @returns how many journal entries were RESOLVED (cleared) — heartbeat progress. */
4036
4306
  async replayMints() {
4307
+ let resolved = 0;
4037
4308
  for (const entry of await this.machineStores.mintJournal.list()) {
4309
+ if (this.activeMoneyOps.has(entry.mintId)) continue;
4038
4310
  try {
4039
- await this.replayMint(entry);
4311
+ if (await this.replayMint(entry)) resolved += 1;
4040
4312
  } catch {
4041
4313
  }
4042
4314
  }
4315
+ return resolved;
4043
4316
  }
4044
4317
  async replayMint(entry) {
4045
4318
  if (entry.tokenId !== "" && await this.tokenInServerInventory(entry.tokenId)) {
4046
4319
  await this.machineStores.mintJournal.removeByKey(entry.mintId);
4047
- return;
4320
+ return true;
4048
4321
  }
4049
4322
  const engine = this.engine();
4050
4323
  if (!supportsDeterministicMint(engine)) {
@@ -4052,7 +4325,7 @@ var PaymentsFacade = class {
4052
4325
  transferId: entry.mintId,
4053
4326
  code: ATTENTION_MINT_UNRESOLVED
4054
4327
  });
4055
- return;
4328
+ return false;
4056
4329
  }
4057
4330
  const token = await engine.mint(this.mintParams(entry.coinId, BigInt(entry.amount)), {
4058
4331
  transferId: entry.mintId
@@ -4061,6 +4334,7 @@ var PaymentsFacade = class {
4061
4334
  await this.machineStores.mintJournal.upsert({ ...entry, tokenId: token.blob.tokenId });
4062
4335
  }
4063
4336
  await this.finalizeMint(engine, entry.mintId, token, entry.coinId, entry.amount);
4337
+ return true;
4064
4338
  }
4065
4339
  mintParams(coinId, amount) {
4066
4340
  return {
@@ -4086,17 +4360,6 @@ var PaymentsFacade = class {
4086
4360
  newId() {
4087
4361
  return (this.deps.newId ?? randomUUID)();
4088
4362
  }
4089
- async runResume() {
4090
- const report = await resumeAll(this.machineDeps);
4091
- await this.requests.reconcile({
4092
- resumed: report.resumed,
4093
- conflicted: report.conflicted,
4094
- open: report.failed
4095
- });
4096
- if (report.resumed.length > 0 || report.conflicted.length > 0) {
4097
- await this.view.delta().catch(() => void 0);
4098
- }
4099
- }
4100
4363
  async seedHeldStates() {
4101
4364
  let page = await this.deps.storagePort.listInventory();
4102
4365
  for (let i = 0; i < INVENTORY_SCAN_PAGE_LIMIT; i++) {
@@ -4110,7 +4373,7 @@ var PaymentsFacade = class {
4110
4373
  toUiToken(tokenId, coinId, amount) {
4111
4374
  const registry = this.deps.registry;
4112
4375
  const iconUrl = registry.getIconUrl(coinId);
4113
- const now = (this.deps.now ?? Date.now)();
4376
+ const now = this.nowMs();
4114
4377
  return {
4115
4378
  id: tokenId,
4116
4379
  coinId,
@@ -4162,6 +4425,8 @@ function messageOf(err) {
4162
4425
  // Annotate the CommonJS export names for ESM import in node:
4163
4426
  0 && (module.exports = {
4164
4427
  ATTENTION_MINT_UNRESOLVED,
4428
+ HEARTBEAT_CAP_MS,
4429
+ HEARTBEAT_SEED_MS,
4165
4430
  MAX_RESELECT,
4166
4431
  PaymentsFacade,
4167
4432
  STORE_KEYS,