@tern-secure/auth 1.1.0-canary.v20251108045933 → 1.1.0-canary.v20251127221555
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/492_ternsecure_b693ad_1.1.0-canary.v20251127221555.js +1 -0
- package/dist/687_ternsecure_b693ad_1.1.0-canary.v20251127221555.js +1 -0
- package/dist/68_ternsecure_b693ad_1.1.0-canary.v20251127221555.js +1 -0
- package/dist/framework_ternsecure_b693ad_1.1.0-canary.v20251127221555.js +1 -0
- package/dist/signin_ternsecure_b693ad_1.1.0-canary.v20251127221555.css +2 -0
- package/dist/signin_ternsecure_b693ad_1.1.0-canary.v20251127221555.js +1 -0
- package/dist/signup_ternsecure_b693ad_1.1.0-canary.v20251127221555.css +2 -0
- package/dist/signup_ternsecure_b693ad_1.1.0-canary.v20251127221555.js +1 -0
- package/dist/ternsecure.browser.js +30 -0
- package/dist/ternsecure.css +2 -0
- package/dist/ternsecure.js +17 -0
- package/dist/ternsecure.mjs +17 -0
- package/dist/types/auth/AuthCookieManager.d.ts +0 -1
- package/dist/types/auth/cookies/authTime_cookie.d.ts +0 -1
- package/dist/types/index.browser.d.ts +1 -0
- package/dist/types/index.d.ts +3 -10
- package/dist/types/instance/c_coreApiClient.d.ts +2 -2
- package/dist/types/instance/constants.d.ts +3 -0
- package/dist/types/instance/coreApiClient.d.ts +0 -1
- package/dist/types/instance/events.d.ts +0 -1
- package/dist/types/instance/fraudProtection.d.ts +7 -0
- package/dist/types/instance/{TernAuth.d.ts → ternsecure.d.ts} +37 -7
- package/dist/types/lib/utils.d.ts +2 -0
- package/dist/types/resources/Base.d.ts +17 -3
- package/dist/types/resources/Error.d.ts +0 -1
- package/dist/types/resources/Session.d.ts +2 -2
- package/dist/types/resources/SignIn.d.ts +27 -10
- package/dist/types/resources/SignUp.d.ts +12 -10
- package/dist/types/resources/internal.d.ts +1 -2
- package/dist/types/ui/Renderer.d.ts +26 -0
- package/dist/types/ui/common/ProviderInitialIcon.d.ts +6 -0
- package/dist/types/ui/common/VerificationCodeCard.d.ts +12 -0
- package/dist/types/ui/common/VerificationLinkCard.d.ts +7 -0
- package/dist/types/ui/common/constants.d.ts +8 -0
- package/dist/types/ui/common/index.d.ts +3 -0
- package/dist/types/ui/components/sign-in/ResetPassword.d.ts +1 -0
- package/dist/types/ui/components/sign-in/ResetPasswordSuccess.d.ts +1 -0
- package/dist/types/ui/components/sign-in/SignIn.d.ts +4 -0
- package/dist/types/ui/components/sign-in/SignInEmailLinkCard.d.ts +2 -0
- package/dist/types/ui/components/sign-in/SignInFactorOne.d.ts +1 -0
- package/dist/types/ui/components/sign-in/SignInFactorOneCodeForm.d.ts +7 -0
- package/dist/types/ui/components/sign-in/SignInFactorOnePasswordCard.d.ts +6 -0
- package/dist/types/ui/components/sign-in/SignInFactorOnePhoneCodeCard.d.ts +7 -0
- package/dist/types/ui/components/sign-in/SignInPassword.d.ts +8 -0
- package/dist/types/ui/components/sign-in/SignInSocialButtons.d.ts +1 -0
- package/dist/types/ui/components/sign-in/SignInStart.d.ts +7 -0
- package/dist/types/ui/components/sign-in/SignInVerifyEmail.d.ts +1 -0
- package/dist/types/ui/components/sign-in/index.d.ts +4 -0
- package/dist/types/ui/components/sign-up/SignUp.d.ts +8 -0
- package/dist/types/ui/components/sign-up/SignUpEmailLinkCard.d.ts +2 -0
- package/dist/types/ui/components/sign-up/SignUpSocialButtons.d.ts +1 -0
- package/dist/types/ui/components/sign-up/SignUpStart.d.ts +1 -0
- package/dist/types/ui/components/sign-up/SignUpVerifyEmail.d.ts +1 -0
- package/dist/types/ui/components/sign-up/index.d.ts +2 -0
- package/dist/types/ui/components/sign-up/util.d.ts +14 -0
- package/dist/types/ui/components/user-button/index.d.ts +1 -0
- package/dist/types/ui/components/user-button/userButton.d.ts +1 -0
- package/dist/types/ui/ctx/TernAuthContext.d.ts +4 -0
- package/dist/types/ui/ctx/TernAuthUIComponentCtx.d.ts +8 -0
- package/dist/types/ui/ctx/TernSecureContextWrapper.d.ts +8 -0
- package/dist/types/ui/ctx/TernSecureOptions.d.ts +10 -0
- package/dist/types/ui/ctx/components/SignIn.d.ts +21 -0
- package/dist/types/ui/ctx/components/SignUp.d.ts +14 -0
- package/dist/types/ui/ctx/components/UserButton.d.ts +10 -0
- package/dist/types/ui/ctx/components/index.d.ts +3 -0
- package/dist/types/ui/ctx/index.d.ts +4 -0
- package/dist/types/ui/ctx/utils.d.ts +3 -0
- package/dist/types/ui/customize/FieldControl.d.ts +21 -0
- package/dist/types/ui/customize/FieldLabelControl.d.ts +11 -0
- package/dist/types/ui/customize/Form.d.ts +36 -0
- package/dist/types/ui/elements/CodeControl.d.ts +47 -0
- package/dist/types/ui/elements/ErrorCard.d.ts +10 -0
- package/dist/types/ui/elements/LoadingCard.d.ts +1 -0
- package/dist/types/ui/elements/RouterLink.d.ts +42 -0
- package/dist/types/ui/elements/SocialButtons.d.ts +6 -0
- package/dist/types/ui/elements/TimerButton.d.ts +9 -0
- package/dist/types/ui/elements/alert.d.ts +8 -0
- package/dist/types/ui/elements/avatar.d.ts +6 -0
- package/dist/types/ui/elements/button.d.ts +10 -0
- package/dist/types/ui/elements/card.d.ts +9 -0
- package/dist/types/ui/elements/ctx/CardStateCtx.d.ts +26 -0
- package/dist/types/ui/elements/ctx/index.d.ts +1 -0
- package/dist/types/ui/elements/field.d.ts +24 -0
- package/dist/types/ui/elements/index.d.ts +16 -0
- package/dist/types/ui/elements/input.d.ts +3 -0
- package/dist/types/ui/elements/label.d.ts +4 -0
- package/dist/types/ui/elements/separator.d.ts +4 -0
- package/dist/types/ui/hooks/index.d.ts +6 -0
- package/dist/types/ui/hooks/useEmailLink.d.ts +11 -0
- package/dist/types/ui/hooks/useFetch.d.ts +44 -0
- package/dist/types/ui/hooks/useLoadingStatus.d.ts +14 -0
- package/dist/types/ui/hooks/useNavigateToFlowStart.d.ts +3 -0
- package/dist/types/ui/hooks/useSafeState.d.ts +9 -0
- package/dist/types/ui/hooks/useWindowEventListener.d.ts +3 -0
- package/dist/types/ui/icons/index.d.ts +13 -0
- package/dist/types/ui/lazyLoading/common.d.ts +2 -0
- package/dist/types/ui/lazyLoading/components.d.ts +11 -0
- package/dist/types/ui/lazyLoading/providersCtx.d.ts +26 -0
- package/dist/types/ui/portal/index.d.ts +12 -0
- package/dist/types/ui/router/BaseRouter.d.ts +21 -0
- package/dist/types/ui/router/HashRouter.d.ts +8 -0
- package/dist/types/ui/router/PathRouter.d.ts +8 -0
- package/dist/types/ui/router/Route.d.ts +19 -0
- package/dist/types/ui/router/RouterCtx.d.ts +32 -0
- package/dist/types/ui/router/Switch.d.ts +4 -0
- package/dist/types/ui/router/index.d.ts +7 -0
- package/dist/types/ui/router/newPaths.d.ts +1 -0
- package/dist/types/ui/router/pathToRegexp.d.ts +127 -0
- package/dist/types/ui/types.d.ts +18 -0
- package/dist/types/ui/utils/form.d.ts +19 -0
- package/dist/types/ui/utils/index.d.ts +1 -0
- package/dist/types/ui/utils/sleep.d.ts +1 -0
- package/dist/types/utils/construct.d.ts +3 -1
- package/dist/types/utils/index.d.ts +1 -1
- package/dist/types/utils/normalizeRoutingOptions.d.ts +6 -0
- package/dist/types/utils/path.d.ts +0 -1
- package/dist/types/utils/querystring.d.ts +0 -1
- package/dist/types/utils/redirectUrls.d.ts +4 -9
- package/dist/types/utils/windowNavigate.d.ts +0 -1
- package/dist/ui-common_ternsecure_b693ad_1.1.0-canary.v20251127221555.js +1 -0
- package/dist/userbutton_ternsecure_b693ad_1.1.0-canary.v20251127221555.js +1 -0
- package/dist/vendors_ternsecure_b693ad_1.1.0-canary.v20251127221555.js +1 -0
- package/package.json +63 -24
- package/dist/cjs/auth/AuthCookieManager.js +0 -113
- package/dist/cjs/auth/AuthCookieManager.js.map +0 -1
- package/dist/cjs/auth/cookies/authTime_cookie.js +0 -62
- package/dist/cjs/auth/cookies/authTime_cookie.js.map +0 -1
- package/dist/cjs/auth/request.js +0 -159
- package/dist/cjs/auth/request.js.map +0 -1
- package/dist/cjs/global.d.js +0 -2
- package/dist/cjs/global.d.js.map +0 -1
- package/dist/cjs/index.js +0 -47
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/instance/TernAuth.js +0 -524
- package/dist/cjs/instance/TernAuth.js.map +0 -1
- package/dist/cjs/instance/TernAuthServer.js +0 -95
- package/dist/cjs/instance/TernAuthServer.js.map +0 -1
- package/dist/cjs/instance/c_coreApiClient.js +0 -264
- package/dist/cjs/instance/c_coreApiClient.js.map +0 -1
- package/dist/cjs/instance/coreApiClient.js +0 -255
- package/dist/cjs/instance/coreApiClient.js.map +0 -1
- package/dist/cjs/instance/events.js +0 -39
- package/dist/cjs/instance/events.js.map +0 -1
- package/dist/cjs/instance/jwtClient.js +0 -72
- package/dist/cjs/instance/jwtClient.js.map +0 -1
- package/dist/cjs/resources/Base.js +0 -137
- package/dist/cjs/resources/Base.js.map +0 -1
- package/dist/cjs/resources/Error.js +0 -31
- package/dist/cjs/resources/Error.js.map +0 -1
- package/dist/cjs/resources/Session.js +0 -105
- package/dist/cjs/resources/Session.js.map +0 -1
- package/dist/cjs/resources/SignIn.js +0 -256
- package/dist/cjs/resources/SignIn.js.map +0 -1
- package/dist/cjs/resources/SignUp.js +0 -72
- package/dist/cjs/resources/SignUp.js.map +0 -1
- package/dist/cjs/resources/Token.js +0 -32
- package/dist/cjs/resources/Token.js.map +0 -1
- package/dist/cjs/resources/UserData.js +0 -43
- package/dist/cjs/resources/UserData.js.map +0 -1
- package/dist/cjs/resources/cookie.js +0 -154
- package/dist/cjs/resources/cookie.js.map +0 -1
- package/dist/cjs/resources/index.js +0 -23
- package/dist/cjs/resources/index.js.map +0 -1
- package/dist/cjs/resources/internal.js +0 -35
- package/dist/cjs/resources/internal.js.map +0 -1
- package/dist/cjs/utils/construct.js +0 -253
- package/dist/cjs/utils/construct.js.map +0 -1
- package/dist/cjs/utils/index.js +0 -29
- package/dist/cjs/utils/index.js.map +0 -1
- package/dist/cjs/utils/jwt.js +0 -46
- package/dist/cjs/utils/jwt.js.map +0 -1
- package/dist/cjs/utils/mapDecode.js +0 -33
- package/dist/cjs/utils/mapDecode.js.map +0 -1
- package/dist/cjs/utils/path.js +0 -33
- package/dist/cjs/utils/path.js.map +0 -1
- package/dist/cjs/utils/querystring.js +0 -70
- package/dist/cjs/utils/querystring.js.map +0 -1
- package/dist/cjs/utils/redirectUrls.js +0 -156
- package/dist/cjs/utils/redirectUrls.js.map +0 -1
- package/dist/cjs/utils/windowNavigate.js +0 -45
- package/dist/cjs/utils/windowNavigate.js.map +0 -1
- package/dist/esm/auth/AuthCookieManager.js +0 -89
- package/dist/esm/auth/AuthCookieManager.js.map +0 -1
- package/dist/esm/auth/cookies/authTime_cookie.js +0 -38
- package/dist/esm/auth/cookies/authTime_cookie.js.map +0 -1
- package/dist/esm/auth/request.js +0 -134
- package/dist/esm/auth/request.js.map +0 -1
- package/dist/esm/global.d.js +0 -1
- package/dist/esm/global.d.js.map +0 -1
- package/dist/esm/index.js +0 -16
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/instance/TernAuth.js +0 -510
- package/dist/esm/instance/TernAuth.js.map +0 -1
- package/dist/esm/instance/TernAuthServer.js +0 -73
- package/dist/esm/instance/TernAuthServer.js.map +0 -1
- package/dist/esm/instance/c_coreApiClient.js +0 -236
- package/dist/esm/instance/c_coreApiClient.js.map +0 -1
- package/dist/esm/instance/coreApiClient.js +0 -226
- package/dist/esm/instance/coreApiClient.js.map +0 -1
- package/dist/esm/instance/events.js +0 -14
- package/dist/esm/instance/events.js.map +0 -1
- package/dist/esm/instance/jwtClient.js +0 -47
- package/dist/esm/instance/jwtClient.js.map +0 -1
- package/dist/esm/resources/Base.js +0 -113
- package/dist/esm/resources/Base.js.map +0 -1
- package/dist/esm/resources/Error.js +0 -9
- package/dist/esm/resources/Error.js.map +0 -1
- package/dist/esm/resources/Session.js +0 -81
- package/dist/esm/resources/Session.js.map +0 -1
- package/dist/esm/resources/SignIn.js +0 -240
- package/dist/esm/resources/SignIn.js.map +0 -1
- package/dist/esm/resources/SignUp.js +0 -48
- package/dist/esm/resources/SignUp.js.map +0 -1
- package/dist/esm/resources/Token.js +0 -8
- package/dist/esm/resources/Token.js.map +0 -1
- package/dist/esm/resources/UserData.js +0 -19
- package/dist/esm/resources/UserData.js.map +0 -1
- package/dist/esm/resources/cookie.js +0 -130
- package/dist/esm/resources/cookie.js.map +0 -1
- package/dist/esm/resources/index.js +0 -2
- package/dist/esm/resources/index.js.map +0 -1
- package/dist/esm/resources/internal.js +0 -8
- package/dist/esm/resources/internal.js.map +0 -1
- package/dist/esm/utils/construct.js +0 -215
- package/dist/esm/utils/construct.js.map +0 -1
- package/dist/esm/utils/index.js +0 -5
- package/dist/esm/utils/index.js.map +0 -1
- package/dist/esm/utils/jwt.js +0 -22
- package/dist/esm/utils/jwt.js.map +0 -1
- package/dist/esm/utils/mapDecode.js +0 -9
- package/dist/esm/utils/mapDecode.js.map +0 -1
- package/dist/esm/utils/path.js +0 -9
- package/dist/esm/utils/path.js.map +0 -1
- package/dist/esm/utils/querystring.js +0 -45
- package/dist/esm/utils/querystring.js.map +0 -1
- package/dist/esm/utils/redirectUrls.js +0 -132
- package/dist/esm/utils/redirectUrls.js.map +0 -1
- package/dist/esm/utils/windowNavigate.js +0 -19
- package/dist/esm/utils/windowNavigate.js.map +0 -1
- package/dist/types/auth/AuthCookieManager.d.ts.map +0 -1
- package/dist/types/auth/cookies/authTime_cookie.d.ts.map +0 -1
- package/dist/types/auth/request.d.ts +0 -49
- package/dist/types/auth/request.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/instance/TernAuth.d.ts.map +0 -1
- package/dist/types/instance/TernAuthServer.d.ts +0 -32
- package/dist/types/instance/TernAuthServer.d.ts.map +0 -1
- package/dist/types/instance/c_coreApiClient.d.ts.map +0 -1
- package/dist/types/instance/coreApiClient.d.ts.map +0 -1
- package/dist/types/instance/events.d.ts.map +0 -1
- package/dist/types/instance/jwtClient.d.ts +0 -22
- package/dist/types/instance/jwtClient.d.ts.map +0 -1
- package/dist/types/resources/Base.d.ts.map +0 -1
- package/dist/types/resources/Error.d.ts.map +0 -1
- package/dist/types/resources/Session.d.ts.map +0 -1
- package/dist/types/resources/SignIn.d.ts.map +0 -1
- package/dist/types/resources/SignUp.d.ts.map +0 -1
- package/dist/types/resources/Token.d.ts +0 -5
- package/dist/types/resources/Token.d.ts.map +0 -1
- package/dist/types/resources/UserData.d.ts +0 -8
- package/dist/types/resources/UserData.d.ts.map +0 -1
- package/dist/types/resources/cookie.d.ts +0 -24
- package/dist/types/resources/cookie.d.ts.map +0 -1
- package/dist/types/resources/index.d.ts +0 -2
- package/dist/types/resources/index.d.ts.map +0 -1
- package/dist/types/resources/internal.d.ts.map +0 -1
- package/dist/types/utils/construct.d.ts.map +0 -1
- package/dist/types/utils/index.d.ts.map +0 -1
- package/dist/types/utils/jwt.d.ts +0 -12
- package/dist/types/utils/jwt.d.ts.map +0 -1
- package/dist/types/utils/mapDecode.d.ts +0 -4
- package/dist/types/utils/mapDecode.d.ts.map +0 -1
- package/dist/types/utils/path.d.ts.map +0 -1
- package/dist/types/utils/querystring.d.ts.map +0 -1
- package/dist/types/utils/redirectUrls.d.ts.map +0 -1
- package/dist/types/utils/windowNavigate.d.ts.map +0 -1
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import { camelToSnake, jitteredDelay } from "@tern-secure/shared/caseUtils";
|
|
2
|
-
import { buildURL as buildUrlUtil, stringifyQueryParams } from "../utils";
|
|
3
|
-
class NetworkError extends Error {
|
|
4
|
-
constructor(url, original) {
|
|
5
|
-
super(`Network error for ${url}: ${original.message}`);
|
|
6
|
-
this.url = url;
|
|
7
|
-
this.original = original;
|
|
8
|
-
this.name = "NetworkError";
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
class TimeoutError extends Error {
|
|
12
|
-
constructor() {
|
|
13
|
-
super("Request timed out");
|
|
14
|
-
this.name = "TimeoutError";
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
class CircuitOpenError extends Error {
|
|
18
|
-
constructor() {
|
|
19
|
-
super("Circuit breaker is open");
|
|
20
|
-
this.name = "CircuitOpenError";
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
class HTTPError extends Error {
|
|
24
|
-
constructor(status, url, body) {
|
|
25
|
-
super(`HTTP ${status} error for ${url}`);
|
|
26
|
-
this.status = status;
|
|
27
|
-
this.url = url;
|
|
28
|
-
this.body = body;
|
|
29
|
-
this.name = "HTTPError";
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function createInitialState(clientOptions = {}) {
|
|
33
|
-
return {
|
|
34
|
-
circuitBreaker: {
|
|
35
|
-
failures: 0,
|
|
36
|
-
lastFailureTime: 0,
|
|
37
|
-
state: "closed"
|
|
38
|
-
},
|
|
39
|
-
beforeRequestHooks: [],
|
|
40
|
-
afterResponseHooks: [],
|
|
41
|
-
clientOptions
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
function addBeforeRequestHook(state, hook) {
|
|
45
|
-
state.beforeRequestHooks.push(hook);
|
|
46
|
-
}
|
|
47
|
-
function addAfterResponseHook(state, hook) {
|
|
48
|
-
state.afterResponseHooks.push(hook);
|
|
49
|
-
}
|
|
50
|
-
async function runBeforeRequestHooks(state) {
|
|
51
|
-
for (const hook of state.beforeRequestHooks) {
|
|
52
|
-
const result = await hook();
|
|
53
|
-
if (result === false) return false;
|
|
54
|
-
}
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
async function runAfterResponseHooks(state, response) {
|
|
58
|
-
for (const hook of state.afterResponseHooks) {
|
|
59
|
-
await hook(response);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function checkCircuitBreaker(state, requestOptions) {
|
|
63
|
-
const { recoveryTimeoutMs = 6e4 } = requestOptions;
|
|
64
|
-
const now = Date.now();
|
|
65
|
-
if (state.circuitBreaker.state === "open") {
|
|
66
|
-
if (now - state.circuitBreaker.lastFailureTime >= recoveryTimeoutMs) {
|
|
67
|
-
state.circuitBreaker.state = "half-open";
|
|
68
|
-
} else {
|
|
69
|
-
throw new CircuitOpenError();
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
function recordSuccess(state) {
|
|
74
|
-
state.circuitBreaker.failures = 0;
|
|
75
|
-
state.circuitBreaker.state = "closed";
|
|
76
|
-
}
|
|
77
|
-
function recordFailure(state, requestOptions) {
|
|
78
|
-
const { failureThreshold = 5 } = requestOptions;
|
|
79
|
-
state.circuitBreaker.failures++;
|
|
80
|
-
state.circuitBreaker.lastFailureTime = Date.now();
|
|
81
|
-
if (state.circuitBreaker.failures >= failureThreshold) {
|
|
82
|
-
state.circuitBreaker.state = "open";
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
function shouldRetry(state, requestOptions, error, method, attempt, maxTries) {
|
|
86
|
-
const isRetryable = error instanceof NetworkError && method.toUpperCase() === "GET" && attempt < maxTries;
|
|
87
|
-
if (!isRetryable) {
|
|
88
|
-
recordFailure(state, requestOptions);
|
|
89
|
-
}
|
|
90
|
-
return isRetryable;
|
|
91
|
-
}
|
|
92
|
-
async function retryWithBackoff(state, requestOptions, attemptFn, shouldRetryFn) {
|
|
93
|
-
const {
|
|
94
|
-
initialDelay = 700,
|
|
95
|
-
factor = 2,
|
|
96
|
-
maxDelay = 5e3,
|
|
97
|
-
maxTries = typeof navigator !== "undefined" && navigator.onLine ? 4 : 11
|
|
98
|
-
} = requestOptions;
|
|
99
|
-
let lastError;
|
|
100
|
-
for (let attempt = 1; attempt <= maxTries; attempt++) {
|
|
101
|
-
try {
|
|
102
|
-
const result = await attemptFn();
|
|
103
|
-
recordSuccess(state);
|
|
104
|
-
return result;
|
|
105
|
-
} catch (error) {
|
|
106
|
-
lastError = error;
|
|
107
|
-
if (!shouldRetryFn(error, attempt)) {
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
110
|
-
recordFailure(state, requestOptions);
|
|
111
|
-
if (attempt < maxTries) {
|
|
112
|
-
const delay = Math.min(initialDelay * Math.pow(factor, attempt - 1), maxDelay);
|
|
113
|
-
await new Promise((resolve) => setTimeout(resolve, jitteredDelay(delay)));
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
throw lastError;
|
|
118
|
-
}
|
|
119
|
-
function createCoreApiClient(clientOptions) {
|
|
120
|
-
function buildUrl(requestInit) {
|
|
121
|
-
var _a, _b;
|
|
122
|
-
const isLocalhost = ((_a = clientOptions.apiUrl) == null ? void 0 : _a.includes("localhost")) || ((_b = clientOptions.apiUrl) == null ? void 0 : _b.includes("127.0.0.1"));
|
|
123
|
-
const { path } = requestInit;
|
|
124
|
-
const { instanceType, domain, apiUrl, apiBasePath = "/api/auth" } = clientOptions;
|
|
125
|
-
const domainInProd = instanceType === "production" ? domain : "";
|
|
126
|
-
let baseUrl;
|
|
127
|
-
if (isLocalhost) {
|
|
128
|
-
baseUrl = (apiUrl == null ? void 0 : apiUrl.startsWith("http")) ? apiUrl : `http://${apiUrl}`;
|
|
129
|
-
} else {
|
|
130
|
-
baseUrl = `https://${domainInProd || apiUrl}`;
|
|
131
|
-
}
|
|
132
|
-
const fullPath = `${apiBasePath}/${path}`.replace(/\/+/g, "/");
|
|
133
|
-
return buildUrlUtil(
|
|
134
|
-
{
|
|
135
|
-
base: baseUrl,
|
|
136
|
-
pathname: fullPath,
|
|
137
|
-
searchParams: requestInit.search ? new URLSearchParams(requestInit.search) : void 0
|
|
138
|
-
},
|
|
139
|
-
{ stringify: false }
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
async function makeRequest(state2, init, opts = {}) {
|
|
143
|
-
const requestInit = { ...init };
|
|
144
|
-
const { method = "GET", body } = requestInit;
|
|
145
|
-
const requestOptions = { ...opts };
|
|
146
|
-
requestInit.url = buildUrl({ ...requestInit });
|
|
147
|
-
checkCircuitBreaker(state2, requestOptions);
|
|
148
|
-
const shouldContinue = await runBeforeRequestHooks(state2);
|
|
149
|
-
if (!shouldContinue) {
|
|
150
|
-
const mockResponse = new Response("{}", {
|
|
151
|
-
status: 200
|
|
152
|
-
});
|
|
153
|
-
mockResponse.payload = { response: {} };
|
|
154
|
-
await runAfterResponseHooks(state2, mockResponse);
|
|
155
|
-
return mockResponse;
|
|
156
|
-
}
|
|
157
|
-
const { timeoutMs } = requestOptions;
|
|
158
|
-
const overwrittenRequestMethod = method === "GET" ? "GET" : "POST";
|
|
159
|
-
const url = requestInit.url.toString();
|
|
160
|
-
requestInit.headers = new Headers(requestInit.headers);
|
|
161
|
-
if (method !== "GET" && !(body instanceof FormData) && !requestInit.headers.has("content-type")) {
|
|
162
|
-
requestInit.headers.set("content-type", "application/json");
|
|
163
|
-
}
|
|
164
|
-
if (requestInit.headers.get("content-type") === "application/x-www-form-urlencoded") {
|
|
165
|
-
requestInit.body = body ? stringifyQueryParams(body, {
|
|
166
|
-
keyEncoder: camelToSnake
|
|
167
|
-
}) : body;
|
|
168
|
-
} else if (requestInit.headers.get("content-type") === "application/json" && body) {
|
|
169
|
-
requestInit.body = typeof body === "string" ? body : JSON.stringify(body);
|
|
170
|
-
}
|
|
171
|
-
const attemptRequest = async () => {
|
|
172
|
-
const controller = new AbortController();
|
|
173
|
-
const timeoutId = timeoutMs ? setTimeout(() => {
|
|
174
|
-
controller.abort();
|
|
175
|
-
}, timeoutMs) : null;
|
|
176
|
-
let response;
|
|
177
|
-
const fetchOpts = {
|
|
178
|
-
...requestInit,
|
|
179
|
-
credentials: "include",
|
|
180
|
-
method: overwrittenRequestMethod
|
|
181
|
-
};
|
|
182
|
-
try {
|
|
183
|
-
response = await fetch(url, fetchOpts);
|
|
184
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
185
|
-
let payload = null;
|
|
186
|
-
if (response.status === 204) {
|
|
187
|
-
payload = null;
|
|
188
|
-
} else {
|
|
189
|
-
try {
|
|
190
|
-
const json = await response.json();
|
|
191
|
-
payload = json;
|
|
192
|
-
} catch {
|
|
193
|
-
payload = { response: {} };
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
const apiResponse = response;
|
|
197
|
-
apiResponse.payload = payload;
|
|
198
|
-
await runAfterResponseHooks(state2, apiResponse);
|
|
199
|
-
return apiResponse;
|
|
200
|
-
} catch (error) {
|
|
201
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
202
|
-
if (error.name === "AbortError") {
|
|
203
|
-
throw new TimeoutError();
|
|
204
|
-
}
|
|
205
|
-
throw new NetworkError(url, error);
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
return retryWithBackoff(
|
|
209
|
-
state2,
|
|
210
|
-
requestOptions,
|
|
211
|
-
attemptRequest,
|
|
212
|
-
(error, attempt) => shouldRetry(
|
|
213
|
-
state2,
|
|
214
|
-
requestOptions,
|
|
215
|
-
error,
|
|
216
|
-
overwrittenRequestMethod,
|
|
217
|
-
attempt,
|
|
218
|
-
requestOptions.maxTries || (typeof navigator !== "undefined" && navigator.onLine ? 4 : 11)
|
|
219
|
-
)
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
const state = createInitialState(clientOptions);
|
|
223
|
-
return {
|
|
224
|
-
onBeforeRequest: (hook) => addBeforeRequestHook(state, hook),
|
|
225
|
-
onAfterResponse: (hook) => addAfterResponseHook(state, hook),
|
|
226
|
-
request: (init, opts = {}) => makeRequest(state, init, opts)
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
export {
|
|
230
|
-
CircuitOpenError,
|
|
231
|
-
HTTPError,
|
|
232
|
-
NetworkError,
|
|
233
|
-
TimeoutError,
|
|
234
|
-
createCoreApiClient
|
|
235
|
-
};
|
|
236
|
-
//# sourceMappingURL=c_coreApiClient.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/instance/c_coreApiClient.ts"],"sourcesContent":["import { camelToSnake, jitteredDelay } from '@tern-secure/shared/caseUtils'\nimport type { InstanceType, TernSecureApiErrorJSON } from '@tern-secure/types';\n\nimport { buildURL as buildUrlUtil, stringifyQueryParams } from '../utils';\n\nexport type HTTPMethod =\n | 'CONNECT'\n | 'DELETE'\n | 'GET'\n | 'HEAD'\n | 'OPTIONS'\n | 'PATCH'\n | 'POST'\n | 'PUT'\n | 'TRACE';\n\nexport type ApiRequestInit = RequestInit & {\n path?: string;\n search?: ConstructorParameters<typeof URLSearchParams>[0];\n sessionId?: string;\n url?: URL;\n};\n\nexport interface ApiResponseJSON<T> {\n response: T;\n errors?: TernSecureApiErrorJSON[];\n}\n\nexport type ApiResponse<T> = Response & { payload: ApiResponseJSON<T> | null };\n\nexport type ApiRequestCallback<T> = (request: ApiRequestInit, response?: ApiResponse<T>) => unknown;\n\nexport interface ApiRequestOptions {\n timeoutMs?: number;\n maxTries?: number;\n initialDelay?: number;\n factor?: number;\n maxDelay?: number;\n failureThreshold?: number;\n recoveryTimeoutMs?: number;\n}\n\nexport interface ApiClientOptions {\n domain?: string;\n apiUrl?: string;\n frontendApi?: string;\n instanceType?: InstanceType;\n apiBasePath?: string;\n}\n\nexport interface ApiClient {\n onBeforeRequest: (hook: BeforeRequestHook) => void;\n onAfterResponse: (hook: AfterResponseHook) => void;\n request: <T>(init: ApiRequestInit, opts?: ApiRequestOptions) => Promise<ApiResponse<T>>;\n}\n\nexport type BeforeRequestHook = () => boolean | Promise<boolean>;\nexport type AfterResponseHook = (response: ApiResponse<any>) => boolean | Promise<boolean>;\n\n// Error classes\nexport class NetworkError extends Error {\n constructor(\n public url: string,\n public original: Error,\n ) {\n super(`Network error for ${url}: ${original.message}`);\n this.name = 'NetworkError';\n }\n}\n\nexport class TimeoutError extends Error {\n constructor() {\n super('Request timed out');\n this.name = 'TimeoutError';\n }\n}\n\nexport class CircuitOpenError extends Error {\n constructor() {\n super('Circuit breaker is open');\n this.name = 'CircuitOpenError';\n }\n}\n\nexport class HTTPError extends Error {\n constructor(\n public status: number,\n public url: string,\n public body?: any,\n ) {\n super(`HTTP ${status} error for ${url}`);\n this.name = 'HTTPError';\n }\n}\n\n// Circuit breaker state interface\ninterface CircuitBreakerState {\n failures: number;\n lastFailureTime: number;\n state: 'closed' | 'open' | 'half-open';\n}\n\n// Client state interface\ninterface ClientState {\n circuitBreaker: CircuitBreakerState;\n beforeRequestHooks: BeforeRequestHook[];\n afterResponseHooks: AfterResponseHook[];\n clientOptions: ApiClientOptions;\n}\n\nfunction createInitialState(clientOptions: ApiClientOptions = {}): ClientState {\n return {\n circuitBreaker: {\n failures: 0,\n lastFailureTime: 0,\n state: 'closed',\n },\n beforeRequestHooks: [],\n afterResponseHooks: [],\n clientOptions,\n };\n}\n\nfunction addBeforeRequestHook(state: ClientState, hook: BeforeRequestHook): void {\n state.beforeRequestHooks.push(hook);\n}\n\nfunction addAfterResponseHook(state: ClientState, hook: AfterResponseHook): void {\n state.afterResponseHooks.push(hook);\n}\n\nasync function runBeforeRequestHooks(state: ClientState): Promise<boolean> {\n for (const hook of state.beforeRequestHooks) {\n const result = await hook();\n if (result === false) return false;\n }\n return true;\n}\n\nasync function runAfterResponseHooks(\n state: ClientState,\n response: ApiResponse<any>,\n): Promise<void> {\n for (const hook of state.afterResponseHooks) {\n await hook(response);\n }\n}\n\nfunction checkCircuitBreaker(state: ClientState, requestOptions: ApiRequestOptions): void {\n const { recoveryTimeoutMs = 60000 } = requestOptions;\n const now = Date.now();\n\n if (state.circuitBreaker.state === 'open') {\n if (now - state.circuitBreaker.lastFailureTime >= recoveryTimeoutMs) {\n state.circuitBreaker.state = 'half-open';\n } else {\n throw new CircuitOpenError();\n }\n }\n}\n\nfunction recordSuccess(state: ClientState): void {\n state.circuitBreaker.failures = 0;\n state.circuitBreaker.state = 'closed';\n}\n\nfunction recordFailure(state: ClientState, requestOptions: ApiRequestOptions): void {\n const { failureThreshold = 5 } = requestOptions;\n state.circuitBreaker.failures++;\n state.circuitBreaker.lastFailureTime = Date.now();\n\n if (state.circuitBreaker.failures >= failureThreshold) {\n state.circuitBreaker.state = 'open';\n }\n}\n\nfunction shouldRetry(\n state: ClientState,\n requestOptions: ApiRequestOptions,\n error: any,\n method: string,\n attempt: number,\n maxTries: number,\n): boolean {\n const isRetryable =\n error instanceof NetworkError && method.toUpperCase() === 'GET' && attempt < maxTries;\n\n if (!isRetryable) {\n recordFailure(state, requestOptions);\n }\n\n return isRetryable;\n}\n\nasync function retryWithBackoff<T>(\n state: ClientState,\n requestOptions: ApiRequestOptions,\n attemptFn: () => Promise<T>,\n shouldRetryFn: (error: any, attempt: number) => boolean,\n): Promise<T> {\n const {\n initialDelay = 700,\n factor = 2,\n maxDelay = 5000,\n maxTries = typeof navigator !== 'undefined' && navigator.onLine ? 4 : 11,\n } = requestOptions;\n\n let lastError: any;\n\n for (let attempt = 1; attempt <= maxTries; attempt++) {\n try {\n const result = await attemptFn();\n recordSuccess(state);\n return result;\n } catch (error) {\n lastError = error;\n\n if (!shouldRetryFn(error, attempt)) {\n throw error;\n }\n\n recordFailure(state, requestOptions);\n\n if (attempt < maxTries) {\n const delay = Math.min(initialDelay * Math.pow(factor, attempt - 1), maxDelay);\n await new Promise(resolve => setTimeout(resolve, jitteredDelay(delay)));\n }\n }\n }\n\n throw lastError;\n}\n\nexport function createCoreApiClient(clientOptions: ApiClientOptions): ApiClient {\n function buildUrl(requestInit: ApiRequestInit): URL {\n const isLocalhost = clientOptions.apiUrl?.includes('localhost') || clientOptions.apiUrl?.includes('127.0.0.1');\n const { path } = requestInit;\n const { instanceType, domain, apiUrl, apiBasePath = '/api/auth' } = clientOptions;\n const domainInProd = instanceType === 'production' ? domain : '';\n\n let baseUrl: string;\n if (isLocalhost) {\n // For localhost, use http and the apiUrl directly\n baseUrl = apiUrl?.startsWith('http') ? apiUrl : `http://${apiUrl}`;\n } else {\n //const domainInProd = instanceType === 'production' ? domain : '';\n baseUrl = `https://${domainInProd || apiUrl}`;\n }\n\n const fullPath = `${apiBasePath}/${path}`.replace(/\\/+/g, '/'); \n return buildUrlUtil(\n {\n base: baseUrl,\n pathname: fullPath,\n searchParams: requestInit.search ? new URLSearchParams(requestInit.search) : undefined,\n },\n { stringify: false },\n );\n }\n\n async function makeRequest<T>(\n state: ClientState,\n init: ApiRequestInit,\n opts: ApiRequestOptions = {},\n ): Promise<ApiResponse<T>> {\n const requestInit = { ...init };\n const { method = 'GET', body } = requestInit;\n const requestOptions = { ...opts };\n\n requestInit.url = buildUrl({ ...requestInit });\n checkCircuitBreaker(state, requestOptions);\n\n const shouldContinue = await runBeforeRequestHooks(state);\n if (!shouldContinue) {\n const mockResponse = new Response('{}', {\n status: 200,\n }) as ApiResponse<T>;\n mockResponse.payload = { response: {} as T };\n await runAfterResponseHooks(state, mockResponse);\n return mockResponse;\n }\n\n const { timeoutMs } = requestOptions;\n\n const overwrittenRequestMethod = method === 'GET' ? 'GET' : 'POST';\n const url = requestInit.url.toString();\n\n requestInit.headers = new Headers(requestInit.headers);\n\n if (\n method !== 'GET' &&\n !(body instanceof FormData) &&\n !requestInit.headers.has('content-type')\n ) {\n requestInit.headers.set('content-type', 'application/json');\n }\n\n if (requestInit.headers.get('content-type') === 'application/x-www-form-urlencoded') {\n requestInit.body = body\n ? stringifyQueryParams(body as any as Record<string, string>, {\n keyEncoder: camelToSnake,\n })\n : body;\n } else if (requestInit.headers.get('content-type') === 'application/json' && body) {\n requestInit.body = typeof body === 'string' ? body : JSON.stringify(body);\n }\n\n const attemptRequest = async (): Promise<ApiResponse<T>> => {\n const controller = new AbortController();\n const timeoutId = timeoutMs\n ? setTimeout(() => {\n controller.abort();\n }, timeoutMs)\n : null;\n\n let response: Response;\n const fetchOpts: ApiRequestInit = {\n ...requestInit,\n credentials: 'include',\n method: overwrittenRequestMethod,\n };\n\n try {\n response = await fetch(url, fetchOpts);\n\n if (timeoutId) clearTimeout(timeoutId);\n\n let payload: ApiResponseJSON<T> | null = null;\n\n if (response.status === 204) {\n payload = null;\n } else {\n try {\n const json = await response.json();\n payload = json;\n } catch {\n payload = { response: {} as T };\n }\n }\n\n const apiResponse = response as ApiResponse<T>;\n apiResponse.payload = payload;\n\n await runAfterResponseHooks(state, apiResponse);\n\n return apiResponse;\n } catch (error: any) {\n if (timeoutId) clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new TimeoutError();\n }\n\n throw new NetworkError(url, error);\n }\n };\n\n return retryWithBackoff(state, requestOptions, attemptRequest, (error, attempt) =>\n shouldRetry(\n state,\n requestOptions,\n error,\n overwrittenRequestMethod,\n attempt,\n requestOptions.maxTries || (typeof navigator !== 'undefined' && navigator.onLine ? 4 : 11),\n ),\n );\n }\n const state = createInitialState(clientOptions);\n\n return {\n onBeforeRequest: (hook: BeforeRequestHook) => addBeforeRequestHook(state, hook),\n onAfterResponse: (hook: AfterResponseHook) => addAfterResponseHook(state, hook),\n request: <T>(init: ApiRequestInit, opts: ApiRequestOptions = {}) =>\n makeRequest<T>(state, init, opts),\n };\n}\n"],"mappings":"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","names":["state"]}
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import { buildURL as buildUrlUtil, stringifyQueryParams } from "../utils";
|
|
2
|
-
class NetworkError extends Error {
|
|
3
|
-
constructor(url, original) {
|
|
4
|
-
super(`Network error for ${url}: ${original.message}`);
|
|
5
|
-
this.url = url;
|
|
6
|
-
this.original = original;
|
|
7
|
-
this.name = "NetworkError";
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
class TimeoutError extends Error {
|
|
11
|
-
constructor() {
|
|
12
|
-
super("Request timed out");
|
|
13
|
-
this.name = "TimeoutError";
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
class CircuitOpenError extends Error {
|
|
17
|
-
constructor() {
|
|
18
|
-
super("Circuit breaker is open");
|
|
19
|
-
this.name = "CircuitOpenError";
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
class HTTPError extends Error {
|
|
23
|
-
constructor(status, url, body) {
|
|
24
|
-
super(`HTTP ${status} error for ${url}`);
|
|
25
|
-
this.status = status;
|
|
26
|
-
this.url = url;
|
|
27
|
-
this.body = body;
|
|
28
|
-
this.name = "HTTPError";
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function camelToSnake(str) {
|
|
32
|
-
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
33
|
-
}
|
|
34
|
-
function jitteredDelay(delay) {
|
|
35
|
-
return delay * Math.random();
|
|
36
|
-
}
|
|
37
|
-
function buildUrl(requestInit, options) {
|
|
38
|
-
const { path } = requestInit;
|
|
39
|
-
const baseUrl = options.apiUrl;
|
|
40
|
-
if (!baseUrl) {
|
|
41
|
-
throw new Error("API URL is required");
|
|
42
|
-
}
|
|
43
|
-
const fullPath = path ? path.startsWith("/") ? path : `/${path}` : "";
|
|
44
|
-
const fullUrl = baseUrl.replace(/\/$/, "") + fullPath;
|
|
45
|
-
return buildUrlUtil(
|
|
46
|
-
{
|
|
47
|
-
base: fullUrl,
|
|
48
|
-
searchParams: requestInit.search ? new URLSearchParams(requestInit.search) : void 0
|
|
49
|
-
},
|
|
50
|
-
{ stringify: false }
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
class CoreApiClient {
|
|
54
|
-
constructor(options = {}) {
|
|
55
|
-
this.options = options;
|
|
56
|
-
}
|
|
57
|
-
circuitBreaker = {
|
|
58
|
-
failures: 0,
|
|
59
|
-
lastFailureTime: 0,
|
|
60
|
-
state: "closed"
|
|
61
|
-
};
|
|
62
|
-
beforeRequestHooks = [];
|
|
63
|
-
afterResponseHooks = [];
|
|
64
|
-
onBeforeRequest(hook) {
|
|
65
|
-
this.beforeRequestHooks.push(hook);
|
|
66
|
-
}
|
|
67
|
-
onAfterResponse(hook) {
|
|
68
|
-
this.afterResponseHooks.push(hook);
|
|
69
|
-
}
|
|
70
|
-
async runBeforeRequestHooks() {
|
|
71
|
-
for (const hook of this.beforeRequestHooks) {
|
|
72
|
-
const result = await hook();
|
|
73
|
-
if (result === false) return false;
|
|
74
|
-
}
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
async runAfterResponseHooks(response) {
|
|
78
|
-
for (const hook of this.afterResponseHooks) {
|
|
79
|
-
await hook(response);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
checkCircuitBreaker() {
|
|
83
|
-
const { recoveryTimeoutMs = 6e4 } = this.options;
|
|
84
|
-
const now = Date.now();
|
|
85
|
-
if (this.circuitBreaker.state === "open") {
|
|
86
|
-
if (now - this.circuitBreaker.lastFailureTime >= recoveryTimeoutMs) {
|
|
87
|
-
this.circuitBreaker.state = "half-open";
|
|
88
|
-
} else {
|
|
89
|
-
throw new CircuitOpenError();
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
recordSuccess() {
|
|
94
|
-
this.circuitBreaker.failures = 0;
|
|
95
|
-
this.circuitBreaker.state = "closed";
|
|
96
|
-
}
|
|
97
|
-
recordFailure() {
|
|
98
|
-
const { failureThreshold = 5 } = this.options;
|
|
99
|
-
this.circuitBreaker.failures++;
|
|
100
|
-
this.circuitBreaker.lastFailureTime = Date.now();
|
|
101
|
-
if (this.circuitBreaker.failures >= failureThreshold) {
|
|
102
|
-
this.circuitBreaker.state = "open";
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
shouldRetry(error, method, attempt, maxTries) {
|
|
106
|
-
const isRetryable = error instanceof NetworkError && method.toUpperCase() === "GET" && attempt < maxTries;
|
|
107
|
-
if (!isRetryable) {
|
|
108
|
-
this.recordFailure();
|
|
109
|
-
}
|
|
110
|
-
return isRetryable;
|
|
111
|
-
}
|
|
112
|
-
async retryWithBackoff(attemptFn, shouldRetry) {
|
|
113
|
-
const {
|
|
114
|
-
initialDelay = 700,
|
|
115
|
-
factor = 2,
|
|
116
|
-
maxDelay = 5e3,
|
|
117
|
-
maxTries = typeof navigator !== "undefined" && navigator.onLine ? 4 : 11
|
|
118
|
-
} = this.options;
|
|
119
|
-
let lastError;
|
|
120
|
-
for (let attempt = 1; attempt <= maxTries; attempt++) {
|
|
121
|
-
try {
|
|
122
|
-
const result = await attemptFn();
|
|
123
|
-
this.recordSuccess();
|
|
124
|
-
return result;
|
|
125
|
-
} catch (error) {
|
|
126
|
-
lastError = error;
|
|
127
|
-
if (!shouldRetry(error, attempt)) {
|
|
128
|
-
throw error;
|
|
129
|
-
}
|
|
130
|
-
this.recordFailure();
|
|
131
|
-
if (attempt < maxTries) {
|
|
132
|
-
const delay = Math.min(initialDelay * Math.pow(factor, attempt - 1), maxDelay);
|
|
133
|
-
await new Promise((resolve) => setTimeout(resolve, jitteredDelay(delay)));
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
throw lastError;
|
|
138
|
-
}
|
|
139
|
-
async request(init, opts = {}) {
|
|
140
|
-
const requestInit = { ...init };
|
|
141
|
-
const { method = "GET", body } = requestInit;
|
|
142
|
-
requestInit.url = buildUrl({ ...init }, { ...opts });
|
|
143
|
-
this.checkCircuitBreaker();
|
|
144
|
-
const shouldContinue = await this.runBeforeRequestHooks();
|
|
145
|
-
if (!shouldContinue) {
|
|
146
|
-
const mockResponse = new Response("{}", {
|
|
147
|
-
status: 200
|
|
148
|
-
});
|
|
149
|
-
mockResponse.payload = { response: {} };
|
|
150
|
-
await this.runAfterResponseHooks(mockResponse);
|
|
151
|
-
return mockResponse;
|
|
152
|
-
}
|
|
153
|
-
const mergedOptions = { ...opts };
|
|
154
|
-
const { timeoutMs } = mergedOptions;
|
|
155
|
-
const overwrittenRequestMethod = method === "GET" ? "GET" : "POST";
|
|
156
|
-
const url = requestInit.url.toString();
|
|
157
|
-
console.log("Request URL:", url);
|
|
158
|
-
requestInit.headers = new Headers(requestInit.headers);
|
|
159
|
-
if (method !== "GET" && !(body instanceof FormData) && !requestInit.headers.has("content-type")) {
|
|
160
|
-
requestInit.headers.set("content-type", "application/json");
|
|
161
|
-
}
|
|
162
|
-
if (requestInit.headers.get("content-type") === "application/x-www-form-urlencoded") {
|
|
163
|
-
requestInit.body = body ? stringifyQueryParams(body, {
|
|
164
|
-
keyEncoder: camelToSnake
|
|
165
|
-
}) : body;
|
|
166
|
-
} else if (requestInit.headers.get("content-type") === "application/json" && body) {
|
|
167
|
-
requestInit.body = typeof body === "string" ? body : JSON.stringify(body);
|
|
168
|
-
}
|
|
169
|
-
const attemptRequest = async () => {
|
|
170
|
-
const controller = new AbortController();
|
|
171
|
-
const timeoutId = timeoutMs ? setTimeout(() => {
|
|
172
|
-
controller.abort();
|
|
173
|
-
}, timeoutMs) : null;
|
|
174
|
-
let response;
|
|
175
|
-
const fetchOpts = {
|
|
176
|
-
...requestInit,
|
|
177
|
-
credentials: "include",
|
|
178
|
-
method: overwrittenRequestMethod
|
|
179
|
-
};
|
|
180
|
-
try {
|
|
181
|
-
response = await fetch(url, fetchOpts);
|
|
182
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
183
|
-
let payload = null;
|
|
184
|
-
if (response.status === 204) {
|
|
185
|
-
payload = null;
|
|
186
|
-
} else {
|
|
187
|
-
try {
|
|
188
|
-
const json = await response.json();
|
|
189
|
-
payload = json;
|
|
190
|
-
} catch {
|
|
191
|
-
payload = { response: {} };
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
const apiResponse = response;
|
|
195
|
-
apiResponse.payload = payload;
|
|
196
|
-
await this.runAfterResponseHooks(apiResponse);
|
|
197
|
-
return apiResponse;
|
|
198
|
-
} catch (error) {
|
|
199
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
200
|
-
if (error.name === "AbortError") {
|
|
201
|
-
throw new TimeoutError();
|
|
202
|
-
}
|
|
203
|
-
throw new NetworkError(url, error);
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
return this.retryWithBackoff(
|
|
207
|
-
attemptRequest,
|
|
208
|
-
(error, attempt) => this.shouldRetry(
|
|
209
|
-
error,
|
|
210
|
-
overwrittenRequestMethod,
|
|
211
|
-
attempt,
|
|
212
|
-
mergedOptions.maxTries || (typeof navigator !== "undefined" && navigator.onLine ? 4 : 11)
|
|
213
|
-
)
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
const coreApiClient = new CoreApiClient();
|
|
218
|
-
export {
|
|
219
|
-
CircuitOpenError,
|
|
220
|
-
CoreApiClient,
|
|
221
|
-
HTTPError,
|
|
222
|
-
NetworkError,
|
|
223
|
-
TimeoutError,
|
|
224
|
-
coreApiClient
|
|
225
|
-
};
|
|
226
|
-
//# sourceMappingURL=coreApiClient.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/instance/coreApiClient.ts"],"sourcesContent":["import type { TernSecureApiErrorJSON } from '@tern-secure/types';\n\nimport { buildURL as buildUrlUtil,stringifyQueryParams } from '../utils';\n\nexport type HTTPMethod =\n | 'CONNECT'\n | 'DELETE'\n | 'GET'\n | 'HEAD'\n | 'OPTIONS'\n | 'PATCH'\n | 'POST'\n | 'PUT'\n | 'TRACE';\n\nexport type ApiRequestInit = RequestInit & {\n path?: string;\n search?: ConstructorParameters<typeof URLSearchParams>[0];\n sessionId?: string;\n url?: URL;\n};\n\nexport interface ApiResponseJSON<T> {\n response: T;\n errors?: TernSecureApiErrorJSON[];\n}\n\nexport type ApiResponse<T> = Response & { payload: ApiResponseJSON<T> | null };\n\nexport type ApiRequestCallback<T> = (request: ApiRequestInit, response?: ApiResponse<T>) => unknown;\n\nexport interface RequestOptions {\n timeoutMs?: number;\n maxTries?: number;\n initialDelay?: number;\n factor?: number;\n maxDelay?: number;\n failureThreshold?: number;\n recoveryTimeoutMs?: number;\n apiUrl?: string;\n frontendApi?: string;\n}\n\nexport type BeforeRequestHook = () => boolean | Promise<boolean>;\nexport type AfterResponseHook = (response: ApiResponse<any>) => boolean | Promise<boolean>;\n\n// Error classes\nexport class NetworkError extends Error {\n constructor(\n public url: string,\n public original: Error,\n ) {\n super(`Network error for ${url}: ${original.message}`);\n this.name = 'NetworkError';\n }\n}\n\nexport class TimeoutError extends Error {\n constructor() {\n super('Request timed out');\n this.name = 'TimeoutError';\n }\n}\n\nexport class CircuitOpenError extends Error {\n constructor() {\n super('Circuit breaker is open');\n this.name = 'CircuitOpenError';\n }\n}\n\nexport class HTTPError extends Error {\n constructor(\n public status: number,\n public url: string,\n public body?: any,\n ) {\n super(`HTTP ${status} error for ${url}`);\n this.name = 'HTTPError';\n }\n}\n\n// Circuit breaker state\ninterface CircuitBreakerState {\n failures: number;\n lastFailureTime: number;\n state: 'closed' | 'open' | 'half-open';\n}\n\n// Utility functions\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);\n}\n\nfunction jitteredDelay(delay: number): number {\n return delay * Math.random();\n}\n\nfunction buildUrl(requestInit: ApiRequestInit, options: RequestOptions): URL {\n const { path } = requestInit;\n const baseUrl = options.apiUrl;\n\n if (!baseUrl) {\n throw new Error('API URL is required');\n }\n\n // Ensure proper URL construction by joining baseUrl and path\n const fullPath = path ? (path.startsWith('/') ? path : `/${path}`) : '';\n const fullUrl = baseUrl.replace(/\\/$/, '') + fullPath;\n\n return buildUrlUtil(\n {\n base: fullUrl,\n searchParams: requestInit.search ? new URLSearchParams(requestInit.search) : undefined,\n },\n { stringify: false },\n );\n}\n\nexport class CoreApiClient {\n private circuitBreaker: CircuitBreakerState = {\n failures: 0,\n lastFailureTime: 0,\n state: 'closed',\n };\n\n private beforeRequestHooks: BeforeRequestHook[] = [];\n private afterResponseHooks: AfterResponseHook[] = [];\n\n constructor(private options: RequestOptions = {}) {}\n\n onBeforeRequest(hook: BeforeRequestHook): void {\n this.beforeRequestHooks.push(hook);\n }\n\n onAfterResponse(hook: AfterResponseHook): void {\n this.afterResponseHooks.push(hook);\n }\n\n private async runBeforeRequestHooks(): Promise<boolean> {\n for (const hook of this.beforeRequestHooks) {\n const result = await hook();\n if (result === false) return false;\n }\n return true;\n }\n\n private async runAfterResponseHooks(response: ApiResponse<any>): Promise<void> {\n for (const hook of this.afterResponseHooks) {\n await hook(response);\n }\n }\n\n private checkCircuitBreaker(): void {\n const { recoveryTimeoutMs = 60000 } = this.options;\n const now = Date.now();\n\n if (this.circuitBreaker.state === 'open') {\n if (now - this.circuitBreaker.lastFailureTime >= recoveryTimeoutMs) {\n this.circuitBreaker.state = 'half-open';\n } else {\n throw new CircuitOpenError();\n }\n }\n }\n\n private recordSuccess(): void {\n this.circuitBreaker.failures = 0;\n this.circuitBreaker.state = 'closed';\n }\n\n private recordFailure(): void {\n const { failureThreshold = 5 } = this.options;\n this.circuitBreaker.failures++;\n this.circuitBreaker.lastFailureTime = Date.now();\n\n if (this.circuitBreaker.failures >= failureThreshold) {\n this.circuitBreaker.state = 'open';\n }\n }\n\n private shouldRetry(error: any, method: string, attempt: number, maxTries: number): boolean {\n // Only retry on network errors for GET requests\n const isRetryable =\n error instanceof NetworkError && method.toUpperCase() === 'GET' && attempt < maxTries;\n\n // If not retrying, we should still record the failure for circuit breaker\n if (!isRetryable) {\n this.recordFailure();\n }\n\n return isRetryable;\n }\n\n private async retryWithBackoff<T>(\n attemptFn: () => Promise<T>,\n shouldRetry: (error: any, attempt: number) => boolean,\n ): Promise<T> {\n const {\n initialDelay = 700,\n factor = 2,\n maxDelay = 5000,\n maxTries = typeof navigator !== 'undefined' && navigator.onLine ? 4 : 11,\n } = this.options;\n\n let lastError: any;\n\n for (let attempt = 1; attempt <= maxTries; attempt++) {\n try {\n const result = await attemptFn();\n this.recordSuccess();\n return result;\n } catch (error) {\n lastError = error;\n\n if (!shouldRetry(error, attempt)) {\n // shouldRetry already recorded the failure, so just throw\n throw error;\n }\n\n // This is a retryable error, record failure for circuit breaker\n this.recordFailure();\n\n if (attempt < maxTries) {\n const delay = Math.min(initialDelay * Math.pow(factor, attempt - 1), maxDelay);\n await new Promise(resolve => setTimeout(resolve, jitteredDelay(delay)));\n }\n }\n }\n\n throw lastError;\n }\n\n async request<T>(init: ApiRequestInit, opts: RequestOptions = {}): Promise<ApiResponse<T>> {\n const requestInit = { ...init };\n const { method = 'GET', body } = requestInit;\n\n requestInit.url = buildUrl({ ...init }, { ...opts });\n // Check circuit breaker\n this.checkCircuitBreaker();\n\n // Run before request hooks\n const shouldContinue = await this.runBeforeRequestHooks();\n if (!shouldContinue) {\n const mockResponse = new Response('{}', {\n status: 200,\n }) as ApiResponse<T>;\n mockResponse.payload = { response: {} as T };\n await this.runAfterResponseHooks(mockResponse);\n return mockResponse;\n }\n\n const mergedOptions = { ...opts };\n const { timeoutMs } = mergedOptions;\n\n // Safari workaround - only use GET/POST\n const overwrittenRequestMethod = method === 'GET' ? 'GET' : 'POST';\n\n const url = requestInit.url.toString();\n\n console.log('Request URL:', url);\n\n requestInit.headers = new Headers(requestInit.headers);\n\n // Set the default content type for non-GET requests.\n if (\n method !== 'GET' &&\n !(body instanceof FormData) &&\n !requestInit.headers.has('content-type')\n ) {\n requestInit.headers.set('content-type', 'application/json');\n }\n\n if (requestInit.headers.get('content-type') === 'application/x-www-form-urlencoded') {\n requestInit.body = body\n ? stringifyQueryParams(body as any as Record<string, string>, {\n keyEncoder: camelToSnake,\n })\n : body;\n } else if (requestInit.headers.get('content-type') === 'application/json' && body) {\n requestInit.body = typeof body === 'string' ? body : JSON.stringify(body);\n }\n\n const attemptRequest = async (): Promise<ApiResponse<T>> => {\n const controller = new AbortController();\n const timeoutId = timeoutMs\n ? setTimeout(() => {\n controller.abort();\n }, timeoutMs)\n : null;\n\n let response: Response;\n const fetchOpts: ApiRequestInit = {\n ...requestInit,\n credentials: 'include',\n method: overwrittenRequestMethod,\n };\n try {\n response = await fetch(url, fetchOpts);\n\n if (timeoutId) clearTimeout(timeoutId);\n\n // Parse response\n let payload: ApiResponseJSON<T> | null = null;\n\n if (response.status === 204) {\n payload = null;\n } else {\n try {\n const json = await response.json();\n payload = json;\n } catch {\n // If JSON parsing fails, create default payload\n payload = { response: {} as T };\n }\n }\n\n const apiResponse = response as ApiResponse<T>;\n apiResponse.payload = payload;\n\n // Run after response hooks\n await this.runAfterResponseHooks(apiResponse);\n\n return apiResponse;\n } catch (error: any) {\n if (timeoutId) clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new TimeoutError();\n }\n\n throw new NetworkError(url, error);\n }\n };\n\n return this.retryWithBackoff(attemptRequest, (error, attempt) =>\n this.shouldRetry(\n error,\n overwrittenRequestMethod,\n attempt,\n mergedOptions.maxTries || (typeof navigator !== 'undefined' && navigator.onLine ? 4 : 11),\n ),\n );\n }\n}\n\nexport const coreApiClient = new CoreApiClient();\n"],"mappings":"AAEA,SAAS,YAAY,cAAa,4BAA4B;AA6CvD,MAAM,qBAAqB,MAAM;AAAA,EACtC,YACS,KACA,UACP;AACA,UAAM,qBAAqB,GAAG,KAAK,SAAS,OAAO,EAAE;AAH9C;AACA;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,qBAAqB,MAAM;AAAA,EACtC,cAAc;AACZ,UAAM,mBAAmB;AACzB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,yBAAyB,MAAM;AAAA,EAC1C,cAAc;AACZ,UAAM,yBAAyB;AAC/B,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,kBAAkB,MAAM;AAAA,EACnC,YACS,QACA,KACA,MACP;AACA,UAAM,QAAQ,MAAM,cAAc,GAAG,EAAE;AAJhC;AACA;AACA;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAUA,SAAS,aAAa,KAAqB;AACzC,SAAO,IAAI,QAAQ,UAAU,YAAU,IAAI,OAAO,YAAY,CAAC,EAAE;AACnE;AAEA,SAAS,cAAc,OAAuB;AAC5C,SAAO,QAAQ,KAAK,OAAO;AAC7B;AAEA,SAAS,SAAS,aAA6B,SAA8B;AAC3E,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,UAAU,QAAQ;AAExB,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,qBAAqB;AAAA,EACvC;AAGA,QAAM,WAAW,OAAQ,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI,KAAM;AACrE,QAAM,UAAU,QAAQ,QAAQ,OAAO,EAAE,IAAI;AAE7C,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,cAAc,YAAY,SAAS,IAAI,gBAAgB,YAAY,MAAM,IAAI;AAAA,IAC/E;AAAA,IACA,EAAE,WAAW,MAAM;AAAA,EACrB;AACF;AAEO,MAAM,cAAc;AAAA,EAUzB,YAAoB,UAA0B,CAAC,GAAG;AAA9B;AAAA,EAA+B;AAAA,EAT3C,iBAAsC;AAAA,IAC5C,UAAU;AAAA,IACV,iBAAiB;AAAA,IACjB,OAAO;AAAA,EACT;AAAA,EAEQ,qBAA0C,CAAC;AAAA,EAC3C,qBAA0C,CAAC;AAAA,EAInD,gBAAgB,MAA+B;AAC7C,SAAK,mBAAmB,KAAK,IAAI;AAAA,EACnC;AAAA,EAEA,gBAAgB,MAA+B;AAC7C,SAAK,mBAAmB,KAAK,IAAI;AAAA,EACnC;AAAA,EAEA,MAAc,wBAA0C;AACtD,eAAW,QAAQ,KAAK,oBAAoB;AAC1C,YAAM,SAAS,MAAM,KAAK;AAC1B,UAAI,WAAW,MAAO,QAAO;AAAA,IAC/B;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,sBAAsB,UAA2C;AAC7E,eAAW,QAAQ,KAAK,oBAAoB;AAC1C,YAAM,KAAK,QAAQ;AAAA,IACrB;AAAA,EACF;AAAA,EAEQ,sBAA4B;AAClC,UAAM,EAAE,oBAAoB,IAAM,IAAI,KAAK;AAC3C,UAAM,MAAM,KAAK,IAAI;AAErB,QAAI,KAAK,eAAe,UAAU,QAAQ;AACxC,UAAI,MAAM,KAAK,eAAe,mBAAmB,mBAAmB;AAClE,aAAK,eAAe,QAAQ;AAAA,MAC9B,OAAO;AACL,cAAM,IAAI,iBAAiB;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,gBAAsB;AAC5B,SAAK,eAAe,WAAW;AAC/B,SAAK,eAAe,QAAQ;AAAA,EAC9B;AAAA,EAEQ,gBAAsB;AAC5B,UAAM,EAAE,mBAAmB,EAAE,IAAI,KAAK;AACtC,SAAK,eAAe;AACpB,SAAK,eAAe,kBAAkB,KAAK,IAAI;AAE/C,QAAI,KAAK,eAAe,YAAY,kBAAkB;AACpD,WAAK,eAAe,QAAQ;AAAA,IAC9B;AAAA,EACF;AAAA,EAEQ,YAAY,OAAY,QAAgB,SAAiB,UAA2B;AAE1F,UAAM,cACJ,iBAAiB,gBAAgB,OAAO,YAAY,MAAM,SAAS,UAAU;AAG/E,QAAI,CAAC,aAAa;AAChB,WAAK,cAAc;AAAA,IACrB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,iBACZ,WACA,aACY;AACZ,UAAM;AAAA,MACJ,eAAe;AAAA,MACf,SAAS;AAAA,MACT,WAAW;AAAA,MACX,WAAW,OAAO,cAAc,eAAe,UAAU,SAAS,IAAI;AAAA,IACxE,IAAI,KAAK;AAET,QAAI;AAEJ,aAAS,UAAU,GAAG,WAAW,UAAU,WAAW;AACpD,UAAI;AACF,cAAM,SAAS,MAAM,UAAU;AAC/B,aAAK,cAAc;AACnB,eAAO;AAAA,MACT,SAAS,OAAO;AACd,oBAAY;AAEZ,YAAI,CAAC,YAAY,OAAO,OAAO,GAAG;AAEhC,gBAAM;AAAA,QACR;AAGA,aAAK,cAAc;AAEnB,YAAI,UAAU,UAAU;AACtB,gBAAM,QAAQ,KAAK,IAAI,eAAe,KAAK,IAAI,QAAQ,UAAU,CAAC,GAAG,QAAQ;AAC7E,gBAAM,IAAI,QAAQ,aAAW,WAAW,SAAS,cAAc,KAAK,CAAC,CAAC;AAAA,QACxE;AAAA,MACF;AAAA,IACF;AAEA,UAAM;AAAA,EACR;AAAA,EAEA,MAAM,QAAW,MAAsB,OAAuB,CAAC,GAA4B;AACzF,UAAM,cAAc,EAAE,GAAG,KAAK;AAC9B,UAAM,EAAE,SAAS,OAAO,KAAK,IAAI;AAEjC,gBAAY,MAAM,SAAS,EAAE,GAAG,KAAK,GAAG,EAAE,GAAG,KAAK,CAAC;AAEnD,SAAK,oBAAoB;AAGzB,UAAM,iBAAiB,MAAM,KAAK,sBAAsB;AACxD,QAAI,CAAC,gBAAgB;AACnB,YAAM,eAAe,IAAI,SAAS,MAAM;AAAA,QACtC,QAAQ;AAAA,MACV,CAAC;AACD,mBAAa,UAAU,EAAE,UAAU,CAAC,EAAO;AAC3C,YAAM,KAAK,sBAAsB,YAAY;AAC7C,aAAO;AAAA,IACT;AAEA,UAAM,gBAAgB,EAAE,GAAG,KAAK;AAChC,UAAM,EAAE,UAAU,IAAI;AAGtB,UAAM,2BAA2B,WAAW,QAAQ,QAAQ;AAE5D,UAAM,MAAM,YAAY,IAAI,SAAS;AAErC,YAAQ,IAAI,gBAAgB,GAAG;AAE/B,gBAAY,UAAU,IAAI,QAAQ,YAAY,OAAO;AAGrD,QACE,WAAW,SACX,EAAE,gBAAgB,aAClB,CAAC,YAAY,QAAQ,IAAI,cAAc,GACvC;AACA,kBAAY,QAAQ,IAAI,gBAAgB,kBAAkB;AAAA,IAC5D;AAEA,QAAI,YAAY,QAAQ,IAAI,cAAc,MAAM,qCAAqC;AACnF,kBAAY,OAAO,OACf,qBAAqB,MAAuC;AAAA,QAC1D,YAAY;AAAA,MACd,CAAC,IACD;AAAA,IACN,WAAW,YAAY,QAAQ,IAAI,cAAc,MAAM,sBAAsB,MAAM;AACjF,kBAAY,OAAO,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI;AAAA,IAC1E;AAEA,UAAM,iBAAiB,YAAqC;AAC1D,YAAM,aAAa,IAAI,gBAAgB;AACvC,YAAM,YAAY,YACd,WAAW,MAAM;AACf,mBAAW,MAAM;AAAA,MACnB,GAAG,SAAS,IACZ;AAEJ,UAAI;AACJ,YAAM,YAA4B;AAAA,QAChC,GAAG;AAAA,QACH,aAAa;AAAA,QACb,QAAQ;AAAA,MACV;AACA,UAAI;AACF,mBAAW,MAAM,MAAM,KAAK,SAAS;AAErC,YAAI,UAAW,cAAa,SAAS;AAGrC,YAAI,UAAqC;AAEzC,YAAI,SAAS,WAAW,KAAK;AAC3B,oBAAU;AAAA,QACZ,OAAO;AACL,cAAI;AACF,kBAAM,OAAO,MAAM,SAAS,KAAK;AACjC,sBAAU;AAAA,UACZ,QAAQ;AAEN,sBAAU,EAAE,UAAU,CAAC,EAAO;AAAA,UAChC;AAAA,QACF;AAEA,cAAM,cAAc;AACpB,oBAAY,UAAU;AAGtB,cAAM,KAAK,sBAAsB,WAAW;AAE5C,eAAO;AAAA,MACT,SAAS,OAAY;AACnB,YAAI,UAAW,cAAa,SAAS;AAErC,YAAI,MAAM,SAAS,cAAc;AAC/B,gBAAM,IAAI,aAAa;AAAA,QACzB;AAEA,cAAM,IAAI,aAAa,KAAK,KAAK;AAAA,MACnC;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,MAAiB;AAAA,MAAgB,CAAC,OAAO,YACnD,KAAK;AAAA,QACH;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc,aAAa,OAAO,cAAc,eAAe,UAAU,SAAS,IAAI;AAAA,MACxF;AAAA,IACF;AAAA,EACF;AACF;AAEO,MAAM,gBAAgB,IAAI,cAAc;","names":[]}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { createEventBus } from "@tern-secure/shared/eventBus";
|
|
2
|
-
const events = {
|
|
3
|
-
UserSignOut: "user:userSignOut",
|
|
4
|
-
SessionChanged: "session:sessionChanged",
|
|
5
|
-
TokenRefreshed: "token:tokenRefreshed",
|
|
6
|
-
TokenUpdate: "token:tokenUpdate",
|
|
7
|
-
TokenJwt: "token:tokenJwt"
|
|
8
|
-
};
|
|
9
|
-
const eventBus = createEventBus();
|
|
10
|
-
export {
|
|
11
|
-
eventBus,
|
|
12
|
-
events
|
|
13
|
-
};
|
|
14
|
-
//# sourceMappingURL=events.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/instance/events.ts"],"sourcesContent":["import { createEventBus } from '@tern-secure/shared/eventBus';\nimport type { IdTokenResult } from '@tern-secure/types';\n\nexport const events = {\n UserSignOut: 'user:userSignOut',\n SessionChanged: 'session:sessionChanged',\n TokenRefreshed: 'token:tokenRefreshed',\n TokenUpdate: 'token:tokenUpdate',\n TokenJwt: 'token:tokenJwt',\n} as const;\n\ntype TokenUpdatePayload = { token: IdTokenResult | null };\ntype TokenJwt = { tokenType: 'idToken' | 'sessionToken' | 'refreshToken' | 'customToken', response: string };\n\ntype InternalEvents = {\n [events.UserSignOut]: null;\n [events.SessionChanged]: null;\n [events.TokenRefreshed]: TokenUpdatePayload;\n [events.TokenUpdate]: TokenUpdatePayload;\n [events.TokenJwt]: TokenJwt;\n};\n\nexport const eventBus = createEventBus<InternalEvents>();\n"],"mappings":"AAAA,SAAS,sBAAsB;AAGxB,MAAM,SAAS;AAAA,EACpB,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,UAAU;AACZ;AAaO,MAAM,WAAW,eAA+B;","names":[]}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { decode } from "../utils/jwt";
|
|
2
|
-
const createClientFromJwt = (jwtToken) => {
|
|
3
|
-
if (!jwtToken) {
|
|
4
|
-
return null;
|
|
5
|
-
}
|
|
6
|
-
const { decoded } = decode(jwtToken);
|
|
7
|
-
console.log("[TernAuth] Loaded user from JWT:", decoded);
|
|
8
|
-
return decoded;
|
|
9
|
-
};
|
|
10
|
-
const updateClient = (user, options) => {
|
|
11
|
-
try {
|
|
12
|
-
if (options.onUserUpdate) {
|
|
13
|
-
options.onUserUpdate(user);
|
|
14
|
-
}
|
|
15
|
-
if (user && options.onSessionUpdate) {
|
|
16
|
-
user.getIdTokenResult().then((tokenResult) => {
|
|
17
|
-
const session = {
|
|
18
|
-
status: "active",
|
|
19
|
-
token: tokenResult.token,
|
|
20
|
-
claims: tokenResult.claims,
|
|
21
|
-
issuedAtTime: tokenResult.issuedAtTime,
|
|
22
|
-
expirationTime: tokenResult.expirationTime,
|
|
23
|
-
authTime: tokenResult.authTime,
|
|
24
|
-
signInProvider: tokenResult.signInProvider || "unknown",
|
|
25
|
-
signInSecondFactor: tokenResult.signInSecondFactor
|
|
26
|
-
};
|
|
27
|
-
if (options.onSessionUpdate) {
|
|
28
|
-
options.onSessionUpdate(session);
|
|
29
|
-
}
|
|
30
|
-
}).catch((error) => {
|
|
31
|
-
console.error("[TernAuth] Error getting token result for session:", error);
|
|
32
|
-
if (options.onSessionUpdate) {
|
|
33
|
-
options.onSessionUpdate(null);
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
} else if (options.onSessionUpdate) {
|
|
37
|
-
options.onSessionUpdate(null);
|
|
38
|
-
}
|
|
39
|
-
} catch (error) {
|
|
40
|
-
console.error("[TernAuth] Error updating client:", error);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
export {
|
|
44
|
-
createClientFromJwt,
|
|
45
|
-
updateClient
|
|
46
|
-
};
|
|
47
|
-
//# sourceMappingURL=jwtClient.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/instance/jwtClient.ts"],"sourcesContent":["import type { DecodedIdToken, SignedInSession, TernSecureUser } from '@tern-secure/types';\n\nimport type { AuthCookieManager } from '../auth/AuthCookieManager';\nimport { decode } from '../utils/jwt';\n\n/**\n * Utility functions for loading user from JWT cookie and updating client state\n * This addresses the issue where user is undefined when client loads on page refresh\n */\n\nexport interface ClientUpdateOptions {\n authCookieManager: AuthCookieManager;\n onUserUpdate?: (user: TernSecureUser | null) => void;\n onSessionUpdate?: (session: SignedInSession | null) => void;\n}\n\n/**\n * Creates Firebase user from stored JWT token (ID token)\n * Since the token is already a Firebase ID token, we validate it and wait for auth state\n */\nconst createClientFromJwt = (jwtToken: string | null): DecodedIdToken | null => {\n if (!jwtToken) {\n return null;\n }\n\n const { decoded } = decode(jwtToken);\n\n console.log('[TernAuth] Loaded user from JWT:', decoded);\n\n return decoded;\n};\n\n/**\n * Updates client user state and triggers callbacks\n */\nconst updateClient = (\n user: TernSecureUser | null,\n options: Pick<ClientUpdateOptions, 'onUserUpdate' | 'onSessionUpdate'>,\n): void => {\n try {\n // Update user\n if (options.onUserUpdate) {\n options.onUserUpdate(user);\n }\n\n // Update session if user exists\n if (user && options.onSessionUpdate) {\n user\n .getIdTokenResult()\n .then(tokenResult => {\n const session: SignedInSession = {\n status: 'active',\n token: tokenResult.token,\n claims: tokenResult.claims,\n issuedAtTime: tokenResult.issuedAtTime,\n expirationTime: tokenResult.expirationTime,\n authTime: tokenResult.authTime,\n signInProvider: tokenResult.signInProvider || 'unknown',\n signInSecondFactor: tokenResult.signInSecondFactor,\n };\n if (options.onSessionUpdate) {\n options.onSessionUpdate(session);\n }\n })\n .catch(error => {\n console.error('[TernAuth] Error getting token result for session:', error);\n if (options.onSessionUpdate) {\n options.onSessionUpdate(null);\n }\n });\n } else if (options.onSessionUpdate) {\n options.onSessionUpdate(null);\n }\n } catch (error) {\n console.error('[TernAuth] Error updating client:', error);\n }\n};\n\nexport { createClientFromJwt, updateClient };\n"],"mappings":"AAGA,SAAS,cAAc;AAiBvB,MAAM,sBAAsB,CAAC,aAAmD;AAC9E,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,QAAM,EAAE,QAAQ,IAAI,OAAO,QAAQ;AAEnC,UAAQ,IAAI,oCAAoC,OAAO;AAEvD,SAAO;AACT;AAKA,MAAM,eAAe,CACnB,MACA,YACS;AACT,MAAI;AAEF,QAAI,QAAQ,cAAc;AACxB,cAAQ,aAAa,IAAI;AAAA,IAC3B;AAGA,QAAI,QAAQ,QAAQ,iBAAiB;AACnC,WACG,iBAAiB,EACjB,KAAK,iBAAe;AACnB,cAAM,UAA2B;AAAA,UAC/B,QAAQ;AAAA,UACR,OAAO,YAAY;AAAA,UACnB,QAAQ,YAAY;AAAA,UACpB,cAAc,YAAY;AAAA,UAC1B,gBAAgB,YAAY;AAAA,UAC5B,UAAU,YAAY;AAAA,UACtB,gBAAgB,YAAY,kBAAkB;AAAA,UAC9C,oBAAoB,YAAY;AAAA,QAClC;AACA,YAAI,QAAQ,iBAAiB;AAC3B,kBAAQ,gBAAgB,OAAO;AAAA,QACjC;AAAA,MACF,CAAC,EACA,MAAM,WAAS;AACd,gBAAQ,MAAM,sDAAsD,KAAK;AACzE,YAAI,QAAQ,iBAAiB;AAC3B,kBAAQ,gBAAgB,IAAI;AAAA,QAC9B;AAAA,MACF,CAAC;AAAA,IACL,WAAW,QAAQ,iBAAiB;AAClC,cAAQ,gBAAgB,IAAI;AAAA,IAC9B;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,qCAAqC,KAAK;AAAA,EAC1D;AACF;","names":[]}
|