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