@pollar/core 0.9.0 → 0.9.1-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/expo-secure-store.js +1 -1
- package/dist/adapters/expo-secure-store.js.map +1 -1
- package/dist/adapters/expo-secure-store.mjs +1 -1
- package/dist/adapters/expo-secure-store.mjs.map +1 -1
- package/dist/adapters/react-native-appstate.js +1 -1
- package/dist/adapters/react-native-appstate.js.map +1 -1
- package/dist/adapters/react-native-appstate.mjs +1 -1
- package/dist/adapters/react-native-appstate.mjs.map +1 -1
- package/dist/adapters/react-native-keychain.js +1 -1
- package/dist/adapters/react-native-keychain.js.map +1 -1
- package/dist/adapters/react-native-keychain.mjs +1 -1
- package/dist/adapters/react-native-keychain.mjs.map +1 -1
- package/dist/index.d.mts +16 -3
- package/dist/index.d.ts +16 -3
- package/dist/index.js +197 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +197 -54
- package/dist/index.mjs.map +1 -1
- package/dist/index.rn.js +197 -54
- package/dist/index.rn.js.map +1 -1
- package/dist/index.rn.mjs +197 -54
- package/dist/index.rn.mjs.map +1 -1
- package/package.json +6 -6
package/dist/index.rn.mjs
CHANGED
|
@@ -1019,6 +1019,17 @@ function createLogger(level = "info", sink = console) {
|
|
|
1019
1019
|
return { error: gate("error"), warn: gate("warn"), info: gate("info"), debug: gate("debug") };
|
|
1020
1020
|
}
|
|
1021
1021
|
|
|
1022
|
+
// src/lib/logging.ts
|
|
1023
|
+
var SENSITIVE_BODY_KEYS = /* @__PURE__ */ new Set(["email", "code", "walletAddress", "dpopJwk", "response", "refreshToken"]);
|
|
1024
|
+
function redactBody(body) {
|
|
1025
|
+
if (!body || typeof body !== "object") return body;
|
|
1026
|
+
const out = {};
|
|
1027
|
+
for (const [key, value] of Object.entries(body)) {
|
|
1028
|
+
out[key] = SENSITIVE_BODY_KEYS.has(key) ? "[redacted]" : value;
|
|
1029
|
+
}
|
|
1030
|
+
return out;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1022
1033
|
// src/storage/web.ts
|
|
1023
1034
|
var LOG_PREFIX = "[PollarClient:storage]";
|
|
1024
1035
|
function createMemoryAdapter() {
|
|
@@ -1107,7 +1118,7 @@ function defaultStorage(options = {}) {
|
|
|
1107
1118
|
}
|
|
1108
1119
|
|
|
1109
1120
|
// src/version.ts
|
|
1110
|
-
var POLLAR_CORE_VERSION = "0.9.0" ;
|
|
1121
|
+
var POLLAR_CORE_VERSION = "0.9.1-rc.0" ;
|
|
1111
1122
|
|
|
1112
1123
|
// src/visibility/noop.ts
|
|
1113
1124
|
function createNoopVisibilityProvider() {
|
|
@@ -1279,10 +1290,13 @@ function openAlbedoPopup(url) {
|
|
|
1279
1290
|
}
|
|
1280
1291
|
function waitForAlbedoPopup() {
|
|
1281
1292
|
return new Promise((resolve, reject) => {
|
|
1282
|
-
const timeout = setTimeout(
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1293
|
+
const timeout = setTimeout(
|
|
1294
|
+
() => {
|
|
1295
|
+
window.removeEventListener("message", handler);
|
|
1296
|
+
reject(new Error("Albedo response timeout"));
|
|
1297
|
+
},
|
|
1298
|
+
2 * 60 * 1e3
|
|
1299
|
+
);
|
|
1286
1300
|
function handler(event) {
|
|
1287
1301
|
if (event.origin !== window.location.origin || event.data?.type !== "ALBEDO_RESULT") return;
|
|
1288
1302
|
clearTimeout(timeout);
|
|
@@ -1642,6 +1656,16 @@ function waitForSessionReady(args) {
|
|
|
1642
1656
|
return useStreaming ? streamUntilFound(api, clientSessionId, check, retryDelayMs ?? 200, signal, logger) : pollUntilFound(baseUrl, clientSessionId, check, retryDelayMs ?? 500, signal, logger);
|
|
1643
1657
|
}
|
|
1644
1658
|
|
|
1659
|
+
// src/client/auth/logging.ts
|
|
1660
|
+
function logApiError(logger, route, detail = {}, level = "error") {
|
|
1661
|
+
const { body, error, data } = detail;
|
|
1662
|
+
logger[level](`[PollarClient:auth] ${route} failed`, {
|
|
1663
|
+
route,
|
|
1664
|
+
...body !== void 0 ? { body: redactBody(body) } : {},
|
|
1665
|
+
cause: error ?? data
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1645
1669
|
// src/client/auth/authenticate.ts
|
|
1646
1670
|
async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
1647
1671
|
const { api, logger, basePath, useStreaming, signal, setAuthState, storeSession, clearSession } = deps;
|
|
@@ -1659,6 +1683,7 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
|
1659
1683
|
} catch (err) {
|
|
1660
1684
|
if (err instanceof SessionStatusError) {
|
|
1661
1685
|
const expired = err.code === "EXPIRED_CLIENT_ID";
|
|
1686
|
+
logApiError(logger, "session status", { data: err });
|
|
1662
1687
|
setAuthState({
|
|
1663
1688
|
step: "error",
|
|
1664
1689
|
previousStep: "authenticating",
|
|
@@ -1671,14 +1696,12 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
|
1671
1696
|
throw err;
|
|
1672
1697
|
}
|
|
1673
1698
|
const dpopJwk = await deps.getPublicJwk();
|
|
1674
|
-
const
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
signal
|
|
1681
|
-
});
|
|
1699
|
+
const body = {
|
|
1700
|
+
clientSessionId,
|
|
1701
|
+
dpopJwk,
|
|
1702
|
+
...deps.deviceLabel ? { deviceLabel: deps.deviceLabel } : {}
|
|
1703
|
+
};
|
|
1704
|
+
const { data, error } = await api.POST("/auth/login", { body, signal });
|
|
1682
1705
|
if (data?.code === "SDK_LOGIN_SUCCESS" && isValidSession(data?.content, logger)) {
|
|
1683
1706
|
const sessionWallet = data.content.data?.providers?.wallet?.address;
|
|
1684
1707
|
if (expectedWallet && sessionWallet !== expectedWallet) {
|
|
@@ -1693,6 +1716,7 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
|
1693
1716
|
}
|
|
1694
1717
|
await storeSession(data.content);
|
|
1695
1718
|
} else {
|
|
1719
|
+
if (!error) logApiError(logger, "POST /auth/login", { body, data });
|
|
1696
1720
|
setAuthState({
|
|
1697
1721
|
step: "error",
|
|
1698
1722
|
previousStep: "authenticating",
|
|
@@ -1705,10 +1729,11 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
|
1705
1729
|
|
|
1706
1730
|
// src/client/auth/deps.ts
|
|
1707
1731
|
async function createAuthSession(deps) {
|
|
1708
|
-
const { api, signal, setAuthState } = deps;
|
|
1732
|
+
const { api, logger, signal, setAuthState } = deps;
|
|
1709
1733
|
setAuthState({ step: "creating_session" });
|
|
1710
1734
|
const { data, error } = await api.POST("/auth/session", { signal });
|
|
1711
1735
|
if (error || !data?.success) {
|
|
1736
|
+
if (!error) logApiError(logger, "POST /auth/session", { data });
|
|
1712
1737
|
setAuthState({
|
|
1713
1738
|
step: "error",
|
|
1714
1739
|
previousStep: "creating_session",
|
|
@@ -1727,13 +1752,12 @@ async function initEmailSession(deps) {
|
|
|
1727
1752
|
deps.setAuthState({ step: "entering_email", clientSessionId });
|
|
1728
1753
|
}
|
|
1729
1754
|
async function sendEmailCode(email, clientSessionId, deps) {
|
|
1730
|
-
const { api, signal, setAuthState } = deps;
|
|
1755
|
+
const { api, logger, signal, setAuthState } = deps;
|
|
1731
1756
|
setAuthState({ step: "sending_email", email });
|
|
1732
|
-
const {
|
|
1733
|
-
|
|
1734
|
-
signal
|
|
1735
|
-
});
|
|
1757
|
+
const body = { clientSessionId, email };
|
|
1758
|
+
const { data, error } = await api.POST("/auth/email", { body, signal });
|
|
1736
1759
|
if (error || !data?.success) {
|
|
1760
|
+
if (!error) logApiError(logger, "POST /auth/email", { body, data });
|
|
1737
1761
|
setAuthState({
|
|
1738
1762
|
step: "error",
|
|
1739
1763
|
previousStep: "sending_email",
|
|
@@ -1745,18 +1769,17 @@ async function sendEmailCode(email, clientSessionId, deps) {
|
|
|
1745
1769
|
setAuthState({ step: "entering_code", clientSessionId, email });
|
|
1746
1770
|
}
|
|
1747
1771
|
async function verifyAndAuthenticate(code, clientSessionId, email, deps) {
|
|
1748
|
-
const { api, signal, setAuthState } = deps;
|
|
1772
|
+
const { api, logger, signal, setAuthState } = deps;
|
|
1749
1773
|
setAuthState({ step: "verifying_email_code", clientSessionId, email });
|
|
1750
|
-
const {
|
|
1751
|
-
|
|
1752
|
-
signal
|
|
1753
|
-
});
|
|
1774
|
+
const body = { clientSessionId, code };
|
|
1775
|
+
const { data, error } = await api.POST("/auth/email/verify-code", { body, signal });
|
|
1754
1776
|
if (data?.code === "SDK_EMAIL_CODE_VERIFIED") {
|
|
1755
1777
|
await authenticate(clientSessionId, deps);
|
|
1756
1778
|
return;
|
|
1757
1779
|
}
|
|
1758
1780
|
const errCode = error?.error ?? data?.code;
|
|
1759
1781
|
if (errCode === "SDK_EMAIL_CODE_EXPIRED") {
|
|
1782
|
+
if (!error) logApiError(logger, "POST /auth/email/verify-code", { body, data });
|
|
1760
1783
|
setAuthState({
|
|
1761
1784
|
step: "error",
|
|
1762
1785
|
previousStep: "verifying_email_code",
|
|
@@ -1768,6 +1791,7 @@ async function verifyAndAuthenticate(code, clientSessionId, email, deps) {
|
|
|
1768
1791
|
return;
|
|
1769
1792
|
}
|
|
1770
1793
|
if (errCode === "INVALID_EMAIL_CODE" || errCode === "SDK_EMAIL_CODE_INVALID") {
|
|
1794
|
+
if (!error) logApiError(logger, "POST /auth/email/verify-code", { body, data });
|
|
1771
1795
|
setAuthState({
|
|
1772
1796
|
step: "error",
|
|
1773
1797
|
previousStep: "verifying_email_code",
|
|
@@ -1778,6 +1802,7 @@ async function verifyAndAuthenticate(code, clientSessionId, email, deps) {
|
|
|
1778
1802
|
});
|
|
1779
1803
|
return;
|
|
1780
1804
|
}
|
|
1805
|
+
if (!error) logApiError(logger, "POST /auth/email/verify-code", { body, data });
|
|
1781
1806
|
setAuthState({
|
|
1782
1807
|
step: "error",
|
|
1783
1808
|
previousStep: "verifying_email_code",
|
|
@@ -1827,10 +1852,73 @@ async function loginOAuth(provider, deps) {
|
|
|
1827
1852
|
await authenticate(clientSessionId, deps);
|
|
1828
1853
|
}
|
|
1829
1854
|
|
|
1855
|
+
// src/client/auth/errorMessages.ts
|
|
1856
|
+
var CATALOG = {
|
|
1857
|
+
// ── Smart-account deploy / sponsor wallet ──────────────────────────────────
|
|
1858
|
+
SPONSOR_NOT_FUNDED: {
|
|
1859
|
+
message: "This app can't create your wallet yet \u2014 its sponsor account isn't funded. Please contact the app's developer.",
|
|
1860
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1861
|
+
},
|
|
1862
|
+
APP_WALLET_NOT_FOUND: {
|
|
1863
|
+
message: "This app isn't fully set up to create wallets yet. Please contact the app's developer.",
|
|
1864
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1865
|
+
},
|
|
1866
|
+
WALLET_NOT_FOUND: {
|
|
1867
|
+
message: "This app isn't fully set up to create wallets yet. Please contact the app's developer.",
|
|
1868
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1869
|
+
},
|
|
1870
|
+
PASSKEY_DEPLOY_FAILED: {
|
|
1871
|
+
message: "We couldn't finish creating your wallet. Please try again in a moment.",
|
|
1872
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1873
|
+
},
|
|
1874
|
+
// ── Passkey ceremony ────────────────────────────────────────────────────────
|
|
1875
|
+
PASSKEY_ALREADY_REGISTERED: {
|
|
1876
|
+
message: "A passkey is already registered for this account. Try signing in instead.",
|
|
1877
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1878
|
+
},
|
|
1879
|
+
PASSKEY_UNKNOWN_CREDENTIAL: {
|
|
1880
|
+
message: "We don't recognize this passkey. Try creating a new one.",
|
|
1881
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1882
|
+
},
|
|
1883
|
+
PASSKEY_VERIFICATION_FAILED: {
|
|
1884
|
+
message: "We couldn't verify your passkey. Please try again.",
|
|
1885
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1886
|
+
},
|
|
1887
|
+
PASSKEY_CHALLENGE_MISSING: {
|
|
1888
|
+
message: "Your passkey session expired. Please start again.",
|
|
1889
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1890
|
+
},
|
|
1891
|
+
// ── On-chain transaction failures (surfaced during deploy/transfer) ─────────
|
|
1892
|
+
TX_INSUFFICIENT_BALANCE: {
|
|
1893
|
+
message: "Insufficient balance to complete this transaction.",
|
|
1894
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1895
|
+
},
|
|
1896
|
+
TX_DESTINATION_NOT_FOUND: {
|
|
1897
|
+
message: "The destination account doesn't exist on the network yet.",
|
|
1898
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1899
|
+
},
|
|
1900
|
+
TX_NO_TRUSTLINE: {
|
|
1901
|
+
message: "The destination can't receive this asset yet (no trustline).",
|
|
1902
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1903
|
+
},
|
|
1904
|
+
TX_BAD_SEQUENCE: {
|
|
1905
|
+
message: "Something went out of sync. Please try again.",
|
|
1906
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1907
|
+
}
|
|
1908
|
+
};
|
|
1909
|
+
function resolveAuthError(code, fallbackMessage) {
|
|
1910
|
+
if (code && CATALOG[code]) return CATALOG[code];
|
|
1911
|
+
return { message: fallbackMessage, errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED };
|
|
1912
|
+
}
|
|
1913
|
+
function extractErrorCode(error, data) {
|
|
1914
|
+
return error?.code ?? data?.code ?? void 0;
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1830
1917
|
// src/client/auth/passkeyFlow.ts
|
|
1831
1918
|
async function smartWalletFlow(deps, mode) {
|
|
1832
|
-
const { api, signal, setAuthState, passkey } = deps;
|
|
1919
|
+
const { api, logger, signal, setAuthState, passkey } = deps;
|
|
1833
1920
|
if (!passkey) {
|
|
1921
|
+
logger.error("[PollarClient:auth] passkey ceremony not configured");
|
|
1834
1922
|
setAuthState({
|
|
1835
1923
|
step: "error",
|
|
1836
1924
|
previousStep: "creating_session",
|
|
@@ -1842,38 +1930,44 @@ async function smartWalletFlow(deps, mode) {
|
|
|
1842
1930
|
const clientSessionId = await createAuthSession(deps);
|
|
1843
1931
|
if (!clientSessionId) return;
|
|
1844
1932
|
try {
|
|
1845
|
-
const
|
|
1846
|
-
|
|
1933
|
+
const challengeBody = { clientSessionId };
|
|
1934
|
+
const { data: challengeData, error: challengeError } = await api.POST("/auth/passkey/challenge", {
|
|
1935
|
+
body: challengeBody,
|
|
1847
1936
|
signal
|
|
1848
1937
|
});
|
|
1849
1938
|
const challenge = challengeData?.content?.challenge;
|
|
1850
1939
|
if (!challengeData?.success || !challenge) {
|
|
1851
|
-
|
|
1940
|
+
if (!challengeError) logApiError(logger, "POST /auth/passkey/challenge", { body: challengeBody, data: challengeData });
|
|
1941
|
+
return failPasskey(setAuthState, extractErrorCode(challengeError, challengeData), "Failed to start passkey");
|
|
1852
1942
|
}
|
|
1853
1943
|
setAuthState({ step: "creating_passkey" });
|
|
1854
1944
|
const ceremony = await passkey({ challenge, mode });
|
|
1855
1945
|
const response = ceremony.response;
|
|
1856
1946
|
if (ceremony.kind === "register") {
|
|
1857
1947
|
setAuthState({ step: "deploying_smart_account" });
|
|
1858
|
-
const
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1948
|
+
const body = { clientSessionId, response };
|
|
1949
|
+
const { data, error } = await api.POST("/auth/passkey/register", { body, signal });
|
|
1950
|
+
if (!data?.success) {
|
|
1951
|
+
if (!error) logApiError(logger, "POST /auth/passkey/register", { body, data });
|
|
1952
|
+
return failPasskey(setAuthState, extractErrorCode(error, data), "Passkey registration failed");
|
|
1953
|
+
}
|
|
1863
1954
|
} else {
|
|
1864
|
-
const
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1955
|
+
const body = { clientSessionId, response };
|
|
1956
|
+
const { data, error } = await api.POST("/auth/passkey/login", { body, signal });
|
|
1957
|
+
if (!data?.success) {
|
|
1958
|
+
if (!error) logApiError(logger, "POST /auth/passkey/login", { body, data });
|
|
1959
|
+
return failPasskey(setAuthState, extractErrorCode(error, data), "Passkey authentication failed");
|
|
1960
|
+
}
|
|
1869
1961
|
}
|
|
1870
|
-
} catch {
|
|
1871
|
-
|
|
1962
|
+
} catch (err) {
|
|
1963
|
+
logApiError(logger, "passkey ceremony", { error: err });
|
|
1964
|
+
return failPasskey(setAuthState, void 0, "Passkey login failed");
|
|
1872
1965
|
}
|
|
1873
1966
|
await authenticate(clientSessionId, deps);
|
|
1874
1967
|
}
|
|
1875
|
-
function failPasskey(setAuthState,
|
|
1876
|
-
|
|
1968
|
+
function failPasskey(setAuthState, code, fallbackMessage) {
|
|
1969
|
+
const { message, errorCode } = resolveAuthError(code, fallbackMessage);
|
|
1970
|
+
setAuthState({ step: "error", previousStep: "creating_passkey", message, errorCode });
|
|
1877
1971
|
}
|
|
1878
1972
|
|
|
1879
1973
|
// src/client/auth/walletFlow.ts
|
|
@@ -1890,7 +1984,7 @@ function withSignal(promise, signal) {
|
|
|
1890
1984
|
]);
|
|
1891
1985
|
}
|
|
1892
1986
|
async function loginWallet(type, deps) {
|
|
1893
|
-
const { api, signal, setAuthState } = deps;
|
|
1987
|
+
const { api, logger, signal, setAuthState } = deps;
|
|
1894
1988
|
const clientSessionId = await createAuthSession(deps);
|
|
1895
1989
|
if (!clientSessionId) return;
|
|
1896
1990
|
let connectedWallet;
|
|
@@ -1906,11 +2000,10 @@ async function loginWallet(type, deps) {
|
|
|
1906
2000
|
connectedWallet = address;
|
|
1907
2001
|
deps.storeWalletAdapter(adapter, type);
|
|
1908
2002
|
setAuthState({ step: "authenticating_wallet" });
|
|
1909
|
-
const {
|
|
1910
|
-
|
|
1911
|
-
signal
|
|
1912
|
-
});
|
|
2003
|
+
const body = { clientSessionId, walletAddress: address };
|
|
2004
|
+
const { data: walletData, error: walletError } = await api.POST("/auth/wallet", { body, signal });
|
|
1913
2005
|
if (walletError || !walletData?.success) {
|
|
2006
|
+
if (!walletError) logApiError(logger, "POST /auth/wallet", { body, data: walletData });
|
|
1914
2007
|
setAuthState({
|
|
1915
2008
|
step: "error",
|
|
1916
2009
|
previousStep: "authenticating_wallet",
|
|
@@ -1919,7 +2012,8 @@ async function loginWallet(type, deps) {
|
|
|
1919
2012
|
});
|
|
1920
2013
|
return;
|
|
1921
2014
|
}
|
|
1922
|
-
} catch {
|
|
2015
|
+
} catch (err) {
|
|
2016
|
+
logApiError(logger, "wallet connect", { error: err });
|
|
1923
2017
|
setAuthState({
|
|
1924
2018
|
step: "error",
|
|
1925
2019
|
previousStep: "connecting_wallet",
|
|
@@ -2125,28 +2219,77 @@ var PollarClient = class {
|
|
|
2125
2219
|
onResponse: async ({ request, response }) => {
|
|
2126
2220
|
const newNonce = response.headers.get("DPoP-Nonce");
|
|
2127
2221
|
if (newNonce) self._dpopNonce = newNonce;
|
|
2128
|
-
if (response.status !== 401) return response;
|
|
2222
|
+
if (response.status !== 401) return self._logHttp(request, response);
|
|
2129
2223
|
const wwwAuth = response.headers.get("WWW-Authenticate") ?? "";
|
|
2130
2224
|
const isNonceChallenge = wwwAuth.includes("use_dpop_nonce");
|
|
2131
2225
|
if (request.url.includes("/auth/refresh")) {
|
|
2132
|
-
if (isNonceChallenge) return self._retryRequest(request);
|
|
2133
|
-
return response;
|
|
2226
|
+
if (isNonceChallenge) return self._logHttp(request, await self._retryRequest(request));
|
|
2227
|
+
return self._logHttp(request, response);
|
|
2134
2228
|
}
|
|
2135
2229
|
if (!isNonceChallenge) {
|
|
2136
2230
|
try {
|
|
2137
2231
|
await self.refresh();
|
|
2138
2232
|
} catch {
|
|
2139
|
-
return response;
|
|
2233
|
+
return self._logHttp(request, response);
|
|
2140
2234
|
}
|
|
2141
2235
|
const method = request.method.toUpperCase();
|
|
2142
2236
|
if (method !== "GET" && method !== "HEAD") {
|
|
2143
|
-
return response;
|
|
2237
|
+
return self._logHttp(request, response);
|
|
2144
2238
|
}
|
|
2145
2239
|
}
|
|
2146
|
-
return self._retryRequest(request);
|
|
2240
|
+
return self._logHttp(request, await self._retryRequest(request));
|
|
2147
2241
|
}
|
|
2148
2242
|
});
|
|
2149
2243
|
}
|
|
2244
|
+
/**
|
|
2245
|
+
* Logs the final outcome of an SDK API call exactly once: successes (`2xx`) at
|
|
2246
|
+
* `debug` (method + path + status, no body), failures (`4xx`/`5xx`) at `error`
|
|
2247
|
+
* with the redacted request body and the response error body. Returns the
|
|
2248
|
+
* response so it can be chained at the middleware's return points. The error
|
|
2249
|
+
* body is read off a synchronous `clone()` so it never disturbs the body the
|
|
2250
|
+
* caller consumes.
|
|
2251
|
+
*/
|
|
2252
|
+
_logHttp(request, response) {
|
|
2253
|
+
const path = this._httpPath(request.url);
|
|
2254
|
+
const label = `[PollarClient:http] ${request.method.toUpperCase()} ${path} ${response.status}`;
|
|
2255
|
+
if (response.ok) {
|
|
2256
|
+
this._log.debug(label);
|
|
2257
|
+
} else {
|
|
2258
|
+
void this._logHttpError(label, request, response.clone());
|
|
2259
|
+
}
|
|
2260
|
+
return response;
|
|
2261
|
+
}
|
|
2262
|
+
/** Reads the redacted request body + JSON response body and logs at `error`. */
|
|
2263
|
+
async _logHttpError(label, request, response) {
|
|
2264
|
+
let requestBody;
|
|
2265
|
+
const cached = this._requestBodyCache.get(request);
|
|
2266
|
+
if (cached) {
|
|
2267
|
+
try {
|
|
2268
|
+
requestBody = redactBody(JSON.parse(new TextDecoder().decode(cached)));
|
|
2269
|
+
} catch {
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
let responseBody;
|
|
2273
|
+
if ((response.headers.get("content-type") ?? "").includes("application/json")) {
|
|
2274
|
+
try {
|
|
2275
|
+
responseBody = await response.json();
|
|
2276
|
+
} catch {
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
this._log.error(label, {
|
|
2280
|
+
...requestBody !== void 0 ? { requestBody } : {},
|
|
2281
|
+
...responseBody !== void 0 ? { responseBody } : {}
|
|
2282
|
+
});
|
|
2283
|
+
}
|
|
2284
|
+
/** Strips origin + `/v1` version prefix from a request URL for compact logs. */
|
|
2285
|
+
_httpPath(url) {
|
|
2286
|
+
try {
|
|
2287
|
+
const { pathname } = new URL(url);
|
|
2288
|
+
return pathname.startsWith("/v1/") ? pathname.slice(3) : pathname;
|
|
2289
|
+
} catch {
|
|
2290
|
+
return url;
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2150
2293
|
async _buildProofForRequest(request, accessToken) {
|
|
2151
2294
|
try {
|
|
2152
2295
|
const htu = request.url.split("?")[0].split("#")[0];
|