@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.rn.js CHANGED
@@ -304,7 +304,6 @@ var NobleKeyManager = class {
304
304
  if (this.privateKey) return;
305
305
  if (!this._initPromise) {
306
306
  this._initPromise = this._doInit().catch((err) => {
307
- console.error("[PollarClient:keys] NobleKeyManager init failed", err);
308
307
  this._initPromise = null;
309
308
  throw err;
310
309
  });
@@ -1012,6 +1011,16 @@ function normalizeHtu(rawUrl) {
1012
1011
  return `${scheme}//${host}${portPart}${url.pathname}`;
1013
1012
  }
1014
1013
 
1014
+ // src/lib/logger.ts
1015
+ var RANK = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
1016
+ function createLogger(level = "info", sink = console) {
1017
+ const threshold = RANK[level];
1018
+ const gate = (lvl) => (...args) => {
1019
+ if (threshold >= RANK[lvl]) sink[lvl](...args);
1020
+ };
1021
+ return { error: gate("error"), warn: gate("warn"), info: gate("info"), debug: gate("debug") };
1022
+ }
1023
+
1015
1024
  // src/storage/web.ts
1016
1025
  var LOG_PREFIX = "[PollarClient:storage]";
1017
1026
  function createMemoryAdapter() {
@@ -1035,7 +1044,7 @@ function createLocalStorageAdapter(options = {}) {
1035
1044
  function degrade(reason, error) {
1036
1045
  if (degraded) return;
1037
1046
  degraded = true;
1038
- console.warn(`${LOG_PREFIX} localStorage unavailable (${reason}); degrading to in-memory storage`);
1047
+ (options.logger ?? console).warn(`${LOG_PREFIX} localStorage unavailable (${reason}); degrading to in-memory storage`);
1039
1048
  options.onDegrade?.(reason, error);
1040
1049
  }
1041
1050
  return {
@@ -1100,7 +1109,7 @@ function defaultStorage(options = {}) {
1100
1109
  }
1101
1110
 
1102
1111
  // src/version.ts
1103
- var POLLAR_CORE_VERSION = "0.9.0-rc.0" ;
1112
+ var POLLAR_CORE_VERSION = "0.9.0-rc.2" ;
1104
1113
 
1105
1114
  // src/visibility/noop.ts
1106
1115
  function createNoopVisibilityProvider() {
@@ -1272,7 +1281,10 @@ function openAlbedoPopup(url) {
1272
1281
  }
1273
1282
  function waitForAlbedoPopup() {
1274
1283
  return new Promise((resolve, reject) => {
1275
- const timeout = setTimeout(() => reject(new Error("Albedo response timeout")), 2 * 60 * 1e3);
1284
+ const timeout = setTimeout(() => {
1285
+ window.removeEventListener("message", handler);
1286
+ reject(new Error("Albedo response timeout"));
1287
+ }, 2 * 60 * 1e3);
1276
1288
  function handler(event) {
1277
1289
  if (event.origin !== window.location.origin || event.data?.type !== "ALBEDO_RESULT") return;
1278
1290
  clearTimeout(timeout);
@@ -1282,24 +1294,6 @@ function waitForAlbedoPopup() {
1282
1294
  window.addEventListener("message", handler);
1283
1295
  });
1284
1296
  }
1285
- function waitForAlbedoResult() {
1286
- return new Promise((resolve, reject) => {
1287
- const timeout = setTimeout(() => reject(new Error("Albedo response timeout")), 2 * 60 * 1e3);
1288
- const parseResult = () => {
1289
- const params = new URLSearchParams(window.location.search);
1290
- if (!params.has("pubkey") && !params.has("signed_envelope_xdr") && !params.has("signed_xdr")) return;
1291
- clearTimeout(timeout);
1292
- const result = {};
1293
- params.forEach((value, key) => {
1294
- result[key] = value;
1295
- });
1296
- window.history.replaceState({}, document.title, window.location.pathname);
1297
- resolve(result);
1298
- };
1299
- parseResult();
1300
- window.addEventListener("popstate", parseResult);
1301
- });
1302
- }
1303
1297
  var AlbedoAdapter = class {
1304
1298
  /**
1305
1299
  * Network used for `connect` and `signAuthEntry` (which carry no per-call
@@ -1341,10 +1335,10 @@ var AlbedoAdapter = class {
1341
1335
  url.searchParams.set("xdr", xdr);
1342
1336
  url.searchParams.set("app_name", "Pollar");
1343
1337
  url.searchParams.set("network", albedoNetwork(options, this.network));
1344
- url.searchParams.set("callback", window.location.href);
1338
+ url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
1345
1339
  url.searchParams.set("origin", window.location.origin);
1346
- window.location.href = url.toString();
1347
- const result = await waitForAlbedoResult();
1340
+ openAlbedoPopup(url.toString());
1341
+ const result = await waitForAlbedoPopup();
1348
1342
  if (!result.signed_envelope_xdr) throw new Error("Albedo signing rejected");
1349
1343
  return { signedTxXdr: result.signed_envelope_xdr };
1350
1344
  }
@@ -1354,10 +1348,10 @@ var AlbedoAdapter = class {
1354
1348
  url.searchParams.set("xdr", entryXdr);
1355
1349
  url.searchParams.set("app_name", "Pollar");
1356
1350
  url.searchParams.set("network", this.network);
1357
- url.searchParams.set("callback", window.location.href);
1351
+ url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
1358
1352
  url.searchParams.set("origin", window.location.origin);
1359
- window.location.href = url.toString();
1360
- const result = await waitForAlbedoResult();
1353
+ openAlbedoPopup(url.toString());
1354
+ const result = await waitForAlbedoPopup();
1361
1355
  if (!result.signed_xdr) throw new Error("Albedo auth entry signing rejected");
1362
1356
  return { signedAuthEntry: result.signed_xdr };
1363
1357
  }
@@ -1384,85 +1378,85 @@ function isBoundedString(v, max, allowEmpty = false) {
1384
1378
  if (!allowEmpty && v.length === 0) return false;
1385
1379
  return v.length <= max;
1386
1380
  }
1387
- function isValidSession(value) {
1381
+ function isValidSession(value, logger = console) {
1388
1382
  if (typeof value !== "object" || value === null) {
1389
- console.warn("[PollarClient:session] Invalid session \u2014 value is not an object");
1383
+ logger.debug("[PollarClient:session] Invalid session \u2014 value is not an object");
1390
1384
  return false;
1391
1385
  }
1392
1386
  const s = value;
1393
1387
  if (!isBoundedString(s["clientSessionId"], MAX_CLIENT_SESSION_ID)) {
1394
- console.warn("[PollarClient:session] Invalid session \u2014 clientSessionId missing/empty/too long");
1388
+ logger.debug("[PollarClient:session] Invalid session \u2014 clientSessionId missing/empty/too long");
1395
1389
  return false;
1396
1390
  }
1397
1391
  if (s["userId"] !== null && !isBoundedString(s["userId"], MAX_USER_ID)) {
1398
- console.warn("[PollarClient:session] Invalid session \u2014 userId must be string|null");
1392
+ logger.debug("[PollarClient:session] Invalid session \u2014 userId must be string|null");
1399
1393
  return false;
1400
1394
  }
1401
1395
  if (!isBoundedString(s["status"], MAX_STATUS)) {
1402
- console.warn("[PollarClient:session] Invalid session \u2014 status must be string");
1396
+ logger.debug("[PollarClient:session] Invalid session \u2014 status must be string");
1403
1397
  return false;
1404
1398
  }
1405
1399
  const token = s["token"];
1406
1400
  if (typeof token !== "object" || token === null) {
1407
- console.warn("[PollarClient:session] Invalid session \u2014 token missing or not an object");
1401
+ logger.debug("[PollarClient:session] Invalid session \u2014 token missing or not an object");
1408
1402
  return false;
1409
1403
  }
1410
1404
  const t = token;
1411
1405
  if (!isBoundedString(t["accessToken"], MAX_ACCESS_TOKEN)) {
1412
- console.warn("[PollarClient:session] Invalid session \u2014 token.accessToken missing/empty/too long");
1406
+ logger.debug("[PollarClient:session] Invalid session \u2014 token.accessToken missing/empty/too long");
1413
1407
  return false;
1414
1408
  }
1415
1409
  if (!isBoundedString(t["refreshToken"], MAX_REFRESH_TOKEN)) {
1416
- console.warn("[PollarClient:session] Invalid session \u2014 token.refreshToken missing/empty/too long");
1410
+ logger.debug("[PollarClient:session] Invalid session \u2014 token.refreshToken missing/empty/too long");
1417
1411
  return false;
1418
1412
  }
1419
1413
  if (typeof t["expiresAt"] !== "number" || !Number.isFinite(t["expiresAt"])) {
1420
- console.warn("[PollarClient:session] Invalid session \u2014 token.expiresAt must be a finite number");
1414
+ logger.debug("[PollarClient:session] Invalid session \u2014 token.expiresAt must be a finite number");
1421
1415
  return false;
1422
1416
  }
1423
1417
  const user = s["user"];
1424
1418
  if (typeof user !== "object" || user === null) {
1425
- console.warn("[PollarClient:session] Invalid session \u2014 user missing or not an object");
1419
+ logger.debug("[PollarClient:session] Invalid session \u2014 user missing or not an object");
1426
1420
  return false;
1427
1421
  }
1428
1422
  const u = user;
1429
1423
  if (u["id"] !== void 0 && !isBoundedString(u["id"], MAX_USER_ID)) {
1430
- console.warn("[PollarClient:session] Invalid session \u2014 user.id must be string if present");
1424
+ logger.debug("[PollarClient:session] Invalid session \u2014 user.id must be string if present");
1431
1425
  return false;
1432
1426
  }
1433
1427
  if (typeof u["ready"] !== "boolean") {
1434
- console.warn("[PollarClient:session] Invalid session \u2014 user.ready must be boolean");
1428
+ logger.debug("[PollarClient:session] Invalid session \u2014 user.ready must be boolean");
1435
1429
  return false;
1436
1430
  }
1437
1431
  const wallet = s["wallet"];
1438
1432
  if (typeof wallet !== "object" || wallet === null) {
1439
- console.warn("[PollarClient:session] Invalid session \u2014 wallet missing or not an object");
1433
+ logger.debug("[PollarClient:session] Invalid session \u2014 wallet missing or not an object");
1440
1434
  return false;
1441
1435
  }
1442
1436
  const w = wallet;
1443
- if (w["type"] !== "custodial" && w["type"] !== "smart" && w["type"] !== "external") {
1444
- console.warn("[PollarClient:session] Invalid session \u2014 wallet.type must be custodial|smart|external");
1437
+ if (w["type"] !== "internal" && w["type"] !== "smart" && w["type"] !== "external") {
1438
+ logger.debug("[PollarClient:session] Invalid session \u2014 wallet.type must be internal|smart|external");
1445
1439
  return false;
1446
1440
  }
1447
1441
  if (w["address"] !== null && !isBoundedString(w["address"], MAX_WALLET_PUBLIC_KEY)) {
1448
- console.warn("[PollarClient:session] Invalid session \u2014 wallet.address must be string|null");
1442
+ logger.debug("[PollarClient:session] Invalid session \u2014 wallet.address must be string|null");
1449
1443
  return false;
1450
1444
  }
1451
1445
  if (w["existsOnStellar"] !== void 0 && typeof w["existsOnStellar"] !== "boolean") {
1452
- console.warn("[PollarClient:session] Invalid session \u2014 wallet.existsOnStellar must be boolean if present");
1446
+ logger.debug("[PollarClient:session] Invalid session \u2014 wallet.existsOnStellar must be boolean if present");
1453
1447
  return false;
1454
1448
  }
1455
1449
  if (w["createdAt"] !== void 0 && (typeof w["createdAt"] !== "number" || !Number.isFinite(w["createdAt"]))) {
1456
- console.warn("[PollarClient:session] Invalid session \u2014 wallet.createdAt must be a finite number if present");
1450
+ logger.debug("[PollarClient:session] Invalid session \u2014 wallet.createdAt must be a finite number if present");
1457
1451
  return false;
1458
1452
  }
1459
1453
  if (w["linkedAt"] !== void 0 && (typeof w["linkedAt"] !== "number" || !Number.isFinite(w["linkedAt"]))) {
1460
- console.warn("[PollarClient:session] Invalid session \u2014 wallet.linkedAt must be a finite number if present");
1454
+ logger.debug("[PollarClient:session] Invalid session \u2014 wallet.linkedAt must be a finite number if present");
1461
1455
  return false;
1462
1456
  }
1463
1457
  return true;
1464
1458
  }
1465
- async function readStorage(storage, apiKeyHash) {
1459
+ async function readStorage(storage, apiKeyHash, logger = console) {
1466
1460
  const raw = await storage.get(sessionStorageKey(apiKeyHash));
1467
1461
  if (!raw) return null;
1468
1462
  try {
@@ -1472,10 +1466,13 @@ async function readStorage(storage, apiKeyHash) {
1472
1466
  if (w && w["address"] == null && typeof w["publicKey"] === "string") {
1473
1467
  w["address"] = w["publicKey"];
1474
1468
  }
1469
+ if (w && w["type"] === "custodial") {
1470
+ w["type"] = "internal";
1471
+ }
1475
1472
  }
1476
- if (!isValidSession(session)) {
1473
+ if (!isValidSession(session, logger)) {
1477
1474
  await storage.remove(sessionStorageKey(apiKeyHash));
1478
- console.warn("[PollarClient:session] Stored session is invalid \u2014 clearing storage");
1475
+ logger.warn("[PollarClient:session] Stored session is invalid \u2014 clearing storage");
1479
1476
  return null;
1480
1477
  }
1481
1478
  if (session.token.expiresAt * 1e3 < Date.now()) {
@@ -1483,7 +1480,7 @@ async function readStorage(storage, apiKeyHash) {
1483
1480
  }
1484
1481
  return session;
1485
1482
  } catch (error) {
1486
- console.error("[PollarClient:session] Failed to parse session from storage", error);
1483
+ logger.error("[PollarClient:session] Failed to parse session from storage", error);
1487
1484
  await storage.remove(sessionStorageKey(apiKeyHash));
1488
1485
  return null;
1489
1486
  }
@@ -1545,7 +1542,7 @@ function abortableDelay(ms, signal) {
1545
1542
  });
1546
1543
  }
1547
1544
  var MAX_BACKOFF_MS = 5e3;
1548
- async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200, signal) {
1545
+ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200, signal, logger = console) {
1549
1546
  let backoff = retryDelayMs;
1550
1547
  const sleep = async (ms) => {
1551
1548
  if (ms <= 0) return;
@@ -1563,7 +1560,7 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
1563
1560
  }));
1564
1561
  } catch (e) {
1565
1562
  if (e instanceof Error && e.name === "AbortError") throw e;
1566
- console.warn("[PollarClient:stream] session-status request failed; will retry", e);
1563
+ logger.debug("[PollarClient:stream] session-status request failed; will retry", e);
1567
1564
  }
1568
1565
  if (error || !data) {
1569
1566
  await sleep(backoff);
@@ -1572,16 +1569,12 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
1572
1569
  }
1573
1570
  const reader = data.getReader();
1574
1571
  const decoder = new TextDecoder();
1575
- let streamDone = false;
1576
1572
  let sawAnyChunk = false;
1577
1573
  try {
1578
1574
  while (true) {
1579
1575
  throwIfAborted(signal);
1580
1576
  const { done, value } = await reader.read();
1581
- if (done) {
1582
- streamDone = true;
1583
- break;
1584
- }
1577
+ if (done) break;
1585
1578
  sawAnyChunk = true;
1586
1579
  const chunk = decoder.decode(value);
1587
1580
  for (const message of chunk.split("\n\n").filter(Boolean)) {
@@ -1603,17 +1596,16 @@ async function streamUntilFound(api, clientSessionId, check, retryDelayMs = 200,
1603
1596
  } catch (e) {
1604
1597
  if (e instanceof Error && e.name === "AbortError") throw e;
1605
1598
  if (e instanceof SessionStatusError) throw e;
1606
- console.warn("[PollarClient:stream] session-status stream read failed; will retry", e);
1599
+ logger.debug("[PollarClient:stream] session-status stream read failed; will retry", e);
1607
1600
  } finally {
1608
1601
  reader.releaseLock();
1609
1602
  }
1610
1603
  if (sawAnyChunk) backoff = retryDelayMs;
1611
1604
  else backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);
1612
- const delay = streamDone ? backoff : 0;
1613
- if (delay) await sleep(delay);
1605
+ await sleep(backoff);
1614
1606
  }
1615
1607
  }
1616
- async function pollUntilFound(baseUrl, clientSessionId, check, intervalMs = 500, signal) {
1608
+ async function pollUntilFound(baseUrl, clientSessionId, check, intervalMs = 500, signal, logger = console) {
1617
1609
  const url = `${baseUrl}/auth/session/status/${encodeURIComponent(clientSessionId)}/poll`;
1618
1610
  let backoff = intervalMs;
1619
1611
  const sleep = async (ms) => {
@@ -1631,7 +1623,7 @@ async function pollUntilFound(baseUrl, clientSessionId, check, intervalMs = 500,
1631
1623
  envelope = await response.json().catch(() => null);
1632
1624
  } catch (e) {
1633
1625
  if (e instanceof Error && e.name === "AbortError") throw e;
1634
- console.warn("[PollarClient:stream] session-status poll failed; will retry", e);
1626
+ logger.debug("[PollarClient:stream] session-status poll failed; will retry", e);
1635
1627
  }
1636
1628
  if (httpStatus === 404 || envelope?.code === "INVALID_CLIENT_SESSION_ID") {
1637
1629
  throw new SessionStatusError("INVALID_CLIENT_SESSION_ID");
@@ -1648,13 +1640,13 @@ async function pollUntilFound(baseUrl, clientSessionId, check, intervalMs = 500,
1648
1640
  }
1649
1641
  }
1650
1642
  function waitForSessionReady(args) {
1651
- const { api, baseUrl, clientSessionId, check, useStreaming, retryDelayMs, signal } = args;
1652
- return useStreaming ? streamUntilFound(api, clientSessionId, check, retryDelayMs ?? 200, signal) : pollUntilFound(baseUrl, clientSessionId, check, retryDelayMs ?? 500, signal);
1643
+ const { api, baseUrl, clientSessionId, check, useStreaming, retryDelayMs, signal, logger = console } = args;
1644
+ return useStreaming ? streamUntilFound(api, clientSessionId, check, retryDelayMs ?? 200, signal, logger) : pollUntilFound(baseUrl, clientSessionId, check, retryDelayMs ?? 500, signal, logger);
1653
1645
  }
1654
1646
 
1655
1647
  // src/client/auth/authenticate.ts
1656
1648
  async function authenticate(clientSessionId, deps, expectedWallet) {
1657
- const { api, basePath, useStreaming, signal, setAuthState, storeSession, clearSession } = deps;
1649
+ const { api, logger, basePath, useStreaming, signal, setAuthState, storeSession, clearSession } = deps;
1658
1650
  setAuthState({ step: "authenticating" });
1659
1651
  try {
1660
1652
  await waitForSessionReady({
@@ -1663,7 +1655,8 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
1663
1655
  clientSessionId,
1664
1656
  check: (data2) => data2?.status === "READY",
1665
1657
  useStreaming,
1666
- signal
1658
+ signal,
1659
+ logger
1667
1660
  });
1668
1661
  } catch (err) {
1669
1662
  if (err instanceof SessionStatusError) {
@@ -1688,18 +1681,19 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
1688
1681
  },
1689
1682
  signal
1690
1683
  });
1691
- if (data?.code === "SDK_LOGIN_SUCCESS" && isValidSession(data?.content)) {
1692
- if (expectedWallet && data.content.data.providers.wallet?.address !== expectedWallet) {
1684
+ if (data?.code === "SDK_LOGIN_SUCCESS" && isValidSession(data?.content, logger)) {
1685
+ const sessionWallet = data.content.data?.providers?.wallet?.address;
1686
+ if (expectedWallet && sessionWallet !== expectedWallet) {
1693
1687
  setAuthState({
1694
1688
  step: "error",
1695
1689
  previousStep: "authenticating",
1696
1690
  message: "Wallet mismatch: session wallet does not match connected wallet",
1697
1691
  errorCode: AUTH_ERROR_CODES.WALLET_AUTH_FAILED
1698
1692
  });
1699
- clearSession();
1693
+ await clearSession();
1700
1694
  return;
1701
1695
  }
1702
- storeSession(data.content);
1696
+ await storeSession(data.content);
1703
1697
  } else {
1704
1698
  setAuthState({
1705
1699
  step: "error",
@@ -1707,7 +1701,7 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
1707
1701
  message: "Failed to load session",
1708
1702
  errorCode: AUTH_ERROR_CODES.AUTH_FAILED
1709
1703
  });
1710
- clearSession();
1704
+ await clearSession();
1711
1705
  }
1712
1706
  }
1713
1707
 
@@ -2006,7 +2000,9 @@ var PollarClient = class {
2006
2000
  this.apiKey = config.apiKey;
2007
2001
  this.id = randomUUID();
2008
2002
  this.basePath = `${config.baseUrl || "https://sdk.api.pollar.xyz"}/v1`;
2003
+ this._log = createLogger(config.logLevel ?? "info", config.logger);
2009
2004
  this._storage = config.storage ?? defaultStorage({
2005
+ logger: this._log,
2010
2006
  onDegrade: (reason, error) => {
2011
2007
  config.onStorageDegrade?.(reason, error);
2012
2008
  this._dispatchStorageDegrade(reason, error);
@@ -2021,7 +2017,7 @@ var PollarClient = class {
2021
2017
  this._visibilityProvider = config.visibilityProvider ?? defaultVisibilityProvider();
2022
2018
  this._maxIdleMs = config.maxIdleMs;
2023
2019
  this._openAuthUrl = config.openAuthUrl ?? defaultWebOAuthOpener;
2024
- this._oauthRedirectUri = config.oauthRedirectUri ?? (isBrowser ? window.location.origin : "");
2020
+ this._oauthRedirectUri = config.oauthRedirectUri ?? (isBrowser ? window.location?.origin ?? "" : "");
2025
2021
  this._api = createApiClient(this.basePath);
2026
2022
  this._wireMiddlewares();
2027
2023
  this._networkState = { step: "connected", network: config.stellarNetwork ?? "testnet" };
@@ -2030,7 +2026,7 @@ var PollarClient = class {
2030
2026
  this._initialized = Promise.resolve();
2031
2027
  return;
2032
2028
  }
2033
- console.info(
2029
+ this._log.info(
2034
2030
  `[PollarClient] Initialized v${POLLAR_CORE_VERSION} \u2014 endpoint: ${this.basePath}, network: ${this._networkState.network}`
2035
2031
  );
2036
2032
  this._initialized = this._initialize();
@@ -2057,7 +2053,7 @@ var PollarClient = class {
2057
2053
  const sessionKey = sessionStorageKey(this._apiKeyHash);
2058
2054
  const handler = (e) => {
2059
2055
  if (e.key === sessionKey) {
2060
- this._restoreSession().catch((err) => console.error("[PollarClient] Cross-tab restore failed", err));
2056
+ this._restoreSession().catch((err) => this._log.error("[PollarClient] Cross-tab restore failed", err));
2061
2057
  }
2062
2058
  };
2063
2059
  window.addEventListener("storage", handler);
@@ -2066,7 +2062,7 @@ var PollarClient = class {
2066
2062
  try {
2067
2063
  await this._keyManager.init();
2068
2064
  } catch (err) {
2069
- console.warn("[PollarClient] KeyManager init failed; DPoP unavailable for this session", err);
2065
+ this._log.warn("[PollarClient] KeyManager init failed; DPoP unavailable for this session", err);
2070
2066
  }
2071
2067
  await this._restoreSession();
2072
2068
  this._visibilityUnsubscribe = this._visibilityProvider.onChange((visible) => {
@@ -2107,7 +2103,7 @@ var PollarClient = class {
2107
2103
  try {
2108
2104
  self._requestBodyCache.set(request, await request.clone().arrayBuffer());
2109
2105
  } catch (err) {
2110
- console.warn("[PollarClient] Could not snapshot request body for retry", err);
2106
+ this._log.warn("[PollarClient] Could not snapshot request body for retry", err);
2111
2107
  }
2112
2108
  }
2113
2109
  const isRefresh = request.url.includes("/auth/refresh");
@@ -2166,7 +2162,7 @@ var PollarClient = class {
2166
2162
  this._keyManager
2167
2163
  );
2168
2164
  } catch (err) {
2169
- console.warn("[PollarClient] DPoP proof build failed", err);
2165
+ this._log.warn("[PollarClient] DPoP proof build failed", err);
2170
2166
  return null;
2171
2167
  }
2172
2168
  }
@@ -2220,7 +2216,7 @@ var PollarClient = class {
2220
2216
  async _doRefresh() {
2221
2217
  const refreshToken = this._session?.token?.refreshToken;
2222
2218
  if (!refreshToken) {
2223
- console.warn("[PollarClient] Refresh skipped: no refresh token in session");
2219
+ this._log.warn("[PollarClient] Refresh skipped: no refresh token in session");
2224
2220
  await this._clearSession();
2225
2221
  throw new Error("No refresh token available");
2226
2222
  }
@@ -2231,18 +2227,18 @@ var PollarClient = class {
2231
2227
  data = response.data;
2232
2228
  error = response.error;
2233
2229
  } catch (err) {
2234
- console.error("[PollarClient] /auth/refresh request threw", err);
2230
+ this._log.error("[PollarClient] /auth/refresh request threw", err);
2235
2231
  await this._clearSession();
2236
2232
  throw err;
2237
2233
  }
2238
2234
  if (error || !data) {
2239
- console.error("[PollarClient] /auth/refresh returned error", { error });
2235
+ this._log.error("[PollarClient] /auth/refresh returned error", { error });
2240
2236
  await this._clearSession();
2241
2237
  throw new Error("Refresh failed");
2242
2238
  }
2243
2239
  const successData = data;
2244
2240
  if (!successData.success || !successData.content?.token) {
2245
- console.error("[PollarClient] /auth/refresh response malformed", {
2241
+ this._log.error("[PollarClient] /auth/refresh response malformed", {
2246
2242
  success: successData.success,
2247
2243
  hasToken: !!successData.content?.token
2248
2244
  });
@@ -2251,7 +2247,7 @@ var PollarClient = class {
2251
2247
  }
2252
2248
  const newToken = successData.content.token;
2253
2249
  if (typeof newToken.accessToken !== "string" || typeof newToken.refreshToken !== "string" || typeof newToken.expiresAt !== "number") {
2254
- console.error("[PollarClient] /auth/refresh token shape invalid", {
2250
+ this._log.error("[PollarClient] /auth/refresh token shape invalid", {
2255
2251
  accessToken: typeof newToken.accessToken,
2256
2252
  refreshToken: typeof newToken.refreshToken,
2257
2253
  expiresAt: typeof newToken.expiresAt
@@ -2263,9 +2259,9 @@ var PollarClient = class {
2263
2259
  try {
2264
2260
  this._session = { ...this._session, token: newToken };
2265
2261
  await writeStorage(this._storage, this.apiKeyHash, this._session);
2266
- console.info("[PollarClient] Tokens refreshed");
2262
+ this._log.info("[PollarClient] Tokens refreshed");
2267
2263
  } catch (err) {
2268
- console.error("[PollarClient] Failed to persist refreshed session", err);
2264
+ this._log.error("[PollarClient] Failed to persist refreshed session", err);
2269
2265
  }
2270
2266
  this._scheduleNextRefresh();
2271
2267
  }
@@ -2319,7 +2315,7 @@ var PollarClient = class {
2319
2315
  try {
2320
2316
  await this.refresh();
2321
2317
  } catch (err) {
2322
- console.warn("[PollarClient] Proactive refresh failed; session cleared", err);
2318
+ this._log.warn("[PollarClient] Proactive refresh failed; session cleared", err);
2323
2319
  }
2324
2320
  }
2325
2321
  _clearRefreshTimer() {
@@ -2365,7 +2361,7 @@ var PollarClient = class {
2365
2361
  try {
2366
2362
  cb(reason, error);
2367
2363
  } catch (err) {
2368
- console.error("[PollarClient] onStorageDegrade listener threw", err);
2364
+ this._log.error("[PollarClient] onStorageDegrade listener threw", err);
2369
2365
  }
2370
2366
  }
2371
2367
  }
@@ -2485,20 +2481,20 @@ var PollarClient = class {
2485
2481
  warnServerSide("logout");
2486
2482
  return;
2487
2483
  }
2488
- console.info("[PollarClient] Logout requested", { everywhere: !!options.everywhere });
2484
+ this._log.info("[PollarClient] Logout requested", { everywhere: !!options.everywhere });
2489
2485
  if (this._session?.token?.accessToken) {
2490
2486
  try {
2491
2487
  await this._api.POST("/auth/logout", {
2492
2488
  body: options.everywhere ? { everywhere: true } : {}
2493
2489
  });
2494
2490
  } catch (err) {
2495
- console.warn("[PollarClient] Server logout failed (continuing with local clear)", err);
2491
+ this._log.warn("[PollarClient] Server logout failed (continuing with local clear)", err);
2496
2492
  }
2497
2493
  }
2498
2494
  try {
2499
2495
  await this._clearSession();
2500
2496
  } catch (err) {
2501
- console.warn("[PollarClient] Local logout cleanup failed", err);
2497
+ this._log.warn("[PollarClient] Local logout cleanup failed", err);
2502
2498
  }
2503
2499
  }
2504
2500
  /** Convenience: revoke every active session for this user (all devices). */
@@ -2574,6 +2570,14 @@ var PollarClient = class {
2574
2570
  getNetworkState() {
2575
2571
  return this._networkState;
2576
2572
  }
2573
+ /**
2574
+ * The client's level-gated logger (built from `logLevel` / `logger`). Exposed
2575
+ * so the runtime layer (`@pollar/react`) can route its own logs through the
2576
+ * same level and sink instead of calling `console` directly.
2577
+ */
2578
+ getLogger() {
2579
+ return this._log;
2580
+ }
2577
2581
  setNetwork(network) {
2578
2582
  this._setNetworkState({ step: "connected", network });
2579
2583
  }
@@ -2722,7 +2726,7 @@ var PollarClient = class {
2722
2726
  this._setTransactionState({ step: "error", phase: "building", ...details && { details } });
2723
2727
  return { status: "error", ...details && { details } };
2724
2728
  } catch (err) {
2725
- console.error("[PollarClient] buildTx failed", err);
2729
+ this._log.error("[PollarClient] buildTx failed", err);
2726
2730
  this._setTransactionState({ step: "error", phase: "building" });
2727
2731
  return { status: "error" };
2728
2732
  }
@@ -3235,6 +3239,7 @@ var PollarClient = class {
3235
3239
  _flowDeps(signal) {
3236
3240
  return {
3237
3241
  api: this._api,
3242
+ logger: this._log,
3238
3243
  basePath: this.basePath,
3239
3244
  // SSE status streaming works on web; React Native's `fetch` has no
3240
3245
  // readable `response.body`, so those clients poll the non-streaming
@@ -3287,12 +3292,12 @@ var PollarClient = class {
3287
3292
  }
3288
3293
  _handleFlowError(error) {
3289
3294
  if (error instanceof Error && error.name === "AbortError") {
3290
- console.info("[PollarClient] Login cancelled");
3295
+ this._log.debug("[PollarClient] Login cancelled");
3291
3296
  this._setAuthState({ step: "idle" });
3292
3297
  return;
3293
3298
  }
3294
3299
  if (error instanceof Error && error.code === AUTH_ERROR_CODES.WALLET_RESOLVER_TIMEOUT) {
3295
- console.error("[PollarClient]", error.message);
3300
+ this._log.error("[PollarClient]", error.message);
3296
3301
  this._setAuthState({
3297
3302
  step: "error",
3298
3303
  previousStep: this._authState.step,
@@ -3301,7 +3306,7 @@ var PollarClient = class {
3301
3306
  });
3302
3307
  return;
3303
3308
  }
3304
- console.error("[PollarClient] Unexpected error in auth flow", error);
3309
+ this._log.error("[PollarClient] Unexpected error in auth flow", error);
3305
3310
  this._setAuthState({
3306
3311
  step: "error",
3307
3312
  previousStep: this._authState.step,
@@ -3310,22 +3315,25 @@ var PollarClient = class {
3310
3315
  });
3311
3316
  }
3312
3317
  async _restoreSession() {
3313
- this._session = await readStorage(this._storage, this.apiKeyHash);
3318
+ this._session = await readStorage(this._storage, this.apiKeyHash, this._log);
3314
3319
  if (this._session) {
3315
3320
  const storedType = await readWalletType(this._storage, this.apiKeyHash);
3316
3321
  if (storedType) {
3317
3322
  try {
3318
3323
  this._walletAdapter = await this._resolveWalletAdapter(storedType);
3319
3324
  } catch (err) {
3320
- console.warn("[PollarClient] Could not restore wallet adapter for stored id", { id: storedType, err });
3325
+ this._log.warn("[PollarClient] Could not restore wallet adapter for stored id", { id: storedType, err });
3321
3326
  }
3322
3327
  }
3323
- console.info("[PollarClient] Session restored from storage");
3328
+ this._log.info("[PollarClient] Session restored from storage");
3324
3329
  this._setAuthState({ step: "authenticated", session: this._session, verified: false });
3325
3330
  this._scheduleNextRefresh();
3326
3331
  void this._resume();
3327
3332
  } else {
3328
- console.info("[PollarClient] No session in storage");
3333
+ this._log.info("[PollarClient] No session in storage");
3334
+ if (this._authState.step !== "idle") {
3335
+ await this._clearSession();
3336
+ }
3329
3337
  }
3330
3338
  }
3331
3339
  /**
@@ -3356,13 +3364,13 @@ var PollarClient = class {
3356
3364
  this._setAuthState({ step: "authenticated", session: this._session, verified: true });
3357
3365
  } catch (err) {
3358
3366
  if (err?.name === "AbortError") return;
3359
- console.warn("[PollarClient] resume failed (network); will retry", err);
3367
+ this._log.warn("[PollarClient] resume failed (network); will retry", err);
3360
3368
  } finally {
3361
3369
  if (this._resumeController === controller) this._resumeController = null;
3362
3370
  }
3363
3371
  }
3364
3372
  async _storeSession(session) {
3365
- console.info("[PollarClient] Session stored");
3373
+ this._log.info("[PollarClient] Session stored");
3366
3374
  const w = session.wallet;
3367
3375
  const persisted = {
3368
3376
  clientSessionId: session.clientSessionId,
@@ -3372,8 +3380,11 @@ var PollarClient = class {
3372
3380
  user: session.user,
3373
3381
  // The wire response still carries the legacy `publicKey` alias (kept for
3374
3382
  // older SDKs); the persisted session standardizes on `address` only.
3383
+ // The wire also still emits the legacy type `'custodial'` (unchanged for
3384
+ // SDKs ≤0.8.x); we remap it to `'internal'` here so the SDK surface and
3385
+ // persisted session speak one vocabulary while the wire stays compatible.
3375
3386
  wallet: {
3376
- type: w.type,
3387
+ type: w.type === "custodial" ? "internal" : w.type,
3377
3388
  address: w.address ?? w.publicKey ?? null,
3378
3389
  ...w.existsOnStellar !== void 0 ? { existsOnStellar: w.existsOnStellar } : {},
3379
3390
  ...w.createdAt !== void 0 ? { createdAt: w.createdAt } : {},
@@ -3397,7 +3408,7 @@ var PollarClient = class {
3397
3408
  this._scheduleNextRefresh();
3398
3409
  }
3399
3410
  async _clearSession() {
3400
- console.info("[PollarClient] Session cleared");
3411
+ this._log.info("[PollarClient] Session cleared");
3401
3412
  this._clearRefreshTimer();
3402
3413
  this._session = null;
3403
3414
  this._profile = null;
@@ -3406,7 +3417,7 @@ var PollarClient = class {
3406
3417
  try {
3407
3418
  await this._keyManager.reset();
3408
3419
  } catch (err) {
3409
- console.warn("[PollarClient] KeyManager reset failed during clearSession", err);
3420
+ this._log.warn("[PollarClient] KeyManager reset failed during clearSession", err);
3410
3421
  }
3411
3422
  await removeStorage(this._storage, this.apiKeyHash);
3412
3423
  this._transactionState = null;
@@ -3418,17 +3429,17 @@ var PollarClient = class {
3418
3429
  _setNetworkState(next) {
3419
3430
  this._networkState = next;
3420
3431
  const label = next.step === "connected" ? next.network : next.step;
3421
- console.info(`[PollarClient] network:${label}`);
3432
+ this._log.debug(`[PollarClient] network:${label}`);
3422
3433
  for (const cb of this._networkStateListeners) cb(next);
3423
3434
  }
3424
3435
  _setAuthState(next) {
3425
3436
  this._authState = next;
3426
- console.info(`[PollarClient] auth:${next.step}`);
3437
+ this._log.debug(`[PollarClient] auth:${next.step}`);
3427
3438
  for (const cb of this._authStateListeners) cb(next);
3428
3439
  }
3429
3440
  _setTransactionState(next) {
3430
3441
  this._transactionState = next;
3431
- console.info(`[PollarClient] transaction:${next.step}`);
3442
+ this._log.debug(`[PollarClient] transaction:${next.step}`);
3432
3443
  for (const cb of this._transactionStateListeners) cb(next);
3433
3444
  }
3434
3445
  /**
@@ -3532,7 +3543,6 @@ var WebCryptoKeyManager = class {
3532
3543
  if (this.keyPair) return;
3533
3544
  if (!this._initPromise) {
3534
3545
  this._initPromise = this._doInit().catch((err) => {
3535
- console.error("[PollarClient:keys] WebCryptoKeyManager init failed", err);
3536
3546
  this._initPromise = null;
3537
3547
  throw err;
3538
3548
  });
@@ -3682,6 +3692,7 @@ exports.canonicalEcJwk = canonicalEcJwk;
3682
3692
  exports.claimDistributionRule = claimDistributionRule;
3683
3693
  exports.computeJwkThumbprint = computeJwkThumbprint;
3684
3694
  exports.createLocalStorageAdapter = createLocalStorageAdapter;
3695
+ exports.createLogger = createLogger;
3685
3696
  exports.createMemoryAdapter = createMemoryAdapter;
3686
3697
  exports.createOffRamp = createOffRamp;
3687
3698
  exports.createOnRamp = createOnRamp;