@unicitylabs/sphere-sdk 0.14.11-dev.8 → 0.15.0-dev.1

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 (46) hide show
  1. package/README.md +55 -17
  2. package/dist/connect/index.cjs +10 -17
  3. package/dist/connect/index.cjs.map +1 -1
  4. package/dist/connect/index.js +10 -17
  5. package/dist/connect/index.js.map +1 -1
  6. package/dist/core/index.cjs +98 -99
  7. package/dist/core/index.cjs.map +1 -1
  8. package/dist/core/index.d.cts +5 -14
  9. package/dist/core/index.d.ts +5 -14
  10. package/dist/core/index.js +98 -99
  11. package/dist/core/index.js.map +1 -1
  12. package/dist/impl/browser/connect/index.cjs +3 -3
  13. package/dist/impl/browser/connect/index.cjs.map +1 -1
  14. package/dist/impl/browser/connect/index.js +3 -3
  15. package/dist/impl/browser/connect/index.js.map +1 -1
  16. package/dist/impl/browser/index.cjs.map +1 -1
  17. package/dist/impl/browser/index.js.map +1 -1
  18. package/dist/impl/nodejs/connect/index.cjs +2 -2
  19. package/dist/impl/nodejs/connect/index.cjs.map +1 -1
  20. package/dist/impl/nodejs/connect/index.js +2 -2
  21. package/dist/impl/nodejs/connect/index.js.map +1 -1
  22. package/dist/impl/nodejs/index.cjs.map +1 -1
  23. package/dist/impl/nodejs/index.js.map +1 -1
  24. package/dist/impl/wallet-api-v2/index.cjs +95 -1
  25. package/dist/impl/wallet-api-v2/index.cjs.map +1 -1
  26. package/dist/impl/wallet-api-v2/index.js +95 -1
  27. package/dist/impl/wallet-api-v2/index.js.map +1 -1
  28. package/dist/index.cjs +98 -99
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +6 -11
  31. package/dist/index.d.ts +6 -11
  32. package/dist/index.js +98 -99
  33. package/dist/index.js.map +1 -1
  34. package/dist/modules/payments-v2/index.cjs +149 -193
  35. package/dist/modules/payments-v2/index.cjs.map +1 -1
  36. package/dist/modules/payments-v2/index.d.cts +8 -5
  37. package/dist/modules/payments-v2/index.d.ts +8 -5
  38. package/dist/modules/payments-v2/index.js +146 -192
  39. package/dist/modules/payments-v2/index.js.map +1 -1
  40. package/dist/token-engine/index.cjs +43 -57
  41. package/dist/token-engine/index.cjs.map +1 -1
  42. package/dist/token-engine/index.d.cts +0 -4
  43. package/dist/token-engine/index.d.ts +0 -4
  44. package/dist/token-engine/index.js +43 -57
  45. package/dist/token-engine/index.js.map +1 -1
  46. package/package.json +2 -2
@@ -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;
@@ -437,6 +442,8 @@ interface StorageProvider extends BaseProvider {
437
442
  loadTrackedAddresses(): Promise<TrackedAddressEntry[]>;
438
443
  }
439
444
 
445
+ /** Never `storage.clear()` — that takes the keys too. */
446
+ declare function sweepSupersededState(storage: StorageProvider): Promise<void>;
440
447
  interface ScopedKV {
441
448
  get<T>(key: string): Promise<T | null>;
442
449
  set<T>(key: string, value: T): Promise<void>;
@@ -539,10 +546,6 @@ interface SphereValue {
539
546
  * `token`, so it is NOT stored — only cached at runtime on SphereToken.value.
540
547
  */
541
548
  interface TokenBlob {
542
- /** Blob format version (sphere storage migrations; independent of SDK CBOR). */
543
- readonly v: number;
544
- /** NetworkId.id the token belongs to (mainnet=1 / testnet=2 / local=3). */
545
- readonly network: number;
546
549
  /**
547
550
  * Genesis-stable token id — 64-char lowercase hex of the v2 `TokenId.bytes`
548
551
  * (same across every state of the token). Stored on the blob so dedup / listing
@@ -1174,4 +1177,4 @@ declare class PaymentsFacade implements PaymentsV2 {
1174
1177
  declare const HEARTBEAT_SEED_MS = 5000;
1175
1178
  declare const HEARTBEAT_CAP_MS = 120000;
1176
1179
 
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 };
1180
+ 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, sweepSupersededState };
@@ -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;
@@ -437,6 +442,8 @@ interface StorageProvider extends BaseProvider {
437
442
  loadTrackedAddresses(): Promise<TrackedAddressEntry[]>;
438
443
  }
439
444
 
445
+ /** Never `storage.clear()` — that takes the keys too. */
446
+ declare function sweepSupersededState(storage: StorageProvider): Promise<void>;
440
447
  interface ScopedKV {
441
448
  get<T>(key: string): Promise<T | null>;
442
449
  set<T>(key: string, value: T): Promise<void>;
@@ -539,10 +546,6 @@ interface SphereValue {
539
546
  * `token`, so it is NOT stored — only cached at runtime on SphereToken.value.
540
547
  */
541
548
  interface TokenBlob {
542
- /** Blob format version (sphere storage migrations; independent of SDK CBOR). */
543
- readonly v: number;
544
- /** NetworkId.id the token belongs to (mainnet=1 / testnet=2 / local=3). */
545
- readonly network: number;
546
549
  /**
547
550
  * Genesis-stable token id — 64-char lowercase hex of the v2 `TokenId.bytes`
548
551
  * (same across every state of the token). Stored on the blob so dedup / listing
@@ -1174,4 +1177,4 @@ declare class PaymentsFacade implements PaymentsV2 {
1174
1177
  declare const HEARTBEAT_SEED_MS = 5000;
1175
1178
  declare const HEARTBEAT_CAP_MS = 120000;
1176
1179
 
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 };
1180
+ 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, sweepSupersededState };
@@ -1,6 +1,114 @@
1
+ // modules/payments-v2/ports.ts
2
+ function isRetryableAckError(err) {
3
+ return err !== null && typeof err === "object" && err.retryable === true;
4
+ }
5
+
6
+ // core/logger.ts
7
+ var LOGGER_KEY = "__sphere_sdk_logger__";
8
+ function getState() {
9
+ const g = globalThis;
10
+ if (!g[LOGGER_KEY]) {
11
+ g[LOGGER_KEY] = { debug: false, tags: {}, handler: null };
12
+ }
13
+ return g[LOGGER_KEY];
14
+ }
15
+ function isEnabled(tag) {
16
+ const state = getState();
17
+ if (tag in state.tags) return state.tags[tag];
18
+ return state.debug;
19
+ }
20
+ var logger = {
21
+ /**
22
+ * Configure the logger. Can be called multiple times (last write wins).
23
+ * Typically called by createBrowserProviders(), createNodeProviders(), or Sphere.init().
24
+ */
25
+ configure(config) {
26
+ const state = getState();
27
+ if (config.debug !== void 0) state.debug = config.debug;
28
+ if (config.handler !== void 0) state.handler = config.handler;
29
+ },
30
+ /**
31
+ * Enable/disable debug logging for a specific tag.
32
+ * Per-tag setting overrides the global debug flag.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * logger.setTagDebug('Nostr', true); // enable only Nostr logs
37
+ * logger.setTagDebug('Nostr', false); // disable Nostr logs even if global debug=true
38
+ * ```
39
+ */
40
+ setTagDebug(tag, enabled) {
41
+ getState().tags[tag] = enabled;
42
+ },
43
+ /**
44
+ * Clear per-tag override, falling back to global debug flag.
45
+ */
46
+ clearTagDebug(tag) {
47
+ delete getState().tags[tag];
48
+ },
49
+ /** Returns true if debug mode is enabled for the given tag (or globally). */
50
+ isDebugEnabled(tag) {
51
+ if (tag) return isEnabled(tag);
52
+ return getState().debug;
53
+ },
54
+ /**
55
+ * Debug-level log. Only shown when debug is enabled (globally or for this tag).
56
+ * Use for detailed operational information.
57
+ */
58
+ debug(tag, message, ...args) {
59
+ if (!isEnabled(tag)) return;
60
+ const state = getState();
61
+ if (state.handler) {
62
+ state.handler("debug", tag, message, ...args);
63
+ } else {
64
+ console.log(`[${tag}]`, message, ...args);
65
+ }
66
+ },
67
+ /**
68
+ * Warning-level log. ALWAYS shown regardless of debug flag.
69
+ * Use for important but non-critical issues (timeouts, retries, degraded state).
70
+ */
71
+ warn(tag, message, ...args) {
72
+ const state = getState();
73
+ if (state.handler) {
74
+ state.handler("warn", tag, message, ...args);
75
+ } else {
76
+ console.warn(`[${tag}]`, message, ...args);
77
+ }
78
+ },
79
+ /**
80
+ * Error-level log. ALWAYS shown regardless of debug flag.
81
+ * Use for critical failures that should never be silenced.
82
+ */
83
+ error(tag, message, ...args) {
84
+ const state = getState();
85
+ if (state.handler) {
86
+ state.handler("error", tag, message, ...args);
87
+ } else {
88
+ console.error(`[${tag}]`, message, ...args);
89
+ }
90
+ },
91
+ /** Reset all logger state (debug flag, tags, handler). Primarily for tests. */
92
+ reset() {
93
+ const g = globalThis;
94
+ delete g[LOGGER_KEY];
95
+ }
96
+ };
97
+
1
98
  // modules/payments-v2/stores.ts
99
+ var KV_PREFIX = "pv2g2:";
100
+ var SUPERSEDED_KV_PREFIXES = ["pv2:"];
101
+ async function sweepSupersededState(storage) {
102
+ for (const prefix of SUPERSEDED_KV_PREFIXES) {
103
+ try {
104
+ await storage.clear(prefix);
105
+ } catch (err) {
106
+ logger.warn("PaymentsV2", `could not clear superseded state under "${prefix}": ${String(err)}`);
107
+ }
108
+ }
109
+ }
2
110
  function createScopedKV(storage, network, chainPubkey) {
3
- const prefix = `pv2:${network}:${chainPubkey}:`;
111
+ const prefix = `${KV_PREFIX}${network}:${chainPubkey}:`;
4
112
  return {
5
113
  async get(key) {
6
114
  const raw = await storage.get(prefix + key);
@@ -449,69 +557,6 @@ function randomUUID() {
449
557
  return `${h[0]}${h[1]}${h[2]}${h[3]}-${h[4]}${h[5]}-${h[6]}${h[7]}-${h[8]}${h[9]}-${h[10]}${h[11]}${h[12]}${h[13]}${h[14]}${h[15]}`;
450
558
  }
451
559
 
452
- // token-engine/sdk.ts
453
- import { StateTransitionClient } from "@unicitylabs/state-transition-sdk/lib/StateTransitionClient.js";
454
- import { AggregatorClient } from "@unicitylabs/state-transition-sdk/lib/api/AggregatorClient.js";
455
- import { NetworkId } from "@unicitylabs/state-transition-sdk/lib/api/NetworkId.js";
456
- import { CertificationData } from "@unicitylabs/state-transition-sdk/lib/api/CertificationData.js";
457
- import { CertificationResponse, CertificationStatus } from "@unicitylabs/state-transition-sdk/lib/api/CertificationResponse.js";
458
- import { StateId } from "@unicitylabs/state-transition-sdk/lib/api/StateId.js";
459
- import { InclusionProof } from "@unicitylabs/state-transition-sdk/lib/api/InclusionProof.js";
460
- import { InclusionProofResponse } from "@unicitylabs/state-transition-sdk/lib/api/InclusionProofResponse.js";
461
- import { JsonRpcNetworkError } from "@unicitylabs/state-transition-sdk/lib/api/json-rpc/JsonRpcNetworkError.js";
462
- import { RootTrustBase } from "@unicitylabs/state-transition-sdk/lib/api/bft/RootTrustBase.js";
463
- import { waitInclusionProof } from "@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils.js";
464
- import { InclusionProofVerificationStatus } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/rule/InclusionProofVerificationRule.js";
465
- import { Token } from "@unicitylabs/state-transition-sdk/lib/transaction/Token.js";
466
- import { MintTransaction } from "@unicitylabs/state-transition-sdk/lib/transaction/MintTransaction.js";
467
- import { TransferTransaction } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction.js";
468
- import { CertifiedMintTransaction } from "@unicitylabs/state-transition-sdk/lib/transaction/CertifiedMintTransaction.js";
469
- import { CertifiedTransferTransaction } from "@unicitylabs/state-transition-sdk/lib/transaction/CertifiedTransferTransaction.js";
470
- import { TokenId } from "@unicitylabs/state-transition-sdk/lib/transaction/TokenId.js";
471
- import { TokenType } from "@unicitylabs/state-transition-sdk/lib/transaction/TokenType.js";
472
- import { TokenSalt } from "@unicitylabs/state-transition-sdk/lib/transaction/TokenSalt.js";
473
- import { StateMask } from "@unicitylabs/state-transition-sdk/lib/transaction/StateMask.js";
474
- import { MintJustificationVerifierService } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/MintJustificationVerifierService.js";
475
- import { TokenIssuanceVerifierService } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/TokenIssuanceVerifierService.js";
476
- import { VerificationContext } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/VerificationContext.js";
477
- import { UnicityCertificateVerifier } from "@unicitylabs/state-transition-sdk/lib/api/bft/verification/UnicityCertificateVerifier.js";
478
- import { UnicitySealQuorumSignaturesVerificationRule } from "@unicitylabs/state-transition-sdk/lib/api/bft/verification/rule/UnicitySealQuorumSignaturesVerificationRule.js";
479
- import { VerifiedSealCache } from "@unicitylabs/state-transition-sdk/lib/api/bft/verification/VerifiedSealCache.js";
480
- import { Secp256k1SignatureVerifier } from "@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/Secp256k1SignatureVerifier.js";
481
- import { WorkerTokenVerifier } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/worker/WorkerTokenVerifier.js";
482
- import { EncodedPredicate } from "@unicitylabs/state-transition-sdk/lib/predicate/EncodedPredicate.js";
483
- import { SignaturePredicate } from "@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicate.js";
484
- import { SignaturePredicateUnlockScript } from "@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicateUnlockScript.js";
485
- import { BurnPredicate } from "@unicitylabs/state-transition-sdk/lib/predicate/builtin/BurnPredicate.js";
486
- import { PredicateVerifierService } from "@unicitylabs/state-transition-sdk/lib/predicate/verification/PredicateVerifierService.js";
487
- import { SigningService } from "@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/SigningService.js";
488
- import { Signature } from "@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/Signature.js";
489
- import { MintSigningService } from "@unicitylabs/state-transition-sdk/lib/crypto/MintSigningService.js";
490
- import { HashAlgorithm } from "@unicitylabs/state-transition-sdk/lib/crypto/hash/HashAlgorithm.js";
491
- import { DataHash } from "@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHash.js";
492
- import { DataHasher } from "@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHasher.js";
493
- import { DataHasherFactory } from "@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHasherFactory.js";
494
- import { CborSerializer } from "@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborSerializer.js";
495
- import { CborDeserializer } from "@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborDeserializer.js";
496
- import { CborError } from "@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborError.js";
497
- import { Asset } from "@unicitylabs/state-transition-sdk/lib/payment/asset/Asset.js";
498
- import { AssetId } from "@unicitylabs/state-transition-sdk/lib/payment/asset/AssetId.js";
499
- import { PaymentAssetCollection } from "@unicitylabs/state-transition-sdk/lib/payment/asset/PaymentAssetCollection.js";
500
- import { TokenSplit } from "@unicitylabs/state-transition-sdk/lib/payment/TokenSplit.js";
501
- import { SplitTokenRequest } from "@unicitylabs/state-transition-sdk/lib/payment/SplitTokenRequest.js";
502
- import { SplitToken } from "@unicitylabs/state-transition-sdk/lib/payment/SplitToken.js";
503
- import { SplitAllocationProof } from "@unicitylabs/state-transition-sdk/lib/payment/SplitAllocationProof.js";
504
- import { SplitMintJustification } from "@unicitylabs/state-transition-sdk/lib/payment/SplitMintJustification.js";
505
- import { SplitMintJustificationVerifier } from "@unicitylabs/state-transition-sdk/lib/payment/SplitMintJustificationVerifier.js";
506
- import { VerificationStatus } from "@unicitylabs/state-transition-sdk/lib/verification/VerificationStatus.js";
507
- import { VerificationResult } from "@unicitylabs/state-transition-sdk/lib/verification/VerificationResult.js";
508
- import { HexConverter } from "@unicitylabs/state-transition-sdk/lib/util/HexConverter.js";
509
- import { BigintConverter } from "@unicitylabs/state-transition-sdk/lib/util/BigintConverter.js";
510
- import { BitString } from "@unicitylabs/state-transition-sdk/lib/util/BitString.js";
511
-
512
- // token-engine/token-blob.ts
513
- var TOKEN_BLOB_VERSION = 1;
514
-
515
560
  // modules/payments-v2/async.ts
516
561
  var SingleFlight = class {
517
562
  flight = null;
@@ -1156,12 +1201,7 @@ var TransferMachine = class {
1156
1201
  let total = 0n;
1157
1202
  for (const outcome of committed) {
1158
1203
  if (outcome.recipientBlob === void 0) continue;
1159
- const token = await engine.decodeToken({
1160
- v: TOKEN_BLOB_VERSION,
1161
- network: 0,
1162
- tokenId: "",
1163
- token: outcome.recipientBlob
1164
- });
1204
+ const token = await engine.decodeToken({ tokenId: "", token: outcome.recipientBlob });
1165
1205
  total += engine.balanceOf(token, coinId);
1166
1206
  }
1167
1207
  return total.toString();
@@ -1277,8 +1317,9 @@ async function fetchSources(deps, jobs) {
1277
1317
  const engine = deps.engine();
1278
1318
  for (const [tokenId, bytes] of blobs) {
1279
1319
  try {
1280
- decoded.set(tokenId, await engine.decodeToken({ v: TOKEN_BLOB_VERSION, network: 0, tokenId, token: bytes }));
1281
- } catch {
1320
+ decoded.set(tokenId, await engine.decodeToken({ tokenId, token: bytes }));
1321
+ } catch (err) {
1322
+ logger.warn("PaymentsV2", `resume: source ${tokenId} did not decode \u2014 ${String(err)}`);
1282
1323
  }
1283
1324
  }
1284
1325
  return decoded;
@@ -3102,103 +3143,6 @@ var InventoryView = class {
3102
3143
  }
3103
3144
  };
3104
3145
 
3105
- // core/logger.ts
3106
- var LOGGER_KEY = "__sphere_sdk_logger__";
3107
- function getState() {
3108
- const g = globalThis;
3109
- if (!g[LOGGER_KEY]) {
3110
- g[LOGGER_KEY] = { debug: false, tags: {}, handler: null };
3111
- }
3112
- return g[LOGGER_KEY];
3113
- }
3114
- function isEnabled(tag) {
3115
- const state = getState();
3116
- if (tag in state.tags) return state.tags[tag];
3117
- return state.debug;
3118
- }
3119
- var logger = {
3120
- /**
3121
- * Configure the logger. Can be called multiple times (last write wins).
3122
- * Typically called by createBrowserProviders(), createNodeProviders(), or Sphere.init().
3123
- */
3124
- configure(config) {
3125
- const state = getState();
3126
- if (config.debug !== void 0) state.debug = config.debug;
3127
- if (config.handler !== void 0) state.handler = config.handler;
3128
- },
3129
- /**
3130
- * Enable/disable debug logging for a specific tag.
3131
- * Per-tag setting overrides the global debug flag.
3132
- *
3133
- * @example
3134
- * ```ts
3135
- * logger.setTagDebug('Nostr', true); // enable only Nostr logs
3136
- * logger.setTagDebug('Nostr', false); // disable Nostr logs even if global debug=true
3137
- * ```
3138
- */
3139
- setTagDebug(tag, enabled) {
3140
- getState().tags[tag] = enabled;
3141
- },
3142
- /**
3143
- * Clear per-tag override, falling back to global debug flag.
3144
- */
3145
- clearTagDebug(tag) {
3146
- delete getState().tags[tag];
3147
- },
3148
- /** Returns true if debug mode is enabled for the given tag (or globally). */
3149
- isDebugEnabled(tag) {
3150
- if (tag) return isEnabled(tag);
3151
- return getState().debug;
3152
- },
3153
- /**
3154
- * Debug-level log. Only shown when debug is enabled (globally or for this tag).
3155
- * Use for detailed operational information.
3156
- */
3157
- debug(tag, message, ...args) {
3158
- if (!isEnabled(tag)) return;
3159
- const state = getState();
3160
- if (state.handler) {
3161
- state.handler("debug", tag, message, ...args);
3162
- } else {
3163
- console.log(`[${tag}]`, message, ...args);
3164
- }
3165
- },
3166
- /**
3167
- * Warning-level log. ALWAYS shown regardless of debug flag.
3168
- * Use for important but non-critical issues (timeouts, retries, degraded state).
3169
- */
3170
- warn(tag, message, ...args) {
3171
- const state = getState();
3172
- if (state.handler) {
3173
- state.handler("warn", tag, message, ...args);
3174
- } else {
3175
- console.warn(`[${tag}]`, message, ...args);
3176
- }
3177
- },
3178
- /**
3179
- * Error-level log. ALWAYS shown regardless of debug flag.
3180
- * Use for critical failures that should never be silenced.
3181
- */
3182
- error(tag, message, ...args) {
3183
- const state = getState();
3184
- if (state.handler) {
3185
- state.handler("error", tag, message, ...args);
3186
- } else {
3187
- console.error(`[${tag}]`, message, ...args);
3188
- }
3189
- },
3190
- /** Reset all logger state (debug flag, tags, handler). Primarily for tests. */
3191
- reset() {
3192
- const g = globalThis;
3193
- delete g[LOGGER_KEY];
3194
- }
3195
- };
3196
-
3197
- // modules/payments-v2/ports.ts
3198
- function isRetryableAckError(err) {
3199
- return err !== null && typeof err === "object" && err.retryable === true;
3200
- }
3201
-
3202
3146
  // modules/payments-v2/receive/Receive.ts
3203
3147
  var ACK_BATCH_SIZE = 200;
3204
3148
  var REFRESH_INTERVAL_MS = 2500;
@@ -3247,22 +3191,24 @@ var Receive = class {
3247
3191
  * the tokens just accepted, and the balance would still only move at the end.
3248
3192
  * Ordering is preserved: store() still precedes the ack (§5.7).
3249
3193
  */
3250
- async maybeRefresh(deps, pending, epochOf) {
3194
+ async maybeRefresh(ctx) {
3195
+ const { deps, pending, pageEpoch } = ctx;
3251
3196
  if (deps.refreshView === void 0) return;
3252
3197
  const now = deps.now?.() ?? Date.now();
3253
3198
  if (now - this.lastRefreshAt < REFRESH_INTERVAL_MS) return;
3254
3199
  this.lastRefreshAt = now;
3255
- await flushAcks(deps, pending, epochOf);
3256
- deps.refreshView();
3200
+ if ((await flushAcks(deps, pending, pageEpoch)).progressed) deps.refreshView();
3257
3201
  }
3258
3202
  /** One listing pass: process each entry, flushing and refreshing as it goes. */
3259
3203
  async drainPages(ctx, basis) {
3260
- const { deps, engine, pending, stored, pageEpoch } = ctx;
3204
+ const { deps, pending, pageEpoch, claimable } = ctx;
3261
3205
  for await (const entry of deps.delivery.incoming(basis === null ? void 0 : String(basis.cursor))) {
3262
- const storedBefore = stored.length;
3263
- await this.processEntry(deps, engine, entry, pending, stored);
3264
- if (pending.length >= ACK_BATCH_SIZE) await flushAcks(deps, pending, pageEpoch);
3265
- if (stored.length > storedBefore) await this.maybeRefresh(deps, pending, pageEpoch);
3206
+ const claimableBefore = claimable.n;
3207
+ await this.processEntry(ctx, entry);
3208
+ if (pending.length >= ACK_BATCH_SIZE) {
3209
+ if (!(await flushAcks(deps, pending, pageEpoch)).progressed) return;
3210
+ }
3211
+ if (claimable.n > claimableBefore) await this.maybeRefresh(ctx);
3266
3212
  }
3267
3213
  await flushAcks(deps, pending, pageEpoch);
3268
3214
  }
@@ -3273,11 +3219,16 @@ var Receive = class {
3273
3219
  const stored = [];
3274
3220
  const pending = [];
3275
3221
  const pageEpoch = () => deps.delivery.incomingEpoch() ?? deps.syncEpoch();
3222
+ const claimable = { n: 0 };
3223
+ const finish = () => {
3224
+ if (claimable.n > 0) deps.refreshView?.();
3225
+ return stored;
3226
+ };
3276
3227
  try {
3277
3228
  const record = await deps.kv.get(STORE_KEYS.streamCursor("mailbox"));
3278
3229
  let basis = record !== null && record.syncEpoch === deps.syncEpoch() ? record : null;
3279
3230
  for (let pass = 0; pass < 2; pass++) {
3280
- await this.drainPages({ deps, engine, pending, stored, pageEpoch }, basis);
3231
+ await this.drainPages({ deps, engine, pending, stored, pageEpoch, claimable }, basis);
3281
3232
  const served = deps.delivery.incomingEpoch();
3282
3233
  if (basis === null || served === null || served === basis.syncEpoch) break;
3283
3234
  await deps.kv.remove(STORE_KEYS.streamCursor("mailbox"));
@@ -3285,22 +3236,25 @@ var Receive = class {
3285
3236
  }
3286
3237
  } catch (err) {
3287
3238
  logger.warn("PaymentsV2", "receive drain interrupted \u2014 unacked entries retry next drain:", err);
3288
- if (isRetryableAckError(err)) return stored;
3239
+ if (isRetryableAckError(err)) return finish();
3289
3240
  await flushAcks(deps, pending, pageEpoch).catch((flushErr) => {
3290
3241
  logger.warn("PaymentsV2", "receive ack flush failed \u2014 cursor holds at the acked prefix:", flushErr);
3242
+ return { progressed: false };
3291
3243
  });
3292
3244
  }
3293
- if (stored.length > 0) deps.refreshView?.();
3294
- return stored;
3245
+ return finish();
3295
3246
  }
3296
- async processEntry(deps, engine, entry, pending, stored) {
3247
+ async processEntry(ctx, entry) {
3248
+ const { deps, engine, pending, stored, claimable } = ctx;
3297
3249
  const screened = await screen(deps, engine, entry);
3298
3250
  if (screened.kind === "ack") {
3299
3251
  pending.push(screened.ack);
3252
+ if (screened.ack.disposition === "claimed") claimable.n += 1;
3300
3253
  return;
3301
3254
  }
3302
3255
  await deps.view.store(screened.record);
3303
3256
  pending.push(claimAck(entry));
3257
+ claimable.n += 1;
3304
3258
  const transfer = await announce(deps, entry, screened.record);
3305
3259
  stored.push(transfer);
3306
3260
  deps.emit("transfer:incoming", transfer);
@@ -3310,13 +3264,9 @@ async function screen(deps, engine, entry) {
3310
3264
  const blobBytes = await entry.fetchBlob();
3311
3265
  let token;
3312
3266
  try {
3313
- token = await engine.decodeToken({
3314
- v: TOKEN_BLOB_VERSION,
3315
- network: 0,
3316
- tokenId: "",
3317
- token: blobBytes
3318
- });
3319
- } catch {
3267
+ token = await engine.decodeToken({ tokenId: "", token: blobBytes });
3268
+ } catch (err) {
3269
+ logger.warn("PaymentsV2", `receive: undecodable blob rejected \u2014 ${String(err)}`);
3320
3270
  return { kind: "ack", ack: rejectAck(entry, "invalid") };
3321
3271
  }
3322
3272
  const verdict = await engine.verify(token);
@@ -3361,7 +3311,8 @@ async function announce(deps, entry, record) {
3361
3311
  };
3362
3312
  }
3363
3313
  async function flushAcks(deps, pending, epochOf) {
3364
- if (pending.length === 0) return;
3314
+ if (pending.length === 0) return { progressed: true };
3315
+ const before = pending.length;
3365
3316
  let lastAcked = null;
3366
3317
  try {
3367
3318
  const settled = await settleAcks(deps, pending);
@@ -3379,6 +3330,7 @@ async function flushAcks(deps, pending, epochOf) {
3379
3330
  await deps.kv.set(STORE_KEYS.streamCursor("mailbox"), record);
3380
3331
  }
3381
3332
  }
3333
+ return { progressed: pending.length < before };
3382
3334
  }
3383
3335
  async function settleAcks(deps, pending) {
3384
3336
  const batch = deps.delivery.ackBatch?.bind(deps.delivery);
@@ -4717,7 +4669,7 @@ var PaymentsFacade = class {
4717
4669
  if (bytes === void 0) {
4718
4670
  throw new SphereError(`Selected source ${tokenId} has no blob in storage`, "STORAGE_ERROR");
4719
4671
  }
4720
- const token = await engine.decodeToken({ v: TOKEN_BLOB_VERSION, network: 0, tokenId, token: bytes });
4672
+ const token = await engine.decodeToken({ tokenId, token: bytes });
4721
4673
  const keys = await engine.deliveryKeys(bytes);
4722
4674
  spentStates[tokenId] = { local: keys.stateHash, protocol: keys.stateHash };
4723
4675
  sources.set(tokenId, token);
@@ -4981,7 +4933,9 @@ export {
4981
4933
  PaymentsFacade,
4982
4934
  STORE_KEYS,
4983
4935
  createScopedKV,
4984
- supportsDeterministicMint
4936
+ isRetryableAckError,
4937
+ supportsDeterministicMint,
4938
+ sweepSupersededState
4985
4939
  };
4986
4940
  /*! Bundled license information:
4987
4941