@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/README.md +35 -0
- package/dist/index.d.mts +56 -3
- package/dist/index.d.ts +56 -3
- package/dist/index.js +118 -106
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +118 -107
- package/dist/index.mjs.map +1 -1
- package/dist/index.rn.d.mts +1 -1
- package/dist/index.rn.d.ts +1 -1
- package/dist/index.rn.js +118 -107
- package/dist/index.rn.js.map +1 -1
- package/dist/index.rn.mjs +118 -108
- package/dist/index.rn.mjs.map +1 -1
- package/package.json +1 -1
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.
|
|
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(() =>
|
|
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.
|
|
1402
|
+
url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
|
|
1409
1403
|
url.searchParams.set("origin", window.location.origin);
|
|
1410
|
-
|
|
1411
|
-
const result = await
|
|
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.
|
|
1415
|
+
url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
|
|
1422
1416
|
url.searchParams.set("origin", window.location.origin);
|
|
1423
|
-
|
|
1424
|
-
const result = await
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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"] !== "
|
|
1508
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2326
|
+
this._log.info("[PollarClient] Tokens refreshed");
|
|
2331
2327
|
} catch (err) {
|
|
2332
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3389
|
+
this._log.warn("[PollarClient] Could not restore wallet adapter for stored id", { id: storedType, err });
|
|
3385
3390
|
}
|
|
3386
3391
|
}
|
|
3387
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|