@pollar/core 0.8.0 → 0.8.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.
@@ -1,7 +1,8 @@
1
1
  import { KeyManager, PublicEcJwk } from './index.mjs';
2
- export { AUTH_ERROR_CODES, AdapterFn, AlbedoAdapter, AuthErrorCode, AuthState, BuildOutcome, BuildProofArgs, ConnectWalletResponse, DistributionClaimBody, DistributionClaimContent, DistributionRule, DistributionRulesState, FreighterAdapter, KycFlow, KycLevel, KycProvider, KycStartBody, KycStartResponse, KycStatus, LocalStorageAdapterOptions, NetworkState, PaymentInstructions, PollarAdapter, PollarAdapters, PollarApiClient, PollarApplicationConfigContent, PollarApplicationConfigResponse, PollarClient, PollarClientConfig, PollarFlowError, PollarLoginOptions, PollarPersistedSession, PollarUserProfile, RampDirection, RampQuote, RampTxStatus, RampsOfframpBody, RampsOfframpResponse, RampsOnrampBody, RampsOnrampResponse, RampsQuoteQuery, RampsQuoteResponse, RampsTransactionResponse, RulePeriod, SessionInfo, SignAuthEntryOptions, SignAuthEntryResponse, SignOutcome, SignTransactionOptions, SignTransactionResponse, StellarBalance, StellarClient, StellarClientConfig, StellarNetwork, SubmitOutcome, TransactionState, TxBuildBody, TxBuildContent, TxBuildResponse, TxBuildSignSubmitBody, TxBuildSignSubmitContent, TxBuildSignSubmitResponse, TxErrorPhase, TxHistoryContent, TxHistoryParams, TxHistoryRecord, TxHistoryState, TxSignAndSendBody, TxSignBody, TxSignContent, TxSignResponse, TxSignSendResponse, TxSubmitSignedBody, WalletAdapter, WalletAdapterResolver, WalletBalanceContent, WalletBalanceRecord, WalletBalanceState, WalletId, WalletType, WebCryptoKeyManager, buildProof, canonicalEcJwk, claimDistributionRule, computeJwkThumbprint, createLocalStorageAdapter, createMemoryAdapter, createOffRamp, createOnRamp, defaultKeyManager, defaultStorage, getKycProviders, getKycStatus, getRampTransaction, getRampsQuote, isValidSession, listDistributionRules, normalizeHtu, pollKycStatus, pollRampTransaction, pollarPaths, resolveKyc, startKyc } from './index.mjs';
2
+ export { AUTH_ERROR_CODES, AdapterFn, AlbedoAdapter, AuthErrorCode, AuthOpenContext, AuthState, AuthUrlOpener, BuildOutcome, BuildProofArgs, ConnectWalletResponse, DistributionClaimBody, DistributionClaimContent, DistributionRule, DistributionRulesState, FreighterAdapter, KycFlow, KycLevel, KycProvider, KycStartBody, KycStartResponse, KycStatus, LocalStorageAdapterOptions, NetworkState, PaymentInstructions, PollarAdapter, PollarAdapters, PollarApiClient, PollarApplicationConfigContent, PollarApplicationConfigResponse, PollarClient, PollarClientConfig, PollarFlowError, PollarLoginOptions, PollarPersistedSession, PollarUserProfile, RampDirection, RampQuote, RampTxStatus, RampsOfframpBody, RampsOfframpResponse, RampsOnrampBody, RampsOnrampResponse, RampsQuoteQuery, RampsQuoteResponse, RampsTransactionResponse, RulePeriod, SessionInfo, SignAuthEntryOptions, SignAuthEntryResponse, SignOutcome, SignTransactionOptions, SignTransactionResponse, StellarBalance, StellarClient, StellarClientConfig, StellarNetwork, SubmitOutcome, TransactionState, TxBuildBody, TxBuildContent, TxBuildResponse, TxBuildSignSubmitBody, TxBuildSignSubmitContent, TxBuildSignSubmitResponse, TxErrorPhase, TxHistoryContent, TxHistoryParams, TxHistoryRecord, TxHistoryState, TxSignAndSendBody, TxSignBody, TxSignContent, TxSignResponse, TxSignSendResponse, TxSubmitSignedBody, WalletAdapter, WalletAdapterResolver, WalletBalanceContent, WalletBalanceRecord, WalletBalanceState, WalletId, WalletType, WebCryptoKeyManager, buildProof, canonicalEcJwk, claimDistributionRule, computeJwkThumbprint, createLocalStorageAdapter, createMemoryAdapter, createOffRamp, createOnRamp, defaultKeyManager, defaultStorage, getKycProviders, getKycStatus, getRampTransaction, getRampsQuote, isValidSession, listDistributionRules, normalizeHtu, pollKycStatus, pollRampTransaction, pollarPaths, resolveKyc, startKyc } from './index.mjs';
3
3
  import { S as Storage } from './types-DqgJIJBl.mjs';
4
4
  export { O as OnStorageDegrade, a as StorageDegradeReason } from './types-DqgJIJBl.mjs';
5
+ import './types-84G_htcn.mjs';
5
6
  import 'openapi-fetch';
6
7
 
7
8
  declare class NobleKeyManager implements KeyManager {
@@ -1,7 +1,8 @@
1
1
  import { KeyManager, PublicEcJwk } from './index.js';
2
- export { AUTH_ERROR_CODES, AdapterFn, AlbedoAdapter, AuthErrorCode, AuthState, BuildOutcome, BuildProofArgs, ConnectWalletResponse, DistributionClaimBody, DistributionClaimContent, DistributionRule, DistributionRulesState, FreighterAdapter, KycFlow, KycLevel, KycProvider, KycStartBody, KycStartResponse, KycStatus, LocalStorageAdapterOptions, NetworkState, PaymentInstructions, PollarAdapter, PollarAdapters, PollarApiClient, PollarApplicationConfigContent, PollarApplicationConfigResponse, PollarClient, PollarClientConfig, PollarFlowError, PollarLoginOptions, PollarPersistedSession, PollarUserProfile, RampDirection, RampQuote, RampTxStatus, RampsOfframpBody, RampsOfframpResponse, RampsOnrampBody, RampsOnrampResponse, RampsQuoteQuery, RampsQuoteResponse, RampsTransactionResponse, RulePeriod, SessionInfo, SignAuthEntryOptions, SignAuthEntryResponse, SignOutcome, SignTransactionOptions, SignTransactionResponse, StellarBalance, StellarClient, StellarClientConfig, StellarNetwork, SubmitOutcome, TransactionState, TxBuildBody, TxBuildContent, TxBuildResponse, TxBuildSignSubmitBody, TxBuildSignSubmitContent, TxBuildSignSubmitResponse, TxErrorPhase, TxHistoryContent, TxHistoryParams, TxHistoryRecord, TxHistoryState, TxSignAndSendBody, TxSignBody, TxSignContent, TxSignResponse, TxSignSendResponse, TxSubmitSignedBody, WalletAdapter, WalletAdapterResolver, WalletBalanceContent, WalletBalanceRecord, WalletBalanceState, WalletId, WalletType, WebCryptoKeyManager, buildProof, canonicalEcJwk, claimDistributionRule, computeJwkThumbprint, createLocalStorageAdapter, createMemoryAdapter, createOffRamp, createOnRamp, defaultKeyManager, defaultStorage, getKycProviders, getKycStatus, getRampTransaction, getRampsQuote, isValidSession, listDistributionRules, normalizeHtu, pollKycStatus, pollRampTransaction, pollarPaths, resolveKyc, startKyc } from './index.js';
2
+ export { AUTH_ERROR_CODES, AdapterFn, AlbedoAdapter, AuthErrorCode, AuthOpenContext, AuthState, AuthUrlOpener, BuildOutcome, BuildProofArgs, ConnectWalletResponse, DistributionClaimBody, DistributionClaimContent, DistributionRule, DistributionRulesState, FreighterAdapter, KycFlow, KycLevel, KycProvider, KycStartBody, KycStartResponse, KycStatus, LocalStorageAdapterOptions, NetworkState, PaymentInstructions, PollarAdapter, PollarAdapters, PollarApiClient, PollarApplicationConfigContent, PollarApplicationConfigResponse, PollarClient, PollarClientConfig, PollarFlowError, PollarLoginOptions, PollarPersistedSession, PollarUserProfile, RampDirection, RampQuote, RampTxStatus, RampsOfframpBody, RampsOfframpResponse, RampsOnrampBody, RampsOnrampResponse, RampsQuoteQuery, RampsQuoteResponse, RampsTransactionResponse, RulePeriod, SessionInfo, SignAuthEntryOptions, SignAuthEntryResponse, SignOutcome, SignTransactionOptions, SignTransactionResponse, StellarBalance, StellarClient, StellarClientConfig, StellarNetwork, SubmitOutcome, TransactionState, TxBuildBody, TxBuildContent, TxBuildResponse, TxBuildSignSubmitBody, TxBuildSignSubmitContent, TxBuildSignSubmitResponse, TxErrorPhase, TxHistoryContent, TxHistoryParams, TxHistoryRecord, TxHistoryState, TxSignAndSendBody, TxSignBody, TxSignContent, TxSignResponse, TxSignSendResponse, TxSubmitSignedBody, WalletAdapter, WalletAdapterResolver, WalletBalanceContent, WalletBalanceRecord, WalletBalanceState, WalletId, WalletType, WebCryptoKeyManager, buildProof, canonicalEcJwk, claimDistributionRule, computeJwkThumbprint, createLocalStorageAdapter, createMemoryAdapter, createOffRamp, createOnRamp, defaultKeyManager, defaultStorage, getKycProviders, getKycStatus, getRampTransaction, getRampsQuote, isValidSession, listDistributionRules, normalizeHtu, pollKycStatus, pollRampTransaction, pollarPaths, resolveKyc, startKyc } from './index.js';
3
3
  import { S as Storage } from './types-DqgJIJBl.js';
4
4
  export { O as OnStorageDegrade, a as StorageDegradeReason } from './types-DqgJIJBl.js';
5
+ import './types-84G_htcn.js';
5
6
  import 'openapi-fetch';
6
7
 
7
8
  declare class NobleKeyManager implements KeyManager {
package/dist/index.rn.js CHANGED
@@ -1108,6 +1108,26 @@ async function pollRampTransaction(api, txId, { intervalMs = 5e3, timeoutMs = 6e
1108
1108
  throw new Error("Ramp transaction polling timed out");
1109
1109
  }
1110
1110
 
1111
+ // src/lib/random-uuid.ts
1112
+ function randomUUID() {
1113
+ const c = globalThis.crypto;
1114
+ if (c && typeof c.randomUUID === "function") {
1115
+ return c.randomUUID();
1116
+ }
1117
+ if (c && typeof c.getRandomValues === "function") {
1118
+ const bytes = new Uint8Array(16);
1119
+ c.getRandomValues(bytes);
1120
+ bytes[6] = bytes[6] & 15 | 64;
1121
+ bytes[8] = bytes[8] & 63 | 128;
1122
+ const hex = [];
1123
+ for (let i = 0; i < 16; i++) hex.push(bytes[i].toString(16).padStart(2, "0"));
1124
+ return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
1125
+ }
1126
+ throw new Error(
1127
+ "[PollarClient] No secure random source available (crypto.randomUUID / crypto.getRandomValues). DPoP requires a secure context (HTTPS) or, in React Native, the `react-native-get-random-values` polyfill."
1128
+ );
1129
+ }
1130
+
1111
1131
  // src/dpop.ts
1112
1132
  async function buildProof(args, keyManager) {
1113
1133
  const jwk = await keyManager.getPublicJwk();
@@ -1117,7 +1137,7 @@ async function buildProof(args, keyManager) {
1117
1137
  jwk
1118
1138
  };
1119
1139
  const payload = {
1120
- jti: generateJti(),
1140
+ jti: randomUUID(),
1121
1141
  htm: args.htm.toUpperCase(),
1122
1142
  htu: normalizeHtu(args.htu),
1123
1143
  iat: Math.floor(Date.now() / 1e3)
@@ -1151,24 +1171,6 @@ function normalizeHtu(rawUrl) {
1151
1171
  const portPart = port ? `:${port}` : "";
1152
1172
  return `${scheme}//${host}${portPart}${url.pathname}`;
1153
1173
  }
1154
- function generateJti() {
1155
- const c = globalThis.crypto;
1156
- if (c && typeof c.randomUUID === "function") {
1157
- return c.randomUUID();
1158
- }
1159
- if (c && typeof c.getRandomValues === "function") {
1160
- const bytes = new Uint8Array(16);
1161
- c.getRandomValues(bytes);
1162
- bytes[6] = bytes[6] & 15 | 64;
1163
- bytes[8] = bytes[8] & 63 | 128;
1164
- const hex = [];
1165
- for (let i = 0; i < 16; i++) hex.push(bytes[i].toString(16).padStart(2, "0"));
1166
- return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
1167
- }
1168
- throw new Error(
1169
- "[PollarClient:dpop] No secure random source available (crypto.randomUUID / crypto.getRandomValues). DPoP requires a secure context (HTTPS) or, in React Native, the `react-native-get-random-values` polyfill."
1170
- );
1171
- }
1172
1174
 
1173
1175
  // src/storage/web.ts
1174
1176
  var LOG_PREFIX = "[PollarClient:storage]";
@@ -1311,6 +1313,8 @@ function defaultVisibilityProvider() {
1311
1313
  // src/types.ts
1312
1314
  var AUTH_ERROR_CODES = {
1313
1315
  SESSION_CREATE_FAILED: "SESSION_CREATE_FAILED",
1316
+ SESSION_EXPIRED: "SESSION_EXPIRED",
1317
+ SESSION_INVALID: "SESSION_INVALID",
1314
1318
  EMAIL_SEND_FAILED: "EMAIL_SEND_FAILED",
1315
1319
  EMAIL_VERIFY_FAILED: "EMAIL_VERIFY_FAILED",
1316
1320
  EMAIL_CODE_EXPIRED: "EMAIL_CODE_EXPIRED",
@@ -1624,7 +1628,32 @@ async function readWalletType(storage, apiKeyHash) {
1624
1628
  return storage.get(walletTypeStorageKey(apiKeyHash));
1625
1629
  }
1626
1630
 
1631
+ // src/lib/abort.ts
1632
+ function abortError() {
1633
+ if (typeof DOMException !== "undefined") {
1634
+ return new DOMException("Aborted", "AbortError");
1635
+ }
1636
+ const err = new Error("Aborted");
1637
+ err.name = "AbortError";
1638
+ return err;
1639
+ }
1640
+ function throwIfAborted(signal) {
1641
+ if (signal?.aborted) throw abortError();
1642
+ }
1643
+
1627
1644
  // src/client/stream.ts
1645
+ var SessionStatusError = class extends Error {
1646
+ constructor(code) {
1647
+ super(`[PollarClient] Session status terminal: ${code}`);
1648
+ this.code = code;
1649
+ this.name = "SessionStatusError";
1650
+ }
1651
+ };
1652
+ function terminalStatusCode(parsed) {
1653
+ const err = parsed?.error;
1654
+ if (err === "INVALID_CLIENT_SESSION_ID" || err === "EXPIRED_CLIENT_ID") return err;
1655
+ return null;
1656
+ }
1628
1657
  function abortableDelay(ms, signal) {
1629
1658
  return new Promise((resolve, reject) => {
1630
1659
  const t = setTimeout(resolve, ms);
@@ -1632,7 +1661,7 @@ function abortableDelay(ms, signal) {
1632
1661
  "abort",
1633
1662
  () => {
1634
1663
  clearTimeout(t);
1635
- reject(new DOMException("Aborted", "AbortError"));
1664
+ reject(abortError());
1636
1665
  },
1637
1666
  { once: true }
1638
1667
  );
@@ -1647,7 +1676,7 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
1647
1676
  else await new Promise((r) => setTimeout(r, ms));
1648
1677
  };
1649
1678
  while (true) {
1650
- signal?.throwIfAborted();
1679
+ throwIfAborted(signal);
1651
1680
  let data, error;
1652
1681
  try {
1653
1682
  ({ data, error } = await api.GET("/auth/session/status/{clientSessionId}", {
@@ -1670,7 +1699,7 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
1670
1699
  let sawAnyChunk = false;
1671
1700
  try {
1672
1701
  while (true) {
1673
- signal?.throwIfAborted();
1702
+ throwIfAborted(signal);
1674
1703
  const { done, value } = await reader.read();
1675
1704
  if (done) {
1676
1705
  streamDone = true;
@@ -1681,17 +1710,22 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
1681
1710
  for (const message of chunk.split("\n\n").filter(Boolean)) {
1682
1711
  const dataLine = message.split("\n").find((l) => l.startsWith("data:"));
1683
1712
  if (!dataLine) continue;
1713
+ let parsed;
1684
1714
  try {
1685
- const parsed = JSON.parse(dataLine.slice("data:".length).trim());
1686
- if (check(parsed)) {
1687
- return parsed;
1688
- }
1715
+ parsed = JSON.parse(dataLine.slice("data:".length).trim());
1689
1716
  } catch {
1717
+ continue;
1718
+ }
1719
+ const terminal = terminalStatusCode(parsed);
1720
+ if (terminal) throw new SessionStatusError(terminal);
1721
+ if (check(parsed)) {
1722
+ return parsed;
1690
1723
  }
1691
1724
  }
1692
1725
  }
1693
1726
  } catch (e) {
1694
1727
  if (e instanceof Error && e.name === "AbortError") throw e;
1728
+ if (e instanceof SessionStatusError) throw e;
1695
1729
  console.warn(e);
1696
1730
  } finally {
1697
1731
  reader.releaseLock();
@@ -1702,12 +1736,72 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
1702
1736
  if (delay) await sleep(delay);
1703
1737
  }
1704
1738
  }
1739
+ async function pollUntilFound(baseUrl, clientSessionId, check, intervalMs = 500, signal) {
1740
+ const url = `${baseUrl}/auth/session/status/${encodeURIComponent(clientSessionId)}/poll`;
1741
+ let backoff = intervalMs;
1742
+ const sleep = async (ms) => {
1743
+ if (ms <= 0) return;
1744
+ if (signal) await abortableDelay(ms, signal);
1745
+ else await new Promise((r) => setTimeout(r, ms));
1746
+ };
1747
+ while (true) {
1748
+ throwIfAborted(signal);
1749
+ let envelope = null;
1750
+ let httpStatus = 0;
1751
+ try {
1752
+ const response = await fetch(url, { headers: { accept: "application/json" }, signal: signal ?? null });
1753
+ httpStatus = response.status;
1754
+ envelope = await response.json().catch(() => null);
1755
+ } catch (e) {
1756
+ if (e instanceof Error && e.name === "AbortError") throw e;
1757
+ console.warn(e);
1758
+ }
1759
+ if (httpStatus === 404 || envelope?.code === "INVALID_CLIENT_SESSION_ID") {
1760
+ throw new SessionStatusError("INVALID_CLIENT_SESSION_ID");
1761
+ }
1762
+ if (httpStatus === 410 || envelope?.code === "EXPIRED_CLIENT_ID") {
1763
+ throw new SessionStatusError("EXPIRED_CLIENT_ID");
1764
+ }
1765
+ if (envelope?.success && envelope.content && check(envelope.content)) {
1766
+ return envelope.content;
1767
+ }
1768
+ if (envelope) backoff = intervalMs;
1769
+ else backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);
1770
+ await sleep(backoff);
1771
+ }
1772
+ }
1773
+ function waitForSessionReady(args) {
1774
+ const { api, baseUrl, clientSessionId, check, useStreaming, retryDelayMs, signal } = args;
1775
+ return useStreaming ? streamUntilFound(api, clientSessionId, check, retryDelayMs ?? 200, signal) : pollUntilFound(baseUrl, clientSessionId, check, retryDelayMs ?? 500, signal);
1776
+ }
1705
1777
 
1706
1778
  // src/client/auth/authenticate.ts
1707
1779
  async function authenticate(clientSessionId, deps, expectedWallet) {
1708
- const { api, signal, setAuthState, storeSession, clearSession } = deps;
1780
+ const { api, basePath, useStreaming, signal, setAuthState, storeSession, clearSession } = deps;
1709
1781
  setAuthState({ step: "authenticating" });
1710
- await streamUntilFound(api, clientSessionId, (data2) => data2?.status === "READY", 200, signal);
1782
+ try {
1783
+ await waitForSessionReady({
1784
+ api,
1785
+ baseUrl: basePath,
1786
+ clientSessionId,
1787
+ check: (data2) => data2?.status === "READY",
1788
+ useStreaming,
1789
+ signal
1790
+ });
1791
+ } catch (err) {
1792
+ if (err instanceof SessionStatusError) {
1793
+ const expired = err.code === "EXPIRED_CLIENT_ID";
1794
+ setAuthState({
1795
+ step: "error",
1796
+ previousStep: "authenticating",
1797
+ message: expired ? "Login session expired \u2014 please try again" : "Login session is no longer valid \u2014 please try again",
1798
+ errorCode: expired ? AUTH_ERROR_CODES.SESSION_EXPIRED : AUTH_ERROR_CODES.SESSION_INVALID
1799
+ });
1800
+ await clearSession();
1801
+ return;
1802
+ }
1803
+ throw err;
1804
+ }
1711
1805
  const dpopJwk = await deps.getPublicJwk();
1712
1806
  const { data } = await api.POST("/auth/login", {
1713
1807
  body: {
@@ -1831,26 +1925,36 @@ function severOpener(popup) {
1831
1925
  } catch {
1832
1926
  }
1833
1927
  }
1834
- async function loginOAuth(provider, deps) {
1835
- const { setAuthState, basePath, apiKey } = deps;
1836
- const popup = window.open("about:blank", "_blank");
1928
+ var defaultWebOAuthOpener = async ({ getUrl }) => {
1929
+ const popup = typeof window !== "undefined" ? window.open("about:blank", "_blank") : null;
1837
1930
  severOpener(popup);
1838
- const clientSessionId = await createAuthSession(deps);
1839
- if (!clientSessionId) {
1931
+ const url = await getUrl();
1932
+ if (!url) {
1840
1933
  popup?.close();
1841
1934
  return;
1842
1935
  }
1843
- setAuthState({ step: "opening_oauth", provider });
1844
- const url = new URL(`${basePath}/auth/${provider}`);
1845
- url.searchParams.set("api_key", apiKey);
1846
- url.searchParams.set("client_session_id", clientSessionId);
1847
- url.searchParams.set("redirect_uri", window.location.origin);
1848
1936
  if (popup) {
1849
- popup.location.href = url.toString();
1937
+ popup.location.href = url;
1850
1938
  severOpener(popup);
1851
- } else {
1852
- window.open(url.toString(), "_blank", "noopener,noreferrer");
1939
+ } else if (typeof window !== "undefined") {
1940
+ window.open(url, "_blank", "noopener,noreferrer");
1853
1941
  }
1942
+ };
1943
+ async function loginOAuth(provider, deps) {
1944
+ const { setAuthState, basePath, apiKey, openAuthUrl, redirectUri, signal } = deps;
1945
+ let clientSessionId = null;
1946
+ const getUrl = async () => {
1947
+ clientSessionId = await createAuthSession(deps);
1948
+ if (!clientSessionId) return null;
1949
+ setAuthState({ step: "opening_oauth", provider });
1950
+ const url = new URL(`${basePath}/auth/${provider}`);
1951
+ url.searchParams.set("api_key", apiKey);
1952
+ url.searchParams.set("client_session_id", clientSessionId);
1953
+ url.searchParams.set("redirect_uri", redirectUri);
1954
+ return url.toString();
1955
+ };
1956
+ await openAuthUrl({ provider, getUrl, redirectUri, signal });
1957
+ if (!clientSessionId) return;
1854
1958
  await authenticate(clientSessionId, deps);
1855
1959
  }
1856
1960
 
@@ -1860,10 +1964,10 @@ function withSignal(promise, signal) {
1860
1964
  promise,
1861
1965
  new Promise((_, reject) => {
1862
1966
  if (signal.aborted) {
1863
- reject(new DOMException("Aborted", "AbortError"));
1967
+ reject(abortError());
1864
1968
  return;
1865
1969
  }
1866
- signal.addEventListener("abort", () => reject(new DOMException("Aborted", "AbortError")), { once: true });
1970
+ signal.addEventListener("abort", () => reject(abortError()), { once: true });
1867
1971
  })
1868
1972
  ]);
1869
1973
  }
@@ -1911,6 +2015,8 @@ async function loginWallet(type, deps) {
1911
2015
 
1912
2016
  // src/client/client.ts
1913
2017
  var isBrowser = typeof window !== "undefined" && typeof localStorage !== "undefined";
2018
+ var isReactNative = typeof navigator !== "undefined" && navigator.product === "ReactNative";
2019
+ var isClientRuntime = isBrowser || isReactNative;
1914
2020
  var REFRESH_SKEW_SECONDS = 60;
1915
2021
  function warnServerSide(method) {
1916
2022
  console.warn(
@@ -1966,7 +2072,7 @@ var PollarClient = class {
1966
2072
  this._walletAdapter = null;
1967
2073
  this._loginController = null;
1968
2074
  this.apiKey = config.apiKey;
1969
- this.id = crypto.randomUUID();
2075
+ this.id = randomUUID();
1970
2076
  this.basePath = `${config.baseUrl || "https://sdk.api.pollar.xyz"}/v1`;
1971
2077
  this._storage = config.storage ?? defaultStorage({
1972
2078
  onDegrade: (reason, error) => {
@@ -1980,10 +2086,12 @@ var PollarClient = class {
1980
2086
  this._deviceLabel = config.deviceLabel;
1981
2087
  this._visibilityProvider = config.visibilityProvider ?? defaultVisibilityProvider();
1982
2088
  this._maxIdleMs = config.maxIdleMs;
2089
+ this._openAuthUrl = config.openAuthUrl ?? defaultWebOAuthOpener;
2090
+ this._oauthRedirectUri = config.oauthRedirectUri ?? (isBrowser ? window.location.origin : "");
1983
2091
  this._api = createApiClient(this.basePath);
1984
2092
  this._wireMiddlewares();
1985
2093
  this._networkState = { step: "connected", network: config.stellarNetwork ?? "testnet" };
1986
- if (!isBrowser) {
2094
+ if (!isClientRuntime) {
1987
2095
  warnServerSide("constructor");
1988
2096
  this._initialized = Promise.resolve();
1989
2097
  return;
@@ -2009,7 +2117,7 @@ var PollarClient = class {
2009
2117
  // ─── Lifecycle ────────────────────────────────────────────────────────────
2010
2118
  async _initialize() {
2011
2119
  this._apiKeyHash = await hashApiKey(this.apiKey);
2012
- if (typeof window !== "undefined") {
2120
+ if (isBrowser) {
2013
2121
  const sessionKey = sessionStorageKey(this._apiKeyHash);
2014
2122
  const handler = (e) => {
2015
2123
  if (e.key === sessionKey) {
@@ -2031,7 +2139,7 @@ var PollarClient = class {
2031
2139
  }
2032
2140
  /** Detach the cross-tab storage listener and abort any in-flight login. */
2033
2141
  destroy() {
2034
- if (this._storageEventHandler && typeof window !== "undefined") {
2142
+ if (this._storageEventHandler && isBrowser) {
2035
2143
  window.removeEventListener("storage", this._storageEventHandler);
2036
2144
  this._storageEventHandler = null;
2037
2145
  }
@@ -2314,7 +2422,7 @@ var PollarClient = class {
2314
2422
  }
2315
2423
  // ─── Login (unified entry point) ─────────────────────────────────────────
2316
2424
  login(options) {
2317
- if (!isBrowser) {
2425
+ if (!isClientRuntime) {
2318
2426
  warnServerSide("login");
2319
2427
  return;
2320
2428
  }
@@ -2325,7 +2433,9 @@ var PollarClient = class {
2325
2433
  loginOAuth(options.provider, {
2326
2434
  ...deps,
2327
2435
  basePath: this.basePath,
2328
- apiKey: this.apiKey
2436
+ apiKey: this.apiKey,
2437
+ openAuthUrl: this._openAuthUrl,
2438
+ redirectUri: this._oauthRedirectUri
2329
2439
  }).catch((err) => this._handleFlowError(err));
2330
2440
  } else if (options.provider === "email") {
2331
2441
  const { email } = options;
@@ -2341,7 +2451,7 @@ var PollarClient = class {
2341
2451
  }
2342
2452
  // ─── Email OTP flow (3 steps) ─────────────────────────────────────────────
2343
2453
  beginEmailLogin() {
2344
- if (!isBrowser) {
2454
+ if (!isClientRuntime) {
2345
2455
  warnServerSide("beginEmailLogin");
2346
2456
  return;
2347
2457
  }
@@ -2349,7 +2459,7 @@ var PollarClient = class {
2349
2459
  initEmailSession(this._flowDeps(controller.signal)).catch((err) => this._handleFlowError(err));
2350
2460
  }
2351
2461
  sendEmailCode(email) {
2352
- if (!isBrowser) {
2462
+ if (!isClientRuntime) {
2353
2463
  warnServerSide("sendEmailCode");
2354
2464
  return;
2355
2465
  }
@@ -2361,7 +2471,7 @@ var PollarClient = class {
2361
2471
  sendEmailCode(email, clientSessionId, this._flowDeps(signal)).catch((err) => this._handleFlowError(err));
2362
2472
  }
2363
2473
  verifyEmailCode(code) {
2364
- if (!isBrowser) {
2474
+ if (!isClientRuntime) {
2365
2475
  warnServerSide("verifyEmailCode");
2366
2476
  return;
2367
2477
  }
@@ -2379,7 +2489,7 @@ var PollarClient = class {
2379
2489
  }
2380
2490
  // ─── Wallet flow (single call) ────────────────────────────────────────────
2381
2491
  loginWallet(type) {
2382
- if (!isBrowser) {
2492
+ if (!isClientRuntime) {
2383
2493
  warnServerSide("loginWallet");
2384
2494
  return;
2385
2495
  }
@@ -2405,7 +2515,7 @@ var PollarClient = class {
2405
2515
  * across all devices.
2406
2516
  */
2407
2517
  async logout(options = {}) {
2408
- if (!isBrowser) {
2518
+ if (!isClientRuntime) {
2409
2519
  warnServerSide("logout");
2410
2520
  return;
2411
2521
  }
@@ -2435,7 +2545,7 @@ var PollarClient = class {
2435
2545
  * `current` flag identifies which entry corresponds to this client.
2436
2546
  */
2437
2547
  async listSessions() {
2438
- if (!isBrowser) {
2548
+ if (!isClientRuntime) {
2439
2549
  warnServerSide("listSessions");
2440
2550
  return [];
2441
2551
  }
@@ -2454,7 +2564,7 @@ var PollarClient = class {
2454
2564
  * does NOT clear local state — call `logout()` for that case.
2455
2565
  */
2456
2566
  async revokeSession(familyId) {
2457
- if (!isBrowser) {
2567
+ if (!isClientRuntime) {
2458
2568
  warnServerSide("revokeSession");
2459
2569
  return;
2460
2570
  }
@@ -2952,6 +3062,11 @@ var PollarClient = class {
2952
3062
  _flowDeps(signal) {
2953
3063
  return {
2954
3064
  api: this._api,
3065
+ basePath: this.basePath,
3066
+ // SSE status streaming works on web; React Native's `fetch` has no
3067
+ // readable `response.body`, so those clients poll the non-streaming
3068
+ // status endpoint instead. `isBrowser` is false in RN and SSR alike.
3069
+ useStreaming: isBrowser,
2955
3070
  signal,
2956
3071
  setAuthState: this._setAuthState.bind(this),
2957
3072
  storeSession: this._storeSession.bind(this),