@unicitylabs/sphere-sdk 0.9.1-dev.12 → 0.9.1-dev.14

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.d.cts CHANGED
@@ -2065,10 +2065,13 @@ declare class PaymentsModule {
2065
2065
  /** Coalesces concurrent payment-request pump runs. */
2066
2066
  private prPumpInFlight;
2067
2067
  /**
2068
- * Set after the once-per-session `status=open` bootstrap scan: the surfaced
2069
- * incoming list is in-memory only, so still-open requests BELOW the
2070
- * persisted cursor must be recovered at session start (bounded by the §5.5
2071
- * per-payer open cap).
2068
+ * Set after the once-per-session full incoming hydration (#556): the surfaced
2069
+ * incoming list is in-memory only, so on a fresh engine the CURRENT state of
2070
+ * ALL incoming requests — open AND resolved (paid/declined/expired) — must be
2071
+ * rebuilt from a `role=incoming&since=0` pull, not just the still-open ones.
2072
+ * A status-filtered bootstrap (the pre-#556 `status=open` scan) dropped
2073
+ * requests resolved in a PRIOR session, so the payer reopened and the
2074
+ * 'Paid Successfully' request was gone (twin of #521/#549).
2072
2075
  */
2073
2076
  private prBootstrapped;
2074
2077
  private loadedPromise;
@@ -2805,11 +2808,19 @@ declare class PaymentsModule {
2805
2808
  * mailbox-cursor pattern: the persisted `{cursor, syncEpoch}` is the resume
2806
2809
  * point; a `syncEpoch` change (server restore — §5.4) voids cursor
2807
2810
  * continuity, so the tail re-pulls from 0 and the id-dedup in
2808
- * {@link surfaceIncomingPaymentRequest} absorbs the replays. Because the
2809
- * surfaced list is in-memory only, each session ALSO starts with one
2810
- * `status=open` bootstrap scan from 0 — still-open requests below the
2811
- * cursor (bounded by the §5.5 per-payer open cap) are recovered; resolved
2812
- * ones are not re-surfaced.
2811
+ * {@link surfaceIncomingPaymentRequest} absorbs the replays.
2812
+ *
2813
+ * Because the surfaced list is in-memory only, each session FIRST runs one
2814
+ * full incoming hydration from `since=0` with NO status filter (#556 —
2815
+ * mirrors {@link hydrateHistoryFromServer}): a fresh engine rebuilds the
2816
+ * CURRENT state of ALL incoming requests — open AND resolved — so a request
2817
+ * paid/declined/expired in a PRIOR session is still present (with its
2818
+ * resolved status) instead of vanishing once the cursor advanced past it.
2819
+ * Hydration NEVER fires the new-incoming handlers/events for resolved
2820
+ * requests — only `open` ones notify (the status-aware
2821
+ * {@link surfaceIncomingPaymentRequest}) — so reopening can't spam stale
2822
+ * 'new request' notifications. After hydration the `since`-cursor delta poll
2823
+ * picks up live updates from the resume point.
2813
2824
  */
2814
2825
  private pumpIncomingPaymentRequests;
2815
2826
  /**
@@ -2822,13 +2833,19 @@ declare class PaymentsModule {
2822
2833
  * (PR twin of #546/#547).
2823
2834
  */
2824
2835
  private decryptPaymentRequestMemo;
2836
+ /** §16 wire status → the public {@link PaymentRequestStatus} display status. */
2837
+ private static readonly PR_WIRE_STATUS;
2825
2838
  /**
2826
2839
  * Map a §16 wire request onto the public {@link IncomingPaymentRequest}
2827
- * surface and notify (event + handlers). Only `open` requests are
2828
- * actionable; the in-memory id-dedup doubles as the replay guard for
2829
- * cursor resets. Multi-asset requests surface their first asset (the
2830
- * module's request surface is single-asset; module-created requests
2831
- * always are).
2840
+ * surface, deduped by id (the in-memory id-dedup doubles as the replay guard
2841
+ * for cursor resets). Requests of EVERY status are surfaced so a reloaded
2842
+ * thin wallet rebuilds the CURRENT state of its incoming view (#556) — open
2843
+ * ones land as actionable `pending`, resolved ones carry their paid/declined
2844
+ * (→ `rejected`)/expired status. Only `open` requests fire the new-incoming
2845
+ * event + handlers; resolved requests are folded into the list silently, so a
2846
+ * reload (or a `syncEpoch` re-pull) never re-notifies for already-resolved
2847
+ * requests. Multi-asset requests surface their first asset (the module's
2848
+ * request surface is single-asset; module-created requests always are).
2832
2849
  */
2833
2850
  private surfaceIncomingPaymentRequest;
2834
2851
  /**
package/dist/index.d.ts CHANGED
@@ -2065,10 +2065,13 @@ declare class PaymentsModule {
2065
2065
  /** Coalesces concurrent payment-request pump runs. */
2066
2066
  private prPumpInFlight;
2067
2067
  /**
2068
- * Set after the once-per-session `status=open` bootstrap scan: the surfaced
2069
- * incoming list is in-memory only, so still-open requests BELOW the
2070
- * persisted cursor must be recovered at session start (bounded by the §5.5
2071
- * per-payer open cap).
2068
+ * Set after the once-per-session full incoming hydration (#556): the surfaced
2069
+ * incoming list is in-memory only, so on a fresh engine the CURRENT state of
2070
+ * ALL incoming requests — open AND resolved (paid/declined/expired) — must be
2071
+ * rebuilt from a `role=incoming&since=0` pull, not just the still-open ones.
2072
+ * A status-filtered bootstrap (the pre-#556 `status=open` scan) dropped
2073
+ * requests resolved in a PRIOR session, so the payer reopened and the
2074
+ * 'Paid Successfully' request was gone (twin of #521/#549).
2072
2075
  */
2073
2076
  private prBootstrapped;
2074
2077
  private loadedPromise;
@@ -2805,11 +2808,19 @@ declare class PaymentsModule {
2805
2808
  * mailbox-cursor pattern: the persisted `{cursor, syncEpoch}` is the resume
2806
2809
  * point; a `syncEpoch` change (server restore — §5.4) voids cursor
2807
2810
  * continuity, so the tail re-pulls from 0 and the id-dedup in
2808
- * {@link surfaceIncomingPaymentRequest} absorbs the replays. Because the
2809
- * surfaced list is in-memory only, each session ALSO starts with one
2810
- * `status=open` bootstrap scan from 0 — still-open requests below the
2811
- * cursor (bounded by the §5.5 per-payer open cap) are recovered; resolved
2812
- * ones are not re-surfaced.
2811
+ * {@link surfaceIncomingPaymentRequest} absorbs the replays.
2812
+ *
2813
+ * Because the surfaced list is in-memory only, each session FIRST runs one
2814
+ * full incoming hydration from `since=0` with NO status filter (#556 —
2815
+ * mirrors {@link hydrateHistoryFromServer}): a fresh engine rebuilds the
2816
+ * CURRENT state of ALL incoming requests — open AND resolved — so a request
2817
+ * paid/declined/expired in a PRIOR session is still present (with its
2818
+ * resolved status) instead of vanishing once the cursor advanced past it.
2819
+ * Hydration NEVER fires the new-incoming handlers/events for resolved
2820
+ * requests — only `open` ones notify (the status-aware
2821
+ * {@link surfaceIncomingPaymentRequest}) — so reopening can't spam stale
2822
+ * 'new request' notifications. After hydration the `since`-cursor delta poll
2823
+ * picks up live updates from the resume point.
2813
2824
  */
2814
2825
  private pumpIncomingPaymentRequests;
2815
2826
  /**
@@ -2822,13 +2833,19 @@ declare class PaymentsModule {
2822
2833
  * (PR twin of #546/#547).
2823
2834
  */
2824
2835
  private decryptPaymentRequestMemo;
2836
+ /** §16 wire status → the public {@link PaymentRequestStatus} display status. */
2837
+ private static readonly PR_WIRE_STATUS;
2825
2838
  /**
2826
2839
  * Map a §16 wire request onto the public {@link IncomingPaymentRequest}
2827
- * surface and notify (event + handlers). Only `open` requests are
2828
- * actionable; the in-memory id-dedup doubles as the replay guard for
2829
- * cursor resets. Multi-asset requests surface their first asset (the
2830
- * module's request surface is single-asset; module-created requests
2831
- * always are).
2840
+ * surface, deduped by id (the in-memory id-dedup doubles as the replay guard
2841
+ * for cursor resets). Requests of EVERY status are surfaced so a reloaded
2842
+ * thin wallet rebuilds the CURRENT state of its incoming view (#556) — open
2843
+ * ones land as actionable `pending`, resolved ones carry their paid/declined
2844
+ * (→ `rejected`)/expired status. Only `open` requests fire the new-incoming
2845
+ * event + handlers; resolved requests are folded into the list silently, so a
2846
+ * reload (or a `syncEpoch` re-pull) never re-notifies for already-resolved
2847
+ * requests. Multi-asset requests surface their first asset (the module's
2848
+ * request surface is single-asset; module-created requests always are).
2832
2849
  */
2833
2850
  private surfaceIncomingPaymentRequest;
2834
2851
  /**
package/dist/index.js CHANGED
@@ -11613,6 +11613,7 @@ function computeHistoryDedupKey(type, tokenId, transferId) {
11613
11613
  }
11614
11614
  var MAX_SYNCED_HISTORY_ENTRIES = 5e3;
11615
11615
  var MAX_HISTORY_HYDRATION_PAGES = 100;
11616
+ var MAX_PR_HYDRATION_PAGES = 100;
11616
11617
  var SEND_ENGINE_OP_TIMEOUT_MS = 6e4;
11617
11618
  var DELIVERY_POLL_INTERVAL_MS = 3e4;
11618
11619
  function enrichWithRegistry(info) {
@@ -11940,10 +11941,13 @@ var PaymentsModule = class _PaymentsModule {
11940
11941
  /** Coalesces concurrent payment-request pump runs. */
11941
11942
  prPumpInFlight = null;
11942
11943
  /**
11943
- * Set after the once-per-session `status=open` bootstrap scan: the surfaced
11944
- * incoming list is in-memory only, so still-open requests BELOW the
11945
- * persisted cursor must be recovered at session start (bounded by the §5.5
11946
- * per-payer open cap).
11944
+ * Set after the once-per-session full incoming hydration (#556): the surfaced
11945
+ * incoming list is in-memory only, so on a fresh engine the CURRENT state of
11946
+ * ALL incoming requests — open AND resolved (paid/declined/expired) — must be
11947
+ * rebuilt from a `role=incoming&since=0` pull, not just the still-open ones.
11948
+ * A status-filtered bootstrap (the pre-#556 `status=open` scan) dropped
11949
+ * requests resolved in a PRIOR session, so the payer reopened and the
11950
+ * 'Paid Successfully' request was gone (twin of #521/#549).
11947
11951
  */
11948
11952
  prBootstrapped = false;
11949
11953
  // Guard: ensure load() completes before processing incoming bundles
@@ -14792,22 +14796,28 @@ var PaymentsModule = class _PaymentsModule {
14792
14796
  * mailbox-cursor pattern: the persisted `{cursor, syncEpoch}` is the resume
14793
14797
  * point; a `syncEpoch` change (server restore — §5.4) voids cursor
14794
14798
  * continuity, so the tail re-pulls from 0 and the id-dedup in
14795
- * {@link surfaceIncomingPaymentRequest} absorbs the replays. Because the
14796
- * surfaced list is in-memory only, each session ALSO starts with one
14797
- * `status=open` bootstrap scan from 0 — still-open requests below the
14798
- * cursor (bounded by the §5.5 per-payer open cap) are recovered; resolved
14799
- * ones are not re-surfaced.
14799
+ * {@link surfaceIncomingPaymentRequest} absorbs the replays.
14800
+ *
14801
+ * Because the surfaced list is in-memory only, each session FIRST runs one
14802
+ * full incoming hydration from `since=0` with NO status filter (#556 —
14803
+ * mirrors {@link hydrateHistoryFromServer}): a fresh engine rebuilds the
14804
+ * CURRENT state of ALL incoming requests — open AND resolved — so a request
14805
+ * paid/declined/expired in a PRIOR session is still present (with its
14806
+ * resolved status) instead of vanishing once the cursor advanced past it.
14807
+ * Hydration NEVER fires the new-incoming handlers/events for resolved
14808
+ * requests — only `open` ones notify (the status-aware
14809
+ * {@link surfaceIncomingPaymentRequest}) — so reopening can't spam stale
14810
+ * 'new request' notifications. After hydration the `since`-cursor delta poll
14811
+ * picks up live updates from the resume point.
14800
14812
  */
14801
14813
  async pumpIncomingPaymentRequests(api) {
14802
14814
  const persisted = await this.readPrCursorState();
14803
14815
  if (!this.prBootstrapped) {
14804
- if (persisted !== null && persisted.cursor > 0n) {
14805
- let scan = await api.listPaymentRequests({ role: "incoming", status: "open", since: 0n });
14806
- for (; ; ) {
14807
- for (const wire of scan.requests) this.surfaceIncomingPaymentRequest(wire);
14808
- if (!scan.more) break;
14809
- scan = await api.listPaymentRequests({ role: "incoming", status: "open", since: scan.cursor });
14810
- }
14816
+ let scan = await api.listPaymentRequests({ role: "incoming", since: 0n });
14817
+ for (let pageCount = 0; pageCount < MAX_PR_HYDRATION_PAGES; pageCount++) {
14818
+ for (const wire of scan.requests) this.surfaceIncomingPaymentRequest(wire);
14819
+ if (!scan.more) break;
14820
+ scan = await api.listPaymentRequests({ role: "incoming", since: scan.cursor });
14811
14821
  }
14812
14822
  this.prBootstrapped = true;
14813
14823
  }
@@ -14841,17 +14851,23 @@ var PaymentsModule = class _PaymentsModule {
14841
14851
  return {};
14842
14852
  }
14843
14853
  }
14854
+ /** §16 wire status → the public {@link PaymentRequestStatus} display status. */
14855
+ static PR_WIRE_STATUS = { open: "pending", paid: "paid", declined: "rejected", expired: "expired" };
14844
14856
  /**
14845
14857
  * Map a §16 wire request onto the public {@link IncomingPaymentRequest}
14846
- * surface and notify (event + handlers). Only `open` requests are
14847
- * actionable; the in-memory id-dedup doubles as the replay guard for
14848
- * cursor resets. Multi-asset requests surface their first asset (the
14849
- * module's request surface is single-asset; module-created requests
14850
- * always are).
14858
+ * surface, deduped by id (the in-memory id-dedup doubles as the replay guard
14859
+ * for cursor resets). Requests of EVERY status are surfaced so a reloaded
14860
+ * thin wallet rebuilds the CURRENT state of its incoming view (#556) — open
14861
+ * ones land as actionable `pending`, resolved ones carry their paid/declined
14862
+ * (→ `rejected`)/expired status. Only `open` requests fire the new-incoming
14863
+ * event + handlers; resolved requests are folded into the list silently, so a
14864
+ * reload (or a `syncEpoch` re-pull) never re-notifies for already-resolved
14865
+ * requests. Multi-asset requests surface their first asset (the module's
14866
+ * request surface is single-asset; module-created requests always are).
14851
14867
  */
14852
14868
  surfaceIncomingPaymentRequest(wire) {
14853
- if (wire.status !== "open") return;
14854
14869
  if (this.paymentRequests.some((r) => r.id === wire.id)) return;
14870
+ const status = _PaymentsModule.PR_WIRE_STATUS[wire.status];
14855
14871
  const coinId = wire.assets[0]?.coinId ?? "";
14856
14872
  const coinDef = TokenRegistry.getInstance().getDefinition(coinId);
14857
14873
  const { memo: message, senderNametag } = this.decryptPaymentRequestMemo(wire);
@@ -14865,18 +14881,20 @@ var PaymentsModule = class _PaymentsModule {
14865
14881
  ...message !== void 0 ? { message } : {},
14866
14882
  requestId: wire.id,
14867
14883
  timestamp: wire.createdAt,
14868
- status: "pending"
14884
+ status
14869
14885
  };
14870
14886
  this.paymentRequests.unshift(request);
14871
- this.deps?.emitEvent("payment_request:incoming", request);
14872
- for (const handler of this.paymentRequestHandlers) {
14873
- try {
14874
- handler(request);
14875
- } catch (error) {
14876
- logger.debug("Payments", "Payment request handler error:", error);
14887
+ if (status === "pending") {
14888
+ this.deps?.emitEvent("payment_request:incoming", request);
14889
+ for (const handler of this.paymentRequestHandlers) {
14890
+ try {
14891
+ handler(request);
14892
+ } catch (error) {
14893
+ logger.debug("Payments", "Payment request handler error:", error);
14894
+ }
14877
14895
  }
14878
14896
  }
14879
- logger.debug("Payments", `Incoming payment request: ${request.id} for ${request.amount} ${request.symbol}`);
14897
+ logger.debug("Payments", `Incoming payment request: ${request.id} (${status}) for ${request.amount} ${request.symbol}`);
14880
14898
  }
14881
14899
  /**
14882
14900
  * Outgoing requests are a `?before=` backfill view (§16 — newest-first, no