@pollar/core 0.9.0-rc.0 → 0.9.0-rc.2

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.js CHANGED
@@ -341,7 +341,6 @@ var WebCryptoKeyManager = class {
341
341
  if (this.keyPair) return;
342
342
  if (!this._initPromise) {
343
343
  this._initPromise = this._doInit().catch((err) => {
344
- console.error("[PollarClient:keys] WebCryptoKeyManager init failed", err);
345
344
  this._initPromise = null;
346
345
  throw err;
347
346
  });
@@ -1076,6 +1075,16 @@ function normalizeHtu(rawUrl) {
1076
1075
  return `${scheme}//${host}${portPart}${url.pathname}`;
1077
1076
  }
1078
1077
 
1078
+ // src/lib/logger.ts
1079
+ var RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
1080
+ function createLogger(level = "info", sink = console) {
1081
+ const threshold = RANK[level];
1082
+ const gate = (lvl) => (...args) => {
1083
+ if (threshold >= RANK[lvl]) sink[lvl](...args);
1084
+ };
1085
+ return { error: gate("error"), warn: gate("warn"), info: gate("info"), debug: gate("debug") };
1086
+ }
1087
+
1079
1088
  // src/storage/web.ts
1080
1089
  var LOG_PREFIX = "[PollarClient:storage]";
1081
1090
  function createMemoryAdapter() {
@@ -1099,7 +1108,7 @@ function createLocalStorageAdapter(options = {}) {
1099
1108
  function degrade(reason, error) {
1100
1109
  if (degraded) return;
1101
1110
  degraded = true;
1102
- console.warn(`${LOG_PREFIX} localStorage unavailable (${reason}); degrading to in-memory storage`);
1111
+ (options.logger ?? console).warn(`${LOG_PREFIX} localStorage unavailable (${reason}); degrading to in-memory storage`);
1103
1112
  options.onDegrade?.(reason, error);
1104
1113
  }
1105
1114
  return {
@@ -1164,7 +1173,7 @@ function defaultStorage(options = {}) {
1164
1173
  }
1165
1174
 
1166
1175
  // src/version.ts
1167
- var POLLAR_CORE_VERSION = "0.9.0-rc.0" ;
1176
+ var POLLAR_CORE_VERSION = "0.9.0-rc.2" ;
1168
1177
 
1169
1178
  // src/visibility/noop.ts
1170
1179
  function createNoopVisibilityProvider() {
@@ -1336,7 +1345,10 @@ function openAlbedoPopup(url) {
1336
1345
  }
1337
1346
  function waitForAlbedoPopup() {
1338
1347
  return new Promise((resolve, reject) => {
1339
- const timeout = setTimeout(() => reject(new Error("Albedo response timeout")), 2 * 60 * 1e3);
1348
+ const timeout = setTimeout(() => {
1349
+ window.removeEventListener("message", handler);
1350
+ reject(new Error("Albedo response timeout"));
1351
+ }, 2 * 60 * 1e3);
1340
1352
  function handler(event) {
1341
1353
  if (event.origin !== window.location.origin || event.data?.type !== "ALBEDO_RESULT") return;
1342
1354
  clearTimeout(timeout);
@@ -1346,24 +1358,6 @@ function waitForAlbedoPopup() {
1346
1358
  window.addEventListener("message", handler);
1347
1359
  });
1348
1360
  }
1349
- function waitForAlbedoResult() {
1350
- return new Promise((resolve, reject) => {
1351
- const timeout = setTimeout(() => reject(new Error("Albedo response timeout")), 2 * 60 * 1e3);
1352
- const parseResult = () => {
1353
- const params = new URLSearchParams(window.location.search);
1354
- if (!params.has("pubkey") && !params.has("signed_envelope_xdr") && !params.has("signed_xdr")) return;
1355
- clearTimeout(timeout);
1356
- const result = {};
1357
- params.forEach((value, key) => {
1358
- result[key] = value;
1359
- });
1360
- window.history.replaceState({}, document.title, window.location.pathname);
1361
- resolve(result);
1362
- };
1363
- parseResult();
1364
- window.addEventListener("popstate", parseResult);
1365
- });
1366
- }
1367
1361
  var AlbedoAdapter = class {
1368
1362
  /**
1369
1363
  * Network used for `connect` and `signAuthEntry` (which carry no per-call
@@ -1405,10 +1399,10 @@ var AlbedoAdapter = class {
1405
1399
  url.searchParams.set("xdr", xdr);
1406
1400
  url.searchParams.set("app_name", "Pollar");
1407
1401
  url.searchParams.set("network", albedoNetwork(options, this.network));
1408
- url.searchParams.set("callback", window.location.href);
1402
+ url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
1409
1403
  url.searchParams.set("origin", window.location.origin);
1410
- window.location.href = url.toString();
1411
- const result = await waitForAlbedoResult();
1404
+ openAlbedoPopup(url.toString());
1405
+ const result = await waitForAlbedoPopup();
1412
1406
  if (!result.signed_envelope_xdr) throw new Error("Albedo signing rejected");
1413
1407
  return { signedTxXdr: result.signed_envelope_xdr };
1414
1408
  }
@@ -1418,10 +1412,10 @@ var AlbedoAdapter = class {
1418
1412
  url.searchParams.set("xdr", entryXdr);
1419
1413
  url.searchParams.set("app_name", "Pollar");
1420
1414
  url.searchParams.set("network", this.network);
1421
- url.searchParams.set("callback", window.location.href);
1415
+ url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
1422
1416
  url.searchParams.set("origin", window.location.origin);
1423
- window.location.href = url.toString();
1424
- const result = await waitForAlbedoResult();
1417
+ openAlbedoPopup(url.toString());
1418
+ const result = await waitForAlbedoPopup();
1425
1419
  if (!result.signed_xdr) throw new Error("Albedo auth entry signing rejected");
1426
1420
  return { signedAuthEntry: result.signed_xdr };
1427
1421
  }
@@ -1448,85 +1442,85 @@ function isBoundedString(v, max, allowEmpty = false) {
1448
1442
  if (!allowEmpty && v.length === 0) return false;
1449
1443
  return v.length <= max;
1450
1444
  }
1451
- function isValidSession(value) {
1445
+ function isValidSession(value, logger = console) {
1452
1446
  if (typeof value !== "object" || value === null) {
1453
- console.warn("[PollarClient:session] Invalid session \u2014 value is not an object");
1447
+ logger.debug("[PollarClient:session] Invalid session \u2014 value is not an object");
1454
1448
  return false;
1455
1449
  }
1456
1450
  const s = value;
1457
1451
  if (!isBoundedString(s["clientSessionId"], MAX_CLIENT_SESSION_ID)) {
1458
- console.warn("[PollarClient:session] Invalid session \u2014 clientSessionId missing/empty/too long");
1452
+ logger.debug("[PollarClient:session] Invalid session \u2014 clientSessionId missing/empty/too long");
1459
1453
  return false;
1460
1454
  }
1461
1455
  if (s["userId"] !== null && !isBoundedString(s["userId"], MAX_USER_ID)) {
1462
- console.warn("[PollarClient:session] Invalid session \u2014 userId must be string|null");
1456
+ logger.debug("[PollarClient:session] Invalid session \u2014 userId must be string|null");
1463
1457
  return false;
1464
1458
  }
1465
1459
  if (!isBoundedString(s["status"], MAX_STATUS)) {
1466
- console.warn("[PollarClient:session] Invalid session \u2014 status must be string");
1460
+ logger.debug("[PollarClient:session] Invalid session \u2014 status must be string");
1467
1461
  return false;
1468
1462
  }
1469
1463
  const token = s["token"];
1470
1464
  if (typeof token !== "object" || token === null) {
1471
- console.warn("[PollarClient:session] Invalid session \u2014 token missing or not an object");
1465
+ logger.debug("[PollarClient:session] Invalid session \u2014 token missing or not an object");
1472
1466
  return false;
1473
1467
  }
1474
1468
  const t = token;
1475
1469
  if (!isBoundedString(t["accessToken"], MAX_ACCESS_TOKEN)) {
1476
- console.warn("[PollarClient:session] Invalid session \u2014 token.accessToken missing/empty/too long");
1470
+ logger.debug("[PollarClient:session] Invalid session \u2014 token.accessToken missing/empty/too long");
1477
1471
  return false;
1478
1472
  }
1479
1473
  if (!isBoundedString(t["refreshToken"], MAX_REFRESH_TOKEN)) {
1480
- console.warn("[PollarClient:session] Invalid session \u2014 token.refreshToken missing/empty/too long");
1474
+ logger.debug("[PollarClient:session] Invalid session \u2014 token.refreshToken missing/empty/too long");
1481
1475
  return false;
1482
1476
  }
1483
1477
  if (typeof t["expiresAt"] !== "number" || !Number.isFinite(t["expiresAt"])) {
1484
- console.warn("[PollarClient:session] Invalid session \u2014 token.expiresAt must be a finite number");
1478
+ logger.debug("[PollarClient:session] Invalid session \u2014 token.expiresAt must be a finite number");
1485
1479
  return false;
1486
1480
  }
1487
1481
  const user = s["user"];
1488
1482
  if (typeof user !== "object" || user === null) {
1489
- console.warn("[PollarClient:session] Invalid session \u2014 user missing or not an object");
1483
+ logger.debug("[PollarClient:session] Invalid session \u2014 user missing or not an object");
1490
1484
  return false;
1491
1485
  }
1492
1486
  const u = user;
1493
1487
  if (u["id"] !== void 0 && !isBoundedString(u["id"], MAX_USER_ID)) {
1494
- console.warn("[PollarClient:session] Invalid session \u2014 user.id must be string if present");
1488
+ logger.debug("[PollarClient:session] Invalid session \u2014 user.id must be string if present");
1495
1489
  return false;
1496
1490
  }
1497
1491
  if (typeof u["ready"] !== "boolean") {
1498
- console.warn("[PollarClient:session] Invalid session \u2014 user.ready must be boolean");
1492
+ logger.debug("[PollarClient:session] Invalid session \u2014 user.ready must be boolean");
1499
1493
  return false;
1500
1494
  }
1501
1495
  const wallet = s["wallet"];
1502
1496
  if (typeof wallet !== "object" || wallet === null) {
1503
- console.warn("[PollarClient:session] Invalid session \u2014 wallet missing or not an object");
1497
+ logger.debug("[PollarClient:session] Invalid session \u2014 wallet missing or not an object");
1504
1498
  return false;
1505
1499
  }
1506
1500
  const w = wallet;
1507
- if (w["type"] !== "custodial" && w["type"] !== "smart" && w["type"] !== "external") {
1508
- console.warn("[PollarClient:session] Invalid session \u2014 wallet.type must be custodial|smart|external");
1501
+ if (w["type"] !== "internal" && w["type"] !== "smart" && w["type"] !== "external") {
1502
+ logger.debug("[PollarClient:session] Invalid session \u2014 wallet.type must be internal|smart|external");
1509
1503
  return false;
1510
1504
  }
1511
1505
  if (w["address"] !== null && !isBoundedString(w["address"], MAX_WALLET_PUBLIC_KEY)) {
1512
- console.warn("[PollarClient:session] Invalid session \u2014 wallet.address must be string|null");
1506
+ logger.debug("[PollarClient:session] Invalid session \u2014 wallet.address must be string|null");
1513
1507
  return false;
1514
1508
  }
1515
1509
  if (w["existsOnStellar"] !== void 0 && typeof w["existsOnStellar"] !== "boolean") {
1516
- console.warn("[PollarClient:session] Invalid session \u2014 wallet.existsOnStellar must be boolean if present");
1510
+ logger.debug("[PollarClient:session] Invalid session \u2014 wallet.existsOnStellar must be boolean if present");
1517
1511
  return false;
1518
1512
  }
1519
1513
  if (w["createdAt"] !== void 0 && (typeof w["createdAt"] !== "number" || !Number.isFinite(w["createdAt"]))) {
1520
- console.warn("[PollarClient:session] Invalid session \u2014 wallet.createdAt must be a finite number if present");
1514
+ logger.debug("[PollarClient:session] Invalid session \u2014 wallet.createdAt must be a finite number if present");
1521
1515
  return false;
1522
1516
  }
1523
1517
  if (w["linkedAt"] !== void 0 && (typeof w["linkedAt"] !== "number" || !Number.isFinite(w["linkedAt"]))) {
1524
- console.warn("[PollarClient:session] Invalid session \u2014 wallet.linkedAt must be a finite number if present");
1518
+ logger.debug("[PollarClient:session] Invalid session \u2014 wallet.linkedAt must be a finite number if present");
1525
1519
  return false;
1526
1520
  }
1527
1521
  return true;
1528
1522
  }
1529
- async function readStorage(storage, apiKeyHash) {
1523
+ async function readStorage(storage, apiKeyHash, logger = console) {
1530
1524
  const raw = await storage.get(sessionStorageKey(apiKeyHash));
1531
1525
  if (!raw) return null;
1532
1526
  try {
@@ -1536,10 +1530,13 @@ async function readStorage(storage, apiKeyHash) {
1536
1530
  if (w && w["address"] == null && typeof w["publicKey"] === "string") {
1537
1531
  w["address"] = w["publicKey"];
1538
1532
  }
1533
+ if (w && w["type"] === "custodial") {
1534
+ w["type"] = "internal";
1535
+ }
1539
1536
  }
1540
- if (!isValidSession(session)) {
1537
+ if (!isValidSession(session, logger)) {
1541
1538
  await storage.remove(sessionStorageKey(apiKeyHash));
1542
- console.warn("[PollarClient:session] Stored session is invalid \u2014 clearing storage");
1539
+ logger.warn("[PollarClient:session] Stored session is invalid \u2014 clearing storage");
1543
1540
  return null;
1544
1541
  }
1545
1542
  if (session.token.expiresAt * 1e3 < Date.now()) {
@@ -1547,7 +1544,7 @@ async function readStorage(storage, apiKeyHash) {
1547
1544
  }
1548
1545
  return session;
1549
1546
  } catch (error) {
1550
- console.error("[PollarClient:session] Failed to parse session from storage", error);
1547
+ logger.error("[PollarClient:session] Failed to parse session from storage", error);
1551
1548
  await storage.remove(sessionStorageKey(apiKeyHash));
1552
1549
  return null;
1553
1550
  }
@@ -1609,7 +1606,7 @@ function abortableDelay(ms, signal) {
1609
1606
  });
1610
1607
  }
1611
1608
  var MAX_BACKOFF_MS = 5e3;
1612
- async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200, signal) {
1609
+ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200, signal, logger = console) {
1613
1610
  let backoff = retryDelayMs;
1614
1611
  const sleep = async (ms) => {
1615
1612
  if (ms <= 0) return;
@@ -1627,7 +1624,7 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
1627
1624
  }));
1628
1625
  } catch (e) {
1629
1626
  if (e instanceof Error && e.name === "AbortError") throw e;
1630
- console.warn("[PollarClient:stream] session-status request failed; will retry", e);
1627
+ logger.debug("[PollarClient:stream] session-status request failed; will retry", e);
1631
1628
  }
1632
1629
  if (error || !data) {
1633
1630
  await sleep(backoff);
@@ -1636,16 +1633,12 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
1636
1633
  }
1637
1634
  const reader = data.getReader();
1638
1635
  const decoder = new TextDecoder();
1639
- let streamDone = false;
1640
1636
  let sawAnyChunk = false;
1641
1637
  try {
1642
1638
  while (true) {
1643
1639
  throwIfAborted(signal);
1644
1640
  const { done, value } = await reader.read();
1645
- if (done) {
1646
- streamDone = true;
1647
- break;
1648
- }
1641
+ if (done) break;
1649
1642
  sawAnyChunk = true;
1650
1643
  const chunk = decoder.decode(value);
1651
1644
  for (const message of chunk.split("\n\n").filter(Boolean)) {
@@ -1667,17 +1660,16 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
1667
1660
  } catch (e) {
1668
1661
  if (e instanceof Error && e.name === "AbortError") throw e;
1669
1662
  if (e instanceof SessionStatusError) throw e;
1670
- console.warn("[PollarClient:stream] session-status stream read failed; will retry", e);
1663
+ logger.debug("[PollarClient:stream] session-status stream read failed; will retry", e);
1671
1664
  } finally {
1672
1665
  reader.releaseLock();
1673
1666
  }
1674
1667
  if (sawAnyChunk) backoff = retryDelayMs;
1675
1668
  else backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);
1676
- const delay = streamDone ? backoff : 0;
1677
- if (delay) await sleep(delay);
1669
+ await sleep(backoff);
1678
1670
  }
1679
1671
  }
1680
- async function pollUntilFound(baseUrl, clientSessionId, check, intervalMs = 500, signal) {
1672
+ async function pollUntilFound(baseUrl, clientSessionId, check, intervalMs = 500, signal, logger = console) {
1681
1673
  const url = `${baseUrl}/auth/session/status/${encodeURIComponent(clientSessionId)}/poll`;
1682
1674
  let backoff = intervalMs;
1683
1675
  const sleep = async (ms) => {
@@ -1695,7 +1687,7 @@ async function pollUntilFound(baseUrl, clientSessionId, check, intervalMs = 500,
1695
1687
  envelope = await response.json().catch(() => null);
1696
1688
  } catch (e) {
1697
1689
  if (e instanceof Error && e.name === "AbortError") throw e;
1698
- console.warn("[PollarClient:stream] session-status poll failed; will retry", e);
1690
+ logger.debug("[PollarClient:stream] session-status poll failed; will retry", e);
1699
1691
  }
1700
1692
  if (httpStatus === 404 || envelope?.code === "INVALID_CLIENT_SESSION_ID") {
1701
1693
  throw new SessionStatusError("INVALID_CLIENT_SESSION_ID");
@@ -1712,13 +1704,13 @@ async function pollUntilFound(baseUrl, clientSessionId, check, intervalMs = 500,
1712
1704
  }
1713
1705
  }
1714
1706
  function waitForSessionReady(args) {
1715
- const { api, baseUrl, clientSessionId, check, useStreaming, retryDelayMs, signal } = args;
1716
- return useStreaming ? streamUntilFound(api, clientSessionId, check, retryDelayMs ?? 200, signal) : pollUntilFound(baseUrl, clientSessionId, check, retryDelayMs ?? 500, signal);
1707
+ const { api, baseUrl, clientSessionId, check, useStreaming, retryDelayMs, signal, logger = console } = args;
1708
+ return useStreaming ? streamUntilFound(api, clientSessionId, check, retryDelayMs ?? 200, signal, logger) : pollUntilFound(baseUrl, clientSessionId, check, retryDelayMs ?? 500, signal, logger);
1717
1709
  }
1718
1710
 
1719
1711
  // src/client/auth/authenticate.ts
1720
1712
  async function authenticate(clientSessionId, deps, expectedWallet) {
1721
- const { api, basePath, useStreaming, signal, setAuthState, storeSession, clearSession } = deps;
1713
+ const { api, logger, basePath, useStreaming, signal, setAuthState, storeSession, clearSession } = deps;
1722
1714
  setAuthState({ step: "authenticating" });
1723
1715
  try {
1724
1716
  await waitForSessionReady({
@@ -1727,7 +1719,8 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
1727
1719
  clientSessionId,
1728
1720
  check: (data2) => data2?.status === "READY",
1729
1721
  useStreaming,
1730
- signal
1722
+ signal,
1723
+ logger
1731
1724
  });
1732
1725
  } catch (err) {
1733
1726
  if (err instanceof SessionStatusError) {
@@ -1752,18 +1745,19 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
1752
1745
  },
1753
1746
  signal
1754
1747
  });
1755
- if (data?.code === "SDK_LOGIN_SUCCESS" && isValidSession(data?.content)) {
1756
- if (expectedWallet && data.content.data.providers.wallet?.address !== expectedWallet) {
1748
+ if (data?.code === "SDK_LOGIN_SUCCESS" && isValidSession(data?.content, logger)) {
1749
+ const sessionWallet = data.content.data?.providers?.wallet?.address;
1750
+ if (expectedWallet && sessionWallet !== expectedWallet) {
1757
1751
  setAuthState({
1758
1752
  step: "error",
1759
1753
  previousStep: "authenticating",
1760
1754
  message: "Wallet mismatch: session wallet does not match connected wallet",
1761
1755
  errorCode: AUTH_ERROR_CODES.WALLET_AUTH_FAILED
1762
1756
  });
1763
- clearSession();
1757
+ await clearSession();
1764
1758
  return;
1765
1759
  }
1766
- storeSession(data.content);
1760
+ await storeSession(data.content);
1767
1761
  } else {
1768
1762
  setAuthState({
1769
1763
  step: "error",
@@ -1771,7 +1765,7 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
1771
1765
  message: "Failed to load session",
1772
1766
  errorCode: AUTH_ERROR_CODES.AUTH_FAILED
1773
1767
  });
1774
- clearSession();
1768
+ await clearSession();
1775
1769
  }
1776
1770
  }
1777
1771
 
@@ -2070,7 +2064,9 @@ var PollarClient = class {
2070
2064
  this.apiKey = config.apiKey;
2071
2065
  this.id = randomUUID();
2072
2066
  this.basePath = `${config.baseUrl || "https://sdk.api.pollar.xyz"}/v1`;
2067
+ this._log = createLogger(config.logLevel ?? "info", config.logger);
2073
2068
  this._storage = config.storage ?? defaultStorage({
2069
+ logger: this._log,
2074
2070
  onDegrade: (reason, error) => {
2075
2071
  config.onStorageDegrade?.(reason, error);
2076
2072
  this._dispatchStorageDegrade(reason, error);
@@ -2085,7 +2081,7 @@ var PollarClient = class {
2085
2081
  this._visibilityProvider = config.visibilityProvider ?? defaultVisibilityProvider();
2086
2082
  this._maxIdleMs = config.maxIdleMs;
2087
2083
  this._openAuthUrl = config.openAuthUrl ?? defaultWebOAuthOpener;
2088
- this._oauthRedirectUri = config.oauthRedirectUri ?? (isBrowser ? window.location.origin : "");
2084
+ this._oauthRedirectUri = config.oauthRedirectUri ?? (isBrowser ? window.location?.origin ?? "" : "");
2089
2085
  this._api = createApiClient(this.basePath);
2090
2086
  this._wireMiddlewares();
2091
2087
  this._networkState = { step: "connected", network: config.stellarNetwork ?? "testnet" };
@@ -2094,7 +2090,7 @@ var PollarClient = class {
2094
2090
  this._initialized = Promise.resolve();
2095
2091
  return;
2096
2092
  }
2097
- console.info(
2093
+ this._log.info(
2098
2094
  `[PollarClient] Initialized v${POLLAR_CORE_VERSION} \u2014 endpoint: ${this.basePath}, network: ${this._networkState.network}`
2099
2095
  );
2100
2096
  this._initialized = this._initialize();
@@ -2121,7 +2117,7 @@ var PollarClient = class {
2121
2117
  const sessionKey = sessionStorageKey(this._apiKeyHash);
2122
2118
  const handler = (e) => {
2123
2119
  if (e.key === sessionKey) {
2124
- this._restoreSession().catch((err) => console.error("[PollarClient] Cross-tab restore failed", err));
2120
+ this._restoreSession().catch((err) => this._log.error("[PollarClient] Cross-tab restore failed", err));
2125
2121
  }
2126
2122
  };
2127
2123
  window.addEventListener("storage", handler);
@@ -2130,7 +2126,7 @@ var PollarClient = class {
2130
2126
  try {
2131
2127
  await this._keyManager.init();
2132
2128
  } catch (err) {
2133
- console.warn("[PollarClient] KeyManager init failed; DPoP unavailable for this session", err);
2129
+ this._log.warn("[PollarClient] KeyManager init failed; DPoP unavailable for this session", err);
2134
2130
  }
2135
2131
  await this._restoreSession();
2136
2132
  this._visibilityUnsubscribe = this._visibilityProvider.onChange((visible) => {
@@ -2171,7 +2167,7 @@ var PollarClient = class {
2171
2167
  try {
2172
2168
  self._requestBodyCache.set(request, await request.clone().arrayBuffer());
2173
2169
  } catch (err) {
2174
- console.warn("[PollarClient] Could not snapshot request body for retry", err);
2170
+ this._log.warn("[PollarClient] Could not snapshot request body for retry", err);
2175
2171
  }
2176
2172
  }
2177
2173
  const isRefresh = request.url.includes("/auth/refresh");
@@ -2230,7 +2226,7 @@ var PollarClient = class {
2230
2226
  this._keyManager
2231
2227
  );
2232
2228
  } catch (err) {
2233
- console.warn("[PollarClient] DPoP proof build failed", err);
2229
+ this._log.warn("[PollarClient] DPoP proof build failed", err);
2234
2230
  return null;
2235
2231
  }
2236
2232
  }
@@ -2284,7 +2280,7 @@ var PollarClient = class {
2284
2280
  async _doRefresh() {
2285
2281
  const refreshToken = this._session?.token?.refreshToken;
2286
2282
  if (!refreshToken) {
2287
- console.warn("[PollarClient] Refresh skipped: no refresh token in session");
2283
+ this._log.warn("[PollarClient] Refresh skipped: no refresh token in session");
2288
2284
  await this._clearSession();
2289
2285
  throw new Error("No refresh token available");
2290
2286
  }
@@ -2295,18 +2291,18 @@ var PollarClient = class {
2295
2291
  data = response.data;
2296
2292
  error = response.error;
2297
2293
  } catch (err) {
2298
- console.error("[PollarClient] /auth/refresh request threw", err);
2294
+ this._log.error("[PollarClient] /auth/refresh request threw", err);
2299
2295
  await this._clearSession();
2300
2296
  throw err;
2301
2297
  }
2302
2298
  if (error || !data) {
2303
- console.error("[PollarClient] /auth/refresh returned error", { error });
2299
+ this._log.error("[PollarClient] /auth/refresh returned error", { error });
2304
2300
  await this._clearSession();
2305
2301
  throw new Error("Refresh failed");
2306
2302
  }
2307
2303
  const successData = data;
2308
2304
  if (!successData.success || !successData.content?.token) {
2309
- console.error("[PollarClient] /auth/refresh response malformed", {
2305
+ this._log.error("[PollarClient] /auth/refresh response malformed", {
2310
2306
  success: successData.success,
2311
2307
  hasToken: !!successData.content?.token
2312
2308
  });
@@ -2315,7 +2311,7 @@ var PollarClient = class {
2315
2311
  }
2316
2312
  const newToken = successData.content.token;
2317
2313
  if (typeof newToken.accessToken !== "string" || typeof newToken.refreshToken !== "string" || typeof newToken.expiresAt !== "number") {
2318
- console.error("[PollarClient] /auth/refresh token shape invalid", {
2314
+ this._log.error("[PollarClient] /auth/refresh token shape invalid", {
2319
2315
  accessToken: typeof newToken.accessToken,
2320
2316
  refreshToken: typeof newToken.refreshToken,
2321
2317
  expiresAt: typeof newToken.expiresAt
@@ -2327,9 +2323,9 @@ var PollarClient = class {
2327
2323
  try {
2328
2324
  this._session = { ...this._session, token: newToken };
2329
2325
  await writeStorage(this._storage, this.apiKeyHash, this._session);
2330
- console.info("[PollarClient] Tokens refreshed");
2326
+ this._log.info("[PollarClient] Tokens refreshed");
2331
2327
  } catch (err) {
2332
- console.error("[PollarClient] Failed to persist refreshed session", err);
2328
+ this._log.error("[PollarClient] Failed to persist refreshed session", err);
2333
2329
  }
2334
2330
  this._scheduleNextRefresh();
2335
2331
  }
@@ -2383,7 +2379,7 @@ var PollarClient = class {
2383
2379
  try {
2384
2380
  await this.refresh();
2385
2381
  } catch (err) {
2386
- console.warn("[PollarClient] Proactive refresh failed; session cleared", err);
2382
+ this._log.warn("[PollarClient] Proactive refresh failed; session cleared", err);
2387
2383
  }
2388
2384
  }
2389
2385
  _clearRefreshTimer() {
@@ -2429,7 +2425,7 @@ var PollarClient = class {
2429
2425
  try {
2430
2426
  cb(reason, error);
2431
2427
  } catch (err) {
2432
- console.error("[PollarClient] onStorageDegrade listener threw", err);
2428
+ this._log.error("[PollarClient] onStorageDegrade listener threw", err);
2433
2429
  }
2434
2430
  }
2435
2431
  }
@@ -2549,20 +2545,20 @@ var PollarClient = class {
2549
2545
  warnServerSide("logout");
2550
2546
  return;
2551
2547
  }
2552
- console.info("[PollarClient] Logout requested", { everywhere: !!options.everywhere });
2548
+ this._log.info("[PollarClient] Logout requested", { everywhere: !!options.everywhere });
2553
2549
  if (this._session?.token?.accessToken) {
2554
2550
  try {
2555
2551
  await this._api.POST("/auth/logout", {
2556
2552
  body: options.everywhere ? { everywhere: true } : {}
2557
2553
  });
2558
2554
  } catch (err) {
2559
- console.warn("[PollarClient] Server logout failed (continuing with local clear)", err);
2555
+ this._log.warn("[PollarClient] Server logout failed (continuing with local clear)", err);
2560
2556
  }
2561
2557
  }
2562
2558
  try {
2563
2559
  await this._clearSession();
2564
2560
  } catch (err) {
2565
- console.warn("[PollarClient] Local logout cleanup failed", err);
2561
+ this._log.warn("[PollarClient] Local logout cleanup failed", err);
2566
2562
  }
2567
2563
  }
2568
2564
  /** Convenience: revoke every active session for this user (all devices). */
@@ -2638,6 +2634,14 @@ var PollarClient = class {
2638
2634
  getNetworkState() {
2639
2635
  return this._networkState;
2640
2636
  }
2637
+ /**
2638
+ * The client's level-gated logger (built from `logLevel` / `logger`). Exposed
2639
+ * so the runtime layer (`@pollar/react`) can route its own logs through the
2640
+ * same level and sink instead of calling `console` directly.
2641
+ */
2642
+ getLogger() {
2643
+ return this._log;
2644
+ }
2641
2645
  setNetwork(network) {
2642
2646
  this._setNetworkState({ step: "connected", network });
2643
2647
  }
@@ -2786,7 +2790,7 @@ var PollarClient = class {
2786
2790
  this._setTransactionState({ step: "error", phase: "building", ...details && { details } });
2787
2791
  return { status: "error", ...details && { details } };
2788
2792
  } catch (err) {
2789
- console.error("[PollarClient] buildTx failed", err);
2793
+ this._log.error("[PollarClient] buildTx failed", err);
2790
2794
  this._setTransactionState({ step: "error", phase: "building" });
2791
2795
  return { status: "error" };
2792
2796
  }
@@ -3299,6 +3303,7 @@ var PollarClient = class {
3299
3303
  _flowDeps(signal) {
3300
3304
  return {
3301
3305
  api: this._api,
3306
+ logger: this._log,
3302
3307
  basePath: this.basePath,
3303
3308
  // SSE status streaming works on web; React Native's `fetch` has no
3304
3309
  // readable `response.body`, so those clients poll the non-streaming
@@ -3351,12 +3356,12 @@ var PollarClient = class {
3351
3356
  }
3352
3357
  _handleFlowError(error) {
3353
3358
  if (error instanceof Error && error.name === "AbortError") {
3354
- console.info("[PollarClient] Login cancelled");
3359
+ this._log.debug("[PollarClient] Login cancelled");
3355
3360
  this._setAuthState({ step: "idle" });
3356
3361
  return;
3357
3362
  }
3358
3363
  if (error instanceof Error && error.code === AUTH_ERROR_CODES.WALLET_RESOLVER_TIMEOUT) {
3359
- console.error("[PollarClient]", error.message);
3364
+ this._log.error("[PollarClient]", error.message);
3360
3365
  this._setAuthState({
3361
3366
  step: "error",
3362
3367
  previousStep: this._authState.step,
@@ -3365,7 +3370,7 @@ var PollarClient = class {
3365
3370
  });
3366
3371
  return;
3367
3372
  }
3368
- console.error("[PollarClient] Unexpected error in auth flow", error);
3373
+ this._log.error("[PollarClient] Unexpected error in auth flow", error);
3369
3374
  this._setAuthState({
3370
3375
  step: "error",
3371
3376
  previousStep: this._authState.step,
@@ -3374,22 +3379,25 @@ var PollarClient = class {
3374
3379
  });
3375
3380
  }
3376
3381
  async _restoreSession() {
3377
- this._session = await readStorage(this._storage, this.apiKeyHash);
3382
+ this._session = await readStorage(this._storage, this.apiKeyHash, this._log);
3378
3383
  if (this._session) {
3379
3384
  const storedType = await readWalletType(this._storage, this.apiKeyHash);
3380
3385
  if (storedType) {
3381
3386
  try {
3382
3387
  this._walletAdapter = await this._resolveWalletAdapter(storedType);
3383
3388
  } catch (err) {
3384
- console.warn("[PollarClient] Could not restore wallet adapter for stored id", { id: storedType, err });
3389
+ this._log.warn("[PollarClient] Could not restore wallet adapter for stored id", { id: storedType, err });
3385
3390
  }
3386
3391
  }
3387
- console.info("[PollarClient] Session restored from storage");
3392
+ this._log.info("[PollarClient] Session restored from storage");
3388
3393
  this._setAuthState({ step: "authenticated", session: this._session, verified: false });
3389
3394
  this._scheduleNextRefresh();
3390
3395
  void this._resume();
3391
3396
  } else {
3392
- console.info("[PollarClient] No session in storage");
3397
+ this._log.info("[PollarClient] No session in storage");
3398
+ if (this._authState.step !== "idle") {
3399
+ await this._clearSession();
3400
+ }
3393
3401
  }
3394
3402
  }
3395
3403
  /**
@@ -3420,13 +3428,13 @@ var PollarClient = class {
3420
3428
  this._setAuthState({ step: "authenticated", session: this._session, verified: true });
3421
3429
  } catch (err) {
3422
3430
  if (err?.name === "AbortError") return;
3423
- console.warn("[PollarClient] resume failed (network); will retry", err);
3431
+ this._log.warn("[PollarClient] resume failed (network); will retry", err);
3424
3432
  } finally {
3425
3433
  if (this._resumeController === controller) this._resumeController = null;
3426
3434
  }
3427
3435
  }
3428
3436
  async _storeSession(session) {
3429
- console.info("[PollarClient] Session stored");
3437
+ this._log.info("[PollarClient] Session stored");
3430
3438
  const w = session.wallet;
3431
3439
  const persisted = {
3432
3440
  clientSessionId: session.clientSessionId,
@@ -3436,8 +3444,11 @@ var PollarClient = class {
3436
3444
  user: session.user,
3437
3445
  // The wire response still carries the legacy `publicKey` alias (kept for
3438
3446
  // older SDKs); the persisted session standardizes on `address` only.
3447
+ // The wire also still emits the legacy type `'custodial'` (unchanged for
3448
+ // SDKs ≤0.8.x); we remap it to `'internal'` here so the SDK surface and
3449
+ // persisted session speak one vocabulary while the wire stays compatible.
3439
3450
  wallet: {
3440
- type: w.type,
3451
+ type: w.type === "custodial" ? "internal" : w.type,
3441
3452
  address: w.address ?? w.publicKey ?? null,
3442
3453
  ...w.existsOnStellar !== void 0 ? { existsOnStellar: w.existsOnStellar } : {},
3443
3454
  ...w.createdAt !== void 0 ? { createdAt: w.createdAt } : {},
@@ -3461,7 +3472,7 @@ var PollarClient = class {
3461
3472
  this._scheduleNextRefresh();
3462
3473
  }
3463
3474
  async _clearSession() {
3464
- console.info("[PollarClient] Session cleared");
3475
+ this._log.info("[PollarClient] Session cleared");
3465
3476
  this._clearRefreshTimer();
3466
3477
  this._session = null;
3467
3478
  this._profile = null;
@@ -3470,7 +3481,7 @@ var PollarClient = class {
3470
3481
  try {
3471
3482
  await this._keyManager.reset();
3472
3483
  } catch (err) {
3473
- console.warn("[PollarClient] KeyManager reset failed during clearSession", err);
3484
+ this._log.warn("[PollarClient] KeyManager reset failed during clearSession", err);
3474
3485
  }
3475
3486
  await removeStorage(this._storage, this.apiKeyHash);
3476
3487
  this._transactionState = null;
@@ -3482,17 +3493,17 @@ var PollarClient = class {
3482
3493
  _setNetworkState(next) {
3483
3494
  this._networkState = next;
3484
3495
  const label = next.step === "connected" ? next.network : next.step;
3485
- console.info(`[PollarClient] network:${label}`);
3496
+ this._log.debug(`[PollarClient] network:${label}`);
3486
3497
  for (const cb of this._networkStateListeners) cb(next);
3487
3498
  }
3488
3499
  _setAuthState(next) {
3489
3500
  this._authState = next;
3490
- console.info(`[PollarClient] auth:${next.step}`);
3501
+ this._log.debug(`[PollarClient] auth:${next.step}`);
3491
3502
  for (const cb of this._authStateListeners) cb(next);
3492
3503
  }
3493
3504
  _setTransactionState(next) {
3494
3505
  this._transactionState = next;
3495
- console.info(`[PollarClient] transaction:${next.step}`);
3506
+ this._log.debug(`[PollarClient] transaction:${next.step}`);
3496
3507
  for (const cb of this._transactionStateListeners) cb(next);
3497
3508
  }
3498
3509
  /**
@@ -3553,6 +3564,7 @@ exports.canonicalEcJwk = canonicalEcJwk;
3553
3564
  exports.claimDistributionRule = claimDistributionRule;
3554
3565
  exports.computeJwkThumbprint = computeJwkThumbprint;
3555
3566
  exports.createLocalStorageAdapter = createLocalStorageAdapter;
3567
+ exports.createLogger = createLogger;
3556
3568
  exports.createMemoryAdapter = createMemoryAdapter;
3557
3569
  exports.createOffRamp = createOffRamp;
3558
3570
  exports.createOnRamp = createOnRamp;