@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.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.
|
|
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(() =>
|
|
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.
|
|
1338
|
+
url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
|
|
1345
1339
|
url.searchParams.set("origin", window.location.origin);
|
|
1346
|
-
|
|
1347
|
-
const result = await
|
|
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.
|
|
1351
|
+
url.searchParams.set("callback", `${window.location.origin}/albedo-callback`);
|
|
1358
1352
|
url.searchParams.set("origin", window.location.origin);
|
|
1359
|
-
|
|
1360
|
-
const result = await
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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"] !== "
|
|
1444
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2262
|
+
this._log.info("[PollarClient] Tokens refreshed");
|
|
2267
2263
|
} catch (err) {
|
|
2268
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3325
|
+
this._log.warn("[PollarClient] Could not restore wallet adapter for stored id", { id: storedType, err });
|
|
3321
3326
|
}
|
|
3322
3327
|
}
|
|
3323
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|