@pollar/core 0.9.0-rc.4 → 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/README.md +17 -3
- 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.js
CHANGED
|
@@ -1085,6 +1085,17 @@ function createLogger(level = "info", sink = console) {
|
|
|
1085
1085
|
return { error: gate("error"), warn: gate("warn"), info: gate("info"), debug: gate("debug") };
|
|
1086
1086
|
}
|
|
1087
1087
|
|
|
1088
|
+
// src/lib/logging.ts
|
|
1089
|
+
var SENSITIVE_BODY_KEYS = /* @__PURE__ */ new Set(["email", "code", "walletAddress", "dpopJwk", "response", "refreshToken"]);
|
|
1090
|
+
function redactBody(body) {
|
|
1091
|
+
if (!body || typeof body !== "object") return body;
|
|
1092
|
+
const out = {};
|
|
1093
|
+
for (const [key, value] of Object.entries(body)) {
|
|
1094
|
+
out[key] = SENSITIVE_BODY_KEYS.has(key) ? "[redacted]" : value;
|
|
1095
|
+
}
|
|
1096
|
+
return out;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1088
1099
|
// src/storage/web.ts
|
|
1089
1100
|
var LOG_PREFIX = "[PollarClient:storage]";
|
|
1090
1101
|
function createMemoryAdapter() {
|
|
@@ -1173,7 +1184,7 @@ function defaultStorage(options = {}) {
|
|
|
1173
1184
|
}
|
|
1174
1185
|
|
|
1175
1186
|
// src/version.ts
|
|
1176
|
-
var POLLAR_CORE_VERSION = "0.9.
|
|
1187
|
+
var POLLAR_CORE_VERSION = "0.9.1-rc.0" ;
|
|
1177
1188
|
|
|
1178
1189
|
// src/visibility/noop.ts
|
|
1179
1190
|
function createNoopVisibilityProvider() {
|
|
@@ -1345,10 +1356,13 @@ function openAlbedoPopup(url) {
|
|
|
1345
1356
|
}
|
|
1346
1357
|
function waitForAlbedoPopup() {
|
|
1347
1358
|
return new Promise((resolve, reject) => {
|
|
1348
|
-
const timeout = setTimeout(
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1359
|
+
const timeout = setTimeout(
|
|
1360
|
+
() => {
|
|
1361
|
+
window.removeEventListener("message", handler);
|
|
1362
|
+
reject(new Error("Albedo response timeout"));
|
|
1363
|
+
},
|
|
1364
|
+
2 * 60 * 1e3
|
|
1365
|
+
);
|
|
1352
1366
|
function handler(event) {
|
|
1353
1367
|
if (event.origin !== window.location.origin || event.data?.type !== "ALBEDO_RESULT") return;
|
|
1354
1368
|
clearTimeout(timeout);
|
|
@@ -1708,6 +1722,16 @@ function waitForSessionReady(args) {
|
|
|
1708
1722
|
return useStreaming ? streamUntilFound(api, clientSessionId, check, retryDelayMs ?? 200, signal, logger) : pollUntilFound(baseUrl, clientSessionId, check, retryDelayMs ?? 500, signal, logger);
|
|
1709
1723
|
}
|
|
1710
1724
|
|
|
1725
|
+
// src/client/auth/logging.ts
|
|
1726
|
+
function logApiError(logger, route, detail = {}, level = "error") {
|
|
1727
|
+
const { body, error, data } = detail;
|
|
1728
|
+
logger[level](`[PollarClient:auth] ${route} failed`, {
|
|
1729
|
+
route,
|
|
1730
|
+
...body !== void 0 ? { body: redactBody(body) } : {},
|
|
1731
|
+
cause: error ?? data
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1711
1735
|
// src/client/auth/authenticate.ts
|
|
1712
1736
|
async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
1713
1737
|
const { api, logger, basePath, useStreaming, signal, setAuthState, storeSession, clearSession } = deps;
|
|
@@ -1725,6 +1749,7 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
|
1725
1749
|
} catch (err) {
|
|
1726
1750
|
if (err instanceof SessionStatusError) {
|
|
1727
1751
|
const expired = err.code === "EXPIRED_CLIENT_ID";
|
|
1752
|
+
logApiError(logger, "session status", { data: err });
|
|
1728
1753
|
setAuthState({
|
|
1729
1754
|
step: "error",
|
|
1730
1755
|
previousStep: "authenticating",
|
|
@@ -1737,14 +1762,12 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
|
1737
1762
|
throw err;
|
|
1738
1763
|
}
|
|
1739
1764
|
const dpopJwk = await deps.getPublicJwk();
|
|
1740
|
-
const
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
signal
|
|
1747
|
-
});
|
|
1765
|
+
const body = {
|
|
1766
|
+
clientSessionId,
|
|
1767
|
+
dpopJwk,
|
|
1768
|
+
...deps.deviceLabel ? { deviceLabel: deps.deviceLabel } : {}
|
|
1769
|
+
};
|
|
1770
|
+
const { data, error } = await api.POST("/auth/login", { body, signal });
|
|
1748
1771
|
if (data?.code === "SDK_LOGIN_SUCCESS" && isValidSession(data?.content, logger)) {
|
|
1749
1772
|
const sessionWallet = data.content.data?.providers?.wallet?.address;
|
|
1750
1773
|
if (expectedWallet && sessionWallet !== expectedWallet) {
|
|
@@ -1759,6 +1782,7 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
|
1759
1782
|
}
|
|
1760
1783
|
await storeSession(data.content);
|
|
1761
1784
|
} else {
|
|
1785
|
+
if (!error) logApiError(logger, "POST /auth/login", { body, data });
|
|
1762
1786
|
setAuthState({
|
|
1763
1787
|
step: "error",
|
|
1764
1788
|
previousStep: "authenticating",
|
|
@@ -1771,10 +1795,11 @@ async function authenticate(clientSessionId, deps, expectedWallet) {
|
|
|
1771
1795
|
|
|
1772
1796
|
// src/client/auth/deps.ts
|
|
1773
1797
|
async function createAuthSession(deps) {
|
|
1774
|
-
const { api, signal, setAuthState } = deps;
|
|
1798
|
+
const { api, logger, signal, setAuthState } = deps;
|
|
1775
1799
|
setAuthState({ step: "creating_session" });
|
|
1776
1800
|
const { data, error } = await api.POST("/auth/session", { signal });
|
|
1777
1801
|
if (error || !data?.success) {
|
|
1802
|
+
if (!error) logApiError(logger, "POST /auth/session", { data });
|
|
1778
1803
|
setAuthState({
|
|
1779
1804
|
step: "error",
|
|
1780
1805
|
previousStep: "creating_session",
|
|
@@ -1793,13 +1818,12 @@ async function initEmailSession(deps) {
|
|
|
1793
1818
|
deps.setAuthState({ step: "entering_email", clientSessionId });
|
|
1794
1819
|
}
|
|
1795
1820
|
async function sendEmailCode(email, clientSessionId, deps) {
|
|
1796
|
-
const { api, signal, setAuthState } = deps;
|
|
1821
|
+
const { api, logger, signal, setAuthState } = deps;
|
|
1797
1822
|
setAuthState({ step: "sending_email", email });
|
|
1798
|
-
const {
|
|
1799
|
-
|
|
1800
|
-
signal
|
|
1801
|
-
});
|
|
1823
|
+
const body = { clientSessionId, email };
|
|
1824
|
+
const { data, error } = await api.POST("/auth/email", { body, signal });
|
|
1802
1825
|
if (error || !data?.success) {
|
|
1826
|
+
if (!error) logApiError(logger, "POST /auth/email", { body, data });
|
|
1803
1827
|
setAuthState({
|
|
1804
1828
|
step: "error",
|
|
1805
1829
|
previousStep: "sending_email",
|
|
@@ -1811,18 +1835,17 @@ async function sendEmailCode(email, clientSessionId, deps) {
|
|
|
1811
1835
|
setAuthState({ step: "entering_code", clientSessionId, email });
|
|
1812
1836
|
}
|
|
1813
1837
|
async function verifyAndAuthenticate(code, clientSessionId, email, deps) {
|
|
1814
|
-
const { api, signal, setAuthState } = deps;
|
|
1838
|
+
const { api, logger, signal, setAuthState } = deps;
|
|
1815
1839
|
setAuthState({ step: "verifying_email_code", clientSessionId, email });
|
|
1816
|
-
const {
|
|
1817
|
-
|
|
1818
|
-
signal
|
|
1819
|
-
});
|
|
1840
|
+
const body = { clientSessionId, code };
|
|
1841
|
+
const { data, error } = await api.POST("/auth/email/verify-code", { body, signal });
|
|
1820
1842
|
if (data?.code === "SDK_EMAIL_CODE_VERIFIED") {
|
|
1821
1843
|
await authenticate(clientSessionId, deps);
|
|
1822
1844
|
return;
|
|
1823
1845
|
}
|
|
1824
1846
|
const errCode = error?.error ?? data?.code;
|
|
1825
1847
|
if (errCode === "SDK_EMAIL_CODE_EXPIRED") {
|
|
1848
|
+
if (!error) logApiError(logger, "POST /auth/email/verify-code", { body, data });
|
|
1826
1849
|
setAuthState({
|
|
1827
1850
|
step: "error",
|
|
1828
1851
|
previousStep: "verifying_email_code",
|
|
@@ -1834,6 +1857,7 @@ async function verifyAndAuthenticate(code, clientSessionId, email, deps) {
|
|
|
1834
1857
|
return;
|
|
1835
1858
|
}
|
|
1836
1859
|
if (errCode === "INVALID_EMAIL_CODE" || errCode === "SDK_EMAIL_CODE_INVALID") {
|
|
1860
|
+
if (!error) logApiError(logger, "POST /auth/email/verify-code", { body, data });
|
|
1837
1861
|
setAuthState({
|
|
1838
1862
|
step: "error",
|
|
1839
1863
|
previousStep: "verifying_email_code",
|
|
@@ -1844,6 +1868,7 @@ async function verifyAndAuthenticate(code, clientSessionId, email, deps) {
|
|
|
1844
1868
|
});
|
|
1845
1869
|
return;
|
|
1846
1870
|
}
|
|
1871
|
+
if (!error) logApiError(logger, "POST /auth/email/verify-code", { body, data });
|
|
1847
1872
|
setAuthState({
|
|
1848
1873
|
step: "error",
|
|
1849
1874
|
previousStep: "verifying_email_code",
|
|
@@ -1893,10 +1918,73 @@ async function loginOAuth(provider, deps) {
|
|
|
1893
1918
|
await authenticate(clientSessionId, deps);
|
|
1894
1919
|
}
|
|
1895
1920
|
|
|
1921
|
+
// src/client/auth/errorMessages.ts
|
|
1922
|
+
var CATALOG = {
|
|
1923
|
+
// ── Smart-account deploy / sponsor wallet ──────────────────────────────────
|
|
1924
|
+
SPONSOR_NOT_FUNDED: {
|
|
1925
|
+
message: "This app can't create your wallet yet \u2014 its sponsor account isn't funded. Please contact the app's developer.",
|
|
1926
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1927
|
+
},
|
|
1928
|
+
APP_WALLET_NOT_FOUND: {
|
|
1929
|
+
message: "This app isn't fully set up to create wallets yet. Please contact the app's developer.",
|
|
1930
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1931
|
+
},
|
|
1932
|
+
WALLET_NOT_FOUND: {
|
|
1933
|
+
message: "This app isn't fully set up to create wallets yet. Please contact the app's developer.",
|
|
1934
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1935
|
+
},
|
|
1936
|
+
PASSKEY_DEPLOY_FAILED: {
|
|
1937
|
+
message: "We couldn't finish creating your wallet. Please try again in a moment.",
|
|
1938
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1939
|
+
},
|
|
1940
|
+
// ── Passkey ceremony ────────────────────────────────────────────────────────
|
|
1941
|
+
PASSKEY_ALREADY_REGISTERED: {
|
|
1942
|
+
message: "A passkey is already registered for this account. Try signing in instead.",
|
|
1943
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1944
|
+
},
|
|
1945
|
+
PASSKEY_UNKNOWN_CREDENTIAL: {
|
|
1946
|
+
message: "We don't recognize this passkey. Try creating a new one.",
|
|
1947
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1948
|
+
},
|
|
1949
|
+
PASSKEY_VERIFICATION_FAILED: {
|
|
1950
|
+
message: "We couldn't verify your passkey. Please try again.",
|
|
1951
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1952
|
+
},
|
|
1953
|
+
PASSKEY_CHALLENGE_MISSING: {
|
|
1954
|
+
message: "Your passkey session expired. Please start again.",
|
|
1955
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1956
|
+
},
|
|
1957
|
+
// ── On-chain transaction failures (surfaced during deploy/transfer) ─────────
|
|
1958
|
+
TX_INSUFFICIENT_BALANCE: {
|
|
1959
|
+
message: "Insufficient balance to complete this transaction.",
|
|
1960
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1961
|
+
},
|
|
1962
|
+
TX_DESTINATION_NOT_FOUND: {
|
|
1963
|
+
message: "The destination account doesn't exist on the network yet.",
|
|
1964
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1965
|
+
},
|
|
1966
|
+
TX_NO_TRUSTLINE: {
|
|
1967
|
+
message: "The destination can't receive this asset yet (no trustline).",
|
|
1968
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1969
|
+
},
|
|
1970
|
+
TX_BAD_SEQUENCE: {
|
|
1971
|
+
message: "Something went out of sync. Please try again.",
|
|
1972
|
+
errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED
|
|
1973
|
+
}
|
|
1974
|
+
};
|
|
1975
|
+
function resolveAuthError(code, fallbackMessage) {
|
|
1976
|
+
if (code && CATALOG[code]) return CATALOG[code];
|
|
1977
|
+
return { message: fallbackMessage, errorCode: AUTH_ERROR_CODES.PASSKEY_FAILED };
|
|
1978
|
+
}
|
|
1979
|
+
function extractErrorCode(error, data) {
|
|
1980
|
+
return error?.code ?? data?.code ?? void 0;
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1896
1983
|
// src/client/auth/passkeyFlow.ts
|
|
1897
1984
|
async function smartWalletFlow(deps, mode) {
|
|
1898
|
-
const { api, signal, setAuthState, passkey } = deps;
|
|
1985
|
+
const { api, logger, signal, setAuthState, passkey } = deps;
|
|
1899
1986
|
if (!passkey) {
|
|
1987
|
+
logger.error("[PollarClient:auth] passkey ceremony not configured");
|
|
1900
1988
|
setAuthState({
|
|
1901
1989
|
step: "error",
|
|
1902
1990
|
previousStep: "creating_session",
|
|
@@ -1908,38 +1996,44 @@ async function smartWalletFlow(deps, mode) {
|
|
|
1908
1996
|
const clientSessionId = await createAuthSession(deps);
|
|
1909
1997
|
if (!clientSessionId) return;
|
|
1910
1998
|
try {
|
|
1911
|
-
const
|
|
1912
|
-
|
|
1999
|
+
const challengeBody = { clientSessionId };
|
|
2000
|
+
const { data: challengeData, error: challengeError } = await api.POST("/auth/passkey/challenge", {
|
|
2001
|
+
body: challengeBody,
|
|
1913
2002
|
signal
|
|
1914
2003
|
});
|
|
1915
2004
|
const challenge = challengeData?.content?.challenge;
|
|
1916
2005
|
if (!challengeData?.success || !challenge) {
|
|
1917
|
-
|
|
2006
|
+
if (!challengeError) logApiError(logger, "POST /auth/passkey/challenge", { body: challengeBody, data: challengeData });
|
|
2007
|
+
return failPasskey(setAuthState, extractErrorCode(challengeError, challengeData), "Failed to start passkey");
|
|
1918
2008
|
}
|
|
1919
2009
|
setAuthState({ step: "creating_passkey" });
|
|
1920
2010
|
const ceremony = await passkey({ challenge, mode });
|
|
1921
2011
|
const response = ceremony.response;
|
|
1922
2012
|
if (ceremony.kind === "register") {
|
|
1923
2013
|
setAuthState({ step: "deploying_smart_account" });
|
|
1924
|
-
const
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
2014
|
+
const body = { clientSessionId, response };
|
|
2015
|
+
const { data, error } = await api.POST("/auth/passkey/register", { body, signal });
|
|
2016
|
+
if (!data?.success) {
|
|
2017
|
+
if (!error) logApiError(logger, "POST /auth/passkey/register", { body, data });
|
|
2018
|
+
return failPasskey(setAuthState, extractErrorCode(error, data), "Passkey registration failed");
|
|
2019
|
+
}
|
|
1929
2020
|
} else {
|
|
1930
|
-
const
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
2021
|
+
const body = { clientSessionId, response };
|
|
2022
|
+
const { data, error } = await api.POST("/auth/passkey/login", { body, signal });
|
|
2023
|
+
if (!data?.success) {
|
|
2024
|
+
if (!error) logApiError(logger, "POST /auth/passkey/login", { body, data });
|
|
2025
|
+
return failPasskey(setAuthState, extractErrorCode(error, data), "Passkey authentication failed");
|
|
2026
|
+
}
|
|
1935
2027
|
}
|
|
1936
|
-
} catch {
|
|
1937
|
-
|
|
2028
|
+
} catch (err) {
|
|
2029
|
+
logApiError(logger, "passkey ceremony", { error: err });
|
|
2030
|
+
return failPasskey(setAuthState, void 0, "Passkey login failed");
|
|
1938
2031
|
}
|
|
1939
2032
|
await authenticate(clientSessionId, deps);
|
|
1940
2033
|
}
|
|
1941
|
-
function failPasskey(setAuthState,
|
|
1942
|
-
|
|
2034
|
+
function failPasskey(setAuthState, code, fallbackMessage) {
|
|
2035
|
+
const { message, errorCode } = resolveAuthError(code, fallbackMessage);
|
|
2036
|
+
setAuthState({ step: "error", previousStep: "creating_passkey", message, errorCode });
|
|
1943
2037
|
}
|
|
1944
2038
|
|
|
1945
2039
|
// src/client/auth/walletFlow.ts
|
|
@@ -1956,7 +2050,7 @@ function withSignal(promise, signal) {
|
|
|
1956
2050
|
]);
|
|
1957
2051
|
}
|
|
1958
2052
|
async function loginWallet(type, deps) {
|
|
1959
|
-
const { api, signal, setAuthState } = deps;
|
|
2053
|
+
const { api, logger, signal, setAuthState } = deps;
|
|
1960
2054
|
const clientSessionId = await createAuthSession(deps);
|
|
1961
2055
|
if (!clientSessionId) return;
|
|
1962
2056
|
let connectedWallet;
|
|
@@ -1972,11 +2066,10 @@ async function loginWallet(type, deps) {
|
|
|
1972
2066
|
connectedWallet = address;
|
|
1973
2067
|
deps.storeWalletAdapter(adapter, type);
|
|
1974
2068
|
setAuthState({ step: "authenticating_wallet" });
|
|
1975
|
-
const {
|
|
1976
|
-
|
|
1977
|
-
signal
|
|
1978
|
-
});
|
|
2069
|
+
const body = { clientSessionId, walletAddress: address };
|
|
2070
|
+
const { data: walletData, error: walletError } = await api.POST("/auth/wallet", { body, signal });
|
|
1979
2071
|
if (walletError || !walletData?.success) {
|
|
2072
|
+
if (!walletError) logApiError(logger, "POST /auth/wallet", { body, data: walletData });
|
|
1980
2073
|
setAuthState({
|
|
1981
2074
|
step: "error",
|
|
1982
2075
|
previousStep: "authenticating_wallet",
|
|
@@ -1985,7 +2078,8 @@ async function loginWallet(type, deps) {
|
|
|
1985
2078
|
});
|
|
1986
2079
|
return;
|
|
1987
2080
|
}
|
|
1988
|
-
} catch {
|
|
2081
|
+
} catch (err) {
|
|
2082
|
+
logApiError(logger, "wallet connect", { error: err });
|
|
1989
2083
|
setAuthState({
|
|
1990
2084
|
step: "error",
|
|
1991
2085
|
previousStep: "connecting_wallet",
|
|
@@ -2191,28 +2285,77 @@ var PollarClient = class {
|
|
|
2191
2285
|
onResponse: async ({ request, response }) => {
|
|
2192
2286
|
const newNonce = response.headers.get("DPoP-Nonce");
|
|
2193
2287
|
if (newNonce) self._dpopNonce = newNonce;
|
|
2194
|
-
if (response.status !== 401) return response;
|
|
2288
|
+
if (response.status !== 401) return self._logHttp(request, response);
|
|
2195
2289
|
const wwwAuth = response.headers.get("WWW-Authenticate") ?? "";
|
|
2196
2290
|
const isNonceChallenge = wwwAuth.includes("use_dpop_nonce");
|
|
2197
2291
|
if (request.url.includes("/auth/refresh")) {
|
|
2198
|
-
if (isNonceChallenge) return self._retryRequest(request);
|
|
2199
|
-
return response;
|
|
2292
|
+
if (isNonceChallenge) return self._logHttp(request, await self._retryRequest(request));
|
|
2293
|
+
return self._logHttp(request, response);
|
|
2200
2294
|
}
|
|
2201
2295
|
if (!isNonceChallenge) {
|
|
2202
2296
|
try {
|
|
2203
2297
|
await self.refresh();
|
|
2204
2298
|
} catch {
|
|
2205
|
-
return response;
|
|
2299
|
+
return self._logHttp(request, response);
|
|
2206
2300
|
}
|
|
2207
2301
|
const method = request.method.toUpperCase();
|
|
2208
2302
|
if (method !== "GET" && method !== "HEAD") {
|
|
2209
|
-
return response;
|
|
2303
|
+
return self._logHttp(request, response);
|
|
2210
2304
|
}
|
|
2211
2305
|
}
|
|
2212
|
-
return self._retryRequest(request);
|
|
2306
|
+
return self._logHttp(request, await self._retryRequest(request));
|
|
2213
2307
|
}
|
|
2214
2308
|
});
|
|
2215
2309
|
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Logs the final outcome of an SDK API call exactly once: successes (`2xx`) at
|
|
2312
|
+
* `debug` (method + path + status, no body), failures (`4xx`/`5xx`) at `error`
|
|
2313
|
+
* with the redacted request body and the response error body. Returns the
|
|
2314
|
+
* response so it can be chained at the middleware's return points. The error
|
|
2315
|
+
* body is read off a synchronous `clone()` so it never disturbs the body the
|
|
2316
|
+
* caller consumes.
|
|
2317
|
+
*/
|
|
2318
|
+
_logHttp(request, response) {
|
|
2319
|
+
const path = this._httpPath(request.url);
|
|
2320
|
+
const label = `[PollarClient:http] ${request.method.toUpperCase()} ${path} ${response.status}`;
|
|
2321
|
+
if (response.ok) {
|
|
2322
|
+
this._log.debug(label);
|
|
2323
|
+
} else {
|
|
2324
|
+
void this._logHttpError(label, request, response.clone());
|
|
2325
|
+
}
|
|
2326
|
+
return response;
|
|
2327
|
+
}
|
|
2328
|
+
/** Reads the redacted request body + JSON response body and logs at `error`. */
|
|
2329
|
+
async _logHttpError(label, request, response) {
|
|
2330
|
+
let requestBody;
|
|
2331
|
+
const cached = this._requestBodyCache.get(request);
|
|
2332
|
+
if (cached) {
|
|
2333
|
+
try {
|
|
2334
|
+
requestBody = redactBody(JSON.parse(new TextDecoder().decode(cached)));
|
|
2335
|
+
} catch {
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
let responseBody;
|
|
2339
|
+
if ((response.headers.get("content-type") ?? "").includes("application/json")) {
|
|
2340
|
+
try {
|
|
2341
|
+
responseBody = await response.json();
|
|
2342
|
+
} catch {
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
this._log.error(label, {
|
|
2346
|
+
...requestBody !== void 0 ? { requestBody } : {},
|
|
2347
|
+
...responseBody !== void 0 ? { responseBody } : {}
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
/** Strips origin + `/v1` version prefix from a request URL for compact logs. */
|
|
2351
|
+
_httpPath(url) {
|
|
2352
|
+
try {
|
|
2353
|
+
const { pathname } = new URL(url);
|
|
2354
|
+
return pathname.startsWith("/v1/") ? pathname.slice(3) : pathname;
|
|
2355
|
+
} catch {
|
|
2356
|
+
return url;
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2216
2359
|
async _buildProofForRequest(request, accessToken) {
|
|
2217
2360
|
try {
|
|
2218
2361
|
const htu = request.url.split("?")[0].split("#")[0];
|