@athenaintel/react 0.26.6 → 0.28.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 +32 -1
- package/dist/chat/StatewireApprovalCard.cjs +19 -0
- package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
- package/dist/chat/StatewireApprovalCard.js +19 -0
- package/dist/chat/StatewireApprovalCard.js.map +1 -1
- package/dist/chat/message-time.cjs.map +1 -1
- package/dist/chat/message-time.d.ts +4 -3
- package/dist/chat/message-time.js.map +1 -1
- package/dist/composer/QuoteContext.cjs +13 -1
- package/dist/composer/QuoteContext.cjs.map +1 -1
- package/dist/composer/QuoteContext.d.ts +11 -1
- package/dist/composer/QuoteContext.js +13 -1
- package/dist/composer/QuoteContext.js.map +1 -1
- package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
- package/dist/diagnostics/athena-diagnostics.d.ts +1 -1
- package/dist/diagnostics/athena-diagnostics.js.map +1 -1
- package/dist/diagnostics/errors.cjs +11 -2
- package/dist/diagnostics/errors.cjs.map +1 -1
- package/dist/diagnostics/errors.d.ts +11 -2
- package/dist/diagnostics/errors.js +11 -2
- package/dist/diagnostics/errors.js.map +1 -1
- package/dist/lib/athena-urls.cjs +1 -1
- package/dist/lib/athena-urls.cjs.map +1 -1
- package/dist/lib/athena-urls.js +1 -1
- package/dist/lib/athena-urls.js.map +1 -1
- package/dist/lib/posthog/mask-sensitive-input.cjs +34 -0
- package/dist/lib/posthog/mask-sensitive-input.cjs.map +1 -0
- package/dist/lib/posthog/mask-sensitive-input.d.ts +9 -0
- package/dist/lib/posthog/mask-sensitive-input.js +34 -0
- package/dist/lib/posthog/mask-sensitive-input.js.map +1 -0
- package/dist/packages/agent-runtime/src/conversation-index.cjs +2 -1
- package/dist/packages/agent-runtime/src/conversation-index.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/conversation-index.js +2 -1
- package/dist/packages/agent-runtime/src/conversation-index.js.map +1 -1
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +2 -0
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +2 -0
- package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
- package/dist/packages/agent-runtime/src/interrupts.cjs +26 -8
- package/dist/packages/agent-runtime/src/interrupts.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/interrupts.js +26 -8
- package/dist/packages/agent-runtime/src/interrupts.js.map +1 -1
- package/dist/packages/agent-runtime/src/messages.cjs +70 -14
- package/dist/packages/agent-runtime/src/messages.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/messages.js +71 -15
- package/dist/packages/agent-runtime/src/messages.js.map +1 -1
- package/dist/packages/agent-runtime/src/tool-metadata.cjs +75 -0
- package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/tool-metadata.js +75 -0
- package/dist/packages/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/packages/agent-runtime/src/tool-result.cjs +19 -4
- package/dist/packages/agent-runtime/src/tool-result.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/tool-result.js +19 -4
- package/dist/packages/agent-runtime/src/tool-result.js.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs +23 -4
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.js +24 -5
- package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
- package/dist/packages/react/package.json.cjs +1 -1
- package/dist/packages/react/package.json.js +1 -1
- package/dist/provider/AthenaProvider.cjs +3 -0
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.js +3 -0
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/provider/PostHogProvider.cjs +3 -1
- package/dist/provider/PostHogProvider.cjs.map +1 -1
- package/dist/provider/PostHogProvider.js +3 -1
- package/dist/provider/PostHogProvider.js.map +1 -1
- package/dist/runtime/auth.cjs +307 -108
- package/dist/runtime/auth.cjs.map +1 -1
- package/dist/runtime/auth.d.ts +151 -10
- package/dist/runtime/auth.js +307 -108
- package/dist/runtime/auth.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/runtime/auth.cjs
CHANGED
|
@@ -2,7 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const react = require("react");
|
|
4
4
|
const athenaDiagnostics = require("../diagnostics/athena-diagnostics.cjs");
|
|
5
|
-
|
|
5
|
+
const errors = require("../diagnostics/errors.cjs");
|
|
6
|
+
const CUSTOMER_APP_HOST = /^.+\.(?:(?:[a-z0-9-]+-)?app(?:\.[a-z0-9-]+)*|computer(?:-[a-z0-9-]+)?\.stg|code-sandpack(?:\.[a-z0-9-]+)*)\.athenaintel\.com$/i;
|
|
7
|
+
function isCustomerAppHost(hostname) {
|
|
8
|
+
return CUSTOMER_APP_HOST.test(hostname);
|
|
9
|
+
}
|
|
10
|
+
function isLoopbackHost(hostname) {
|
|
11
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
12
|
+
}
|
|
13
|
+
function isAthenaHost(hostname) {
|
|
14
|
+
return hostname === "athenaintel.com" || hostname.endsWith(".athenaintel.com");
|
|
15
|
+
}
|
|
16
|
+
function isTrustedParentOrigin({
|
|
6
17
|
origin,
|
|
7
18
|
trustedOrigins
|
|
8
19
|
}) {
|
|
@@ -12,11 +23,37 @@ function isTrustedOrigin({
|
|
|
12
23
|
return true;
|
|
13
24
|
}
|
|
14
25
|
const { hostname } = new URL(normalizedOrigin);
|
|
15
|
-
|
|
26
|
+
if (isCustomerAppHost(hostname)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return isAthenaHost(hostname) || hostname === "localhost";
|
|
16
30
|
} catch {
|
|
17
31
|
return false;
|
|
18
32
|
}
|
|
19
33
|
}
|
|
34
|
+
function refuseBridgeUrl({
|
|
35
|
+
url,
|
|
36
|
+
parentOrigin
|
|
37
|
+
}) {
|
|
38
|
+
let parsed;
|
|
39
|
+
let parentHostname;
|
|
40
|
+
try {
|
|
41
|
+
parsed = new URL(url);
|
|
42
|
+
parentHostname = new URL(parentOrigin).hostname;
|
|
43
|
+
} catch {
|
|
44
|
+
return "invalid";
|
|
45
|
+
}
|
|
46
|
+
if (parsed.username || parsed.password) return "invalid";
|
|
47
|
+
const { hostname } = parsed;
|
|
48
|
+
const loopbackPair = isLoopbackHost(hostname) && isLoopbackHost(parentHostname);
|
|
49
|
+
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && loopbackPair)) {
|
|
50
|
+
return "insecure";
|
|
51
|
+
}
|
|
52
|
+
if (isCustomerAppHost(hostname)) return "customer_app_host";
|
|
53
|
+
if (loopbackPair || isAthenaHost(hostname)) return null;
|
|
54
|
+
if (isAthenaHost(parentHostname) || isLoopbackHost(parentHostname)) return "other_domain";
|
|
55
|
+
return hostname === parentHostname || hostname.endsWith(`.${parentHostname}`) ? null : "other_domain";
|
|
56
|
+
}
|
|
20
57
|
const BRIDGE_URL_FIELDS = ["statewireSyncUrl", "backendBaseUrl", "appUrl"];
|
|
21
58
|
const EMPTY_BRIDGE_URLS = {
|
|
22
59
|
statewireSyncUrl: null,
|
|
@@ -37,16 +74,51 @@ function mergeBridgeConfigMessage(previous, payload) {
|
|
|
37
74
|
}
|
|
38
75
|
return next;
|
|
39
76
|
}
|
|
77
|
+
function acceptBridgeConfigMessage({
|
|
78
|
+
previous,
|
|
79
|
+
payload,
|
|
80
|
+
parentOrigin,
|
|
81
|
+
locked = false
|
|
82
|
+
}) {
|
|
83
|
+
const urls = {
|
|
84
|
+
statewireSyncUrl: previous.statewireSyncUrl,
|
|
85
|
+
backendBaseUrl: previous.backendBaseUrl,
|
|
86
|
+
appUrl: previous.appUrl
|
|
87
|
+
};
|
|
88
|
+
const rejected = [];
|
|
89
|
+
for (const field of BRIDGE_URL_FIELDS) {
|
|
90
|
+
const value = payload[field];
|
|
91
|
+
if (typeof value !== "string") continue;
|
|
92
|
+
const held = previous[field];
|
|
93
|
+
if (locked || held !== null) {
|
|
94
|
+
if (held !== value) rejected.push({ field, reason: "changed" });
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const refusal = refuseBridgeUrl({ url: value, parentOrigin });
|
|
98
|
+
if (refusal !== null) {
|
|
99
|
+
rejected.push({ field, reason: refusal });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
urls[field] = value;
|
|
103
|
+
}
|
|
104
|
+
return { urls, rejected };
|
|
105
|
+
}
|
|
40
106
|
function readAuthMintUrls(data) {
|
|
41
107
|
return mergeBridgeConfigMessage(EMPTY_BRIDGE_URLS, data);
|
|
42
108
|
}
|
|
43
109
|
const BRIDGE_TIMEOUT_MS = 2e3;
|
|
44
110
|
const PARENT_TOKEN_RENEWAL_TIMEOUT_MS = 5e3;
|
|
45
111
|
const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1e3;
|
|
46
|
-
const
|
|
47
|
-
const TOKEN_REFRESH_RETRY_MAX_MS = 5 * 60 * 1e3;
|
|
112
|
+
const TOKEN_REFRESH_MIN_DELAY_MS = 60 * 1e3;
|
|
48
113
|
const DEFAULT_TOKEN_REFRESH_MS = 55 * 60 * 1e3;
|
|
49
114
|
const EMPTY_TRUSTED_ORIGINS = [];
|
|
115
|
+
const DEFAULT_MINT_RETRY_POLICY = {
|
|
116
|
+
maxAttempts: 4,
|
|
117
|
+
baseDelayMs: 500,
|
|
118
|
+
maxDelayMs: 4e3,
|
|
119
|
+
timeoutMs: BRIDGE_TIMEOUT_MS,
|
|
120
|
+
random: Math.random
|
|
121
|
+
};
|
|
50
122
|
const normalizeOrigin = (value) => {
|
|
51
123
|
try {
|
|
52
124
|
return new URL(value).origin;
|
|
@@ -62,14 +134,74 @@ const getTokenRefreshDelay = (expiresAt, nowMs) => {
|
|
|
62
134
|
if (!Number.isFinite(expiresAtMs)) {
|
|
63
135
|
return DEFAULT_TOKEN_REFRESH_MS;
|
|
64
136
|
}
|
|
65
|
-
return Math.max(
|
|
66
|
-
};
|
|
67
|
-
const getAuthRetryDelay = (retryAttempt) => {
|
|
68
|
-
const safeAttempt = Number.isFinite(retryAttempt) ? Math.max(0, Math.floor(retryAttempt)) : 0;
|
|
69
|
-
return Math.min(TOKEN_REFRESH_RETRY_MAX_MS, TOKEN_REFRESH_RETRY_BASE_MS * 2 ** safeAttempt);
|
|
137
|
+
return Math.max(TOKEN_REFRESH_MIN_DELAY_MS, expiresAtMs - nowMs - TOKEN_REFRESH_BUFFER_MS);
|
|
70
138
|
};
|
|
139
|
+
function getMintRetryDelay({
|
|
140
|
+
retry,
|
|
141
|
+
policy = DEFAULT_MINT_RETRY_POLICY
|
|
142
|
+
}) {
|
|
143
|
+
const safeRetry = Number.isFinite(retry) ? Math.max(1, Math.floor(retry)) : 1;
|
|
144
|
+
const ceiling = Math.min(policy.maxDelayMs, policy.baseDelayMs * 2 ** (safeRetry - 1));
|
|
145
|
+
const jitter = Math.min(1, Math.max(0, policy.random()));
|
|
146
|
+
return Math.round(ceiling / 2 + ceiling / 2 * jitter);
|
|
147
|
+
}
|
|
148
|
+
function isRetryableMintStatus(status) {
|
|
149
|
+
return status >= 500 || status === 408 || status === 429;
|
|
150
|
+
}
|
|
151
|
+
async function readHostError(resp) {
|
|
152
|
+
try {
|
|
153
|
+
const body = await resp.json();
|
|
154
|
+
if (body && typeof body === "object" && "error" in body) {
|
|
155
|
+
const value = body.error;
|
|
156
|
+
return typeof value === "string" ? value.slice(0, 100) : null;
|
|
157
|
+
}
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
const MINT_RETRY_NOTE = "The SDK stopped retrying and does not connect without a credential. Remount the provider, or call renewToken({ force: true }), to try again.";
|
|
163
|
+
function mintFailureHint(failure) {
|
|
164
|
+
if (failure.kind === "transient") {
|
|
165
|
+
return `The host could not mint a viewer token. Check the Marathon and Agora logs. ${MINT_RETRY_NOTE}`;
|
|
166
|
+
}
|
|
167
|
+
if (failure.reason === "invalid_body") {
|
|
168
|
+
return `The host answered /_athena/auth without a JSON mint body, so this origin is not a Marathon bridge. Pass token, getToken or apiKey. ${MINT_RETRY_NOTE}`;
|
|
169
|
+
}
|
|
170
|
+
if (failure.status === 401) {
|
|
171
|
+
return `The host has no session for this viewer. Sign in again through the app URL. ${MINT_RETRY_NOTE}`;
|
|
172
|
+
}
|
|
173
|
+
if (failure.status === 403) {
|
|
174
|
+
return `The host does not give this viewer access to the app. ${MINT_RETRY_NOTE}`;
|
|
175
|
+
}
|
|
176
|
+
return `The host refused the mint request. ${MINT_RETRY_NOTE}`;
|
|
177
|
+
}
|
|
178
|
+
function mintFailureError({
|
|
179
|
+
failure,
|
|
180
|
+
attempts,
|
|
181
|
+
phase
|
|
182
|
+
}) {
|
|
183
|
+
const timedOut = failure.kind === "transient" && failure.reason === "timeout";
|
|
184
|
+
const answer = failure.status !== null ? `HTTP ${failure.status}` : timedOut ? "a timeout" : "a network error";
|
|
185
|
+
const message = failure.kind === "transient" ? `The host auth bridge (/_athena/auth) failed ${attempts} time${attempts === 1 ? "" : "s"} (last: ${answer}).` : failure.reason === "invalid_body" ? `The host auth bridge (/_athena/auth) answered ${answer} without a JSON mint body.` : `The host auth bridge (/_athena/auth) refused the mint (${answer}).`;
|
|
186
|
+
return new errors.AthenaSdkError({
|
|
187
|
+
code: timedOut ? "auth_bridge_timeout" : "auth_bridge_failed",
|
|
188
|
+
message,
|
|
189
|
+
hint: mintFailureHint(failure),
|
|
190
|
+
...failure.status !== null && { status: failure.status },
|
|
191
|
+
...failure.hostError !== null && { detail: { code: failure.hostError } },
|
|
192
|
+
...failure.kind === "transient" && failure.cause !== void 0 && { cause: failure.cause },
|
|
193
|
+
context: {
|
|
194
|
+
mode: "marathon",
|
|
195
|
+
phase,
|
|
196
|
+
attempts,
|
|
197
|
+
reason: failure.reason,
|
|
198
|
+
host_error: failure.hostError
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
71
202
|
function useParentBridge({
|
|
72
|
-
trustedOrigins = EMPTY_TRUSTED_ORIGINS
|
|
203
|
+
trustedOrigins = EMPTY_TRUSTED_ORIGINS,
|
|
204
|
+
mintRetryPolicy = DEFAULT_MINT_RETRY_POLICY
|
|
73
205
|
} = {}) {
|
|
74
206
|
const isInIframe = typeof window !== "undefined" && window.parent !== window;
|
|
75
207
|
const runtimeTrustedOrigins = react.useMemo(() => {
|
|
@@ -89,10 +221,14 @@ function useParentBridge({
|
|
|
89
221
|
token: null,
|
|
90
222
|
...EMPTY_BRIDGE_URLS,
|
|
91
223
|
hostAnswered: false,
|
|
92
|
-
ready: false
|
|
224
|
+
ready: false,
|
|
225
|
+
error: null
|
|
93
226
|
});
|
|
227
|
+
const mintRetryPolicyRef = react.useRef(mintRetryPolicy);
|
|
228
|
+
mintRetryPolicyRef.current = mintRetryPolicy;
|
|
94
229
|
const readySignalSent = react.useRef(false);
|
|
95
230
|
const configReceived = react.useRef(false);
|
|
231
|
+
const parentUrlsRef = react.useRef(EMPTY_BRIDGE_URLS);
|
|
96
232
|
const tokenRef = react.useRef(state.token);
|
|
97
233
|
tokenRef.current = state.token;
|
|
98
234
|
const renewerRef = react.useRef(null);
|
|
@@ -109,15 +245,29 @@ function useParentBridge({
|
|
|
109
245
|
};
|
|
110
246
|
const tokenWaiters = /* @__PURE__ */ new Set();
|
|
111
247
|
const handler = (event) => {
|
|
112
|
-
if (
|
|
248
|
+
if (event.source !== window.parent) return;
|
|
249
|
+
if (!isTrustedParentOrigin({ origin: event.origin, trustedOrigins: runtimeTrustedOrigins })) {
|
|
113
250
|
return;
|
|
114
251
|
}
|
|
115
252
|
if (!event.data || typeof event.data !== "object") return;
|
|
116
253
|
if (event.data.type === "athena-config") {
|
|
254
|
+
const { urls, rejected } = acceptBridgeConfigMessage({
|
|
255
|
+
previous: parentUrlsRef.current,
|
|
256
|
+
payload: event.data,
|
|
257
|
+
parentOrigin: event.origin,
|
|
258
|
+
locked: configReceived.current
|
|
259
|
+
});
|
|
117
260
|
configReceived.current = true;
|
|
261
|
+
parentUrlsRef.current = urls;
|
|
262
|
+
for (const { field, reason } of rejected) {
|
|
263
|
+
athenaDiagnostics.athenaDiagnostics.emit("auth.bridge.config_rejected", {
|
|
264
|
+
level: "warn",
|
|
265
|
+
data: { mode: "iframe", field, reason }
|
|
266
|
+
});
|
|
267
|
+
}
|
|
118
268
|
setState((prev) => ({
|
|
119
269
|
...prev,
|
|
120
|
-
...
|
|
270
|
+
...urls,
|
|
121
271
|
hostAnswered: true
|
|
122
272
|
// Don't set ready here — wait for athena-auth (token) or the timeout.
|
|
123
273
|
// Setting ready on config alone causes a race: the thread list fires
|
|
@@ -181,147 +331,189 @@ function useParentBridge({
|
|
|
181
331
|
let refreshTimer = null;
|
|
182
332
|
let controller = null;
|
|
183
333
|
let inFlight = null;
|
|
184
|
-
let
|
|
334
|
+
let retryWait = null;
|
|
185
335
|
const clearRefreshTimer = () => {
|
|
186
336
|
if (refreshTimer) {
|
|
187
337
|
clearTimeout(refreshTimer);
|
|
188
338
|
refreshTimer = null;
|
|
189
339
|
}
|
|
190
340
|
};
|
|
191
|
-
const
|
|
192
|
-
if (
|
|
193
|
-
|
|
341
|
+
const endRetryWait = () => {
|
|
342
|
+
if (!retryWait) return;
|
|
343
|
+
clearTimeout(retryWait.timer);
|
|
344
|
+
const { resolve } = retryWait;
|
|
345
|
+
retryWait = null;
|
|
346
|
+
resolve();
|
|
194
347
|
};
|
|
348
|
+
const waitBeforeRetry = (delayMs) => new Promise((resolve) => {
|
|
349
|
+
retryWait = { timer: setTimeout(endRetryWait, delayMs), resolve };
|
|
350
|
+
});
|
|
195
351
|
const scheduleRefresh = (expiresAt) => {
|
|
196
352
|
clearRefreshTimer();
|
|
197
|
-
retryAttempt = 0;
|
|
198
353
|
const delay = getTokenRefreshDelay(expiresAt, Date.now());
|
|
199
354
|
refreshTimer = setTimeout(() => {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
};
|
|
203
|
-
const scheduleRetry = () => {
|
|
204
|
-
clearRefreshTimer();
|
|
205
|
-
const delay = getAuthRetryDelay(retryAttempt);
|
|
206
|
-
retryAttempt += 1;
|
|
207
|
-
refreshTimer = setTimeout(() => {
|
|
208
|
-
void fetchAuth({ markReadyOnFailure: false });
|
|
355
|
+
refreshTimer = null;
|
|
356
|
+
void fetchAuth({ initial: false });
|
|
209
357
|
}, delay);
|
|
210
358
|
};
|
|
211
|
-
const
|
|
212
|
-
markReadyOnFailure
|
|
213
|
-
}) => {
|
|
359
|
+
const mintOnce = async (timeoutMs) => {
|
|
214
360
|
const requestController = new AbortController();
|
|
215
361
|
controller = requestController;
|
|
216
|
-
|
|
217
|
-
const rawEndSpan = isInitial ? athenaDiagnostics.athenaDiagnostics.span("auth.bridge", "auth.bridge.start", { mode: "marathon" }) : null;
|
|
218
|
-
let spanEnded = false;
|
|
219
|
-
const endBridgeSpan = rawEndSpan ? (name, options) => {
|
|
220
|
-
if (spanEnded) return 0;
|
|
221
|
-
spanEnded = true;
|
|
222
|
-
return rawEndSpan(name, options);
|
|
223
|
-
} : null;
|
|
362
|
+
let timedOut = false;
|
|
224
363
|
const requestTimer = setTimeout(() => {
|
|
364
|
+
timedOut = true;
|
|
225
365
|
requestController.abort();
|
|
226
|
-
|
|
227
|
-
data: { mode: "marathon", timeout_ms: BRIDGE_TIMEOUT_MS }
|
|
228
|
-
});
|
|
229
|
-
if (markReadyOnFailure) {
|
|
230
|
-
markReady();
|
|
231
|
-
}
|
|
232
|
-
}, BRIDGE_TIMEOUT_MS);
|
|
366
|
+
}, timeoutMs);
|
|
233
367
|
try {
|
|
234
368
|
const resp = await fetch("/_athena/auth", {
|
|
235
369
|
credentials: "include",
|
|
236
370
|
headers: { Accept: "application/json" },
|
|
237
371
|
signal: requestController.signal
|
|
238
372
|
});
|
|
239
|
-
if (cancelled) return
|
|
373
|
+
if (cancelled) return { kind: "cancelled" };
|
|
374
|
+
if (resp.status === 404) return { kind: "absent", status: resp.status };
|
|
240
375
|
if (!resp.ok) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
markReady();
|
|
246
|
-
} else {
|
|
247
|
-
const failure = {
|
|
248
|
-
level: "warn",
|
|
249
|
-
data: { mode: "marathon", status: resp.status }
|
|
250
|
-
};
|
|
251
|
-
if (endBridgeSpan) {
|
|
252
|
-
endBridgeSpan("auth.bridge.error", failure);
|
|
253
|
-
} else {
|
|
254
|
-
athenaDiagnostics.athenaDiagnostics.emit("auth.token.refresh_failed", failure);
|
|
255
|
-
}
|
|
256
|
-
if (markReadyOnFailure) {
|
|
257
|
-
markReady();
|
|
258
|
-
}
|
|
259
|
-
scheduleRetry();
|
|
376
|
+
const hostError = await readHostError(resp);
|
|
377
|
+
if (cancelled) return { kind: "cancelled" };
|
|
378
|
+
if (timedOut && isRetryableMintStatus(resp.status)) {
|
|
379
|
+
return { kind: "transient", status: null, reason: "timeout", hostError: null };
|
|
260
380
|
}
|
|
261
|
-
return
|
|
262
|
-
}
|
|
263
|
-
const data = await resp.json();
|
|
264
|
-
if (cancelled) return null;
|
|
265
|
-
if (endBridgeSpan) {
|
|
266
|
-
endBridgeSpan("auth.bridge.ready", {
|
|
267
|
-
data: { mode: "marathon", has_token: typeof data.token === "string" }
|
|
268
|
-
});
|
|
269
|
-
athenaDiagnostics.athenaDiagnostics.emit("auth.token.acquired", {
|
|
270
|
-
level: "debug",
|
|
271
|
-
data: { source: "marathon", expires_at: data.expires_at }
|
|
272
|
-
});
|
|
273
|
-
} else {
|
|
274
|
-
athenaDiagnostics.athenaDiagnostics.emit("auth.token.refreshed", {
|
|
275
|
-
level: "debug",
|
|
276
|
-
data: { source: "marathon", expires_at: data.expires_at }
|
|
277
|
-
});
|
|
381
|
+
return isRetryableMintStatus(resp.status) ? { kind: "transient", status: resp.status, reason: "status", hostError } : { kind: "refused", status: resp.status, reason: "status", hostError };
|
|
278
382
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
tokenRef.current = token;
|
|
287
|
-
scheduleRefresh(data.expires_at);
|
|
288
|
-
return token;
|
|
289
|
-
} catch (error) {
|
|
290
|
-
if (!cancelled && !(error instanceof DOMException && error.name === "AbortError")) {
|
|
291
|
-
const failure = { level: "warn", data: { mode: "marathon" }, error };
|
|
292
|
-
if (endBridgeSpan) {
|
|
293
|
-
endBridgeSpan("auth.bridge.error", failure);
|
|
294
|
-
} else {
|
|
295
|
-
athenaDiagnostics.athenaDiagnostics.emit("auth.token.refresh_failed", failure);
|
|
383
|
+
let data;
|
|
384
|
+
try {
|
|
385
|
+
data = await resp.json();
|
|
386
|
+
} catch (error) {
|
|
387
|
+
if (cancelled) return { kind: "cancelled" };
|
|
388
|
+
if (timedOut) {
|
|
389
|
+
return { kind: "transient", status: null, reason: "timeout", hostError: null, cause: error };
|
|
296
390
|
}
|
|
391
|
+
return { kind: "refused", status: resp.status, reason: "invalid_body", hostError: null };
|
|
297
392
|
}
|
|
298
|
-
if (
|
|
299
|
-
|
|
393
|
+
if (cancelled) return { kind: "cancelled" };
|
|
394
|
+
const token = data && typeof data === "object" ? data.token : null;
|
|
395
|
+
if (typeof token !== "string" || token === "") {
|
|
396
|
+
return { kind: "refused", status: resp.status, reason: "invalid_body", hostError: null };
|
|
300
397
|
}
|
|
301
|
-
|
|
302
|
-
|
|
398
|
+
return { kind: "minted", data };
|
|
399
|
+
} catch (error) {
|
|
400
|
+
if (cancelled) return { kind: "cancelled" };
|
|
401
|
+
if (timedOut) {
|
|
402
|
+
return { kind: "transient", status: null, reason: "timeout", hostError: null, cause: error };
|
|
303
403
|
}
|
|
304
|
-
return null;
|
|
404
|
+
return { kind: "transient", status: null, reason: "network", hostError: null, cause: error };
|
|
305
405
|
} finally {
|
|
306
406
|
clearTimeout(requestTimer);
|
|
307
407
|
if (controller === requestController) controller = null;
|
|
308
408
|
}
|
|
309
409
|
};
|
|
410
|
+
const runMintCycle = async ({ initial }) => {
|
|
411
|
+
const policy = mintRetryPolicyRef.current;
|
|
412
|
+
const maxAttempts = Math.max(1, Math.floor(policy.maxAttempts));
|
|
413
|
+
const phase = initial ? "initial" : "renewal";
|
|
414
|
+
const endBridgeSpan = initial ? athenaDiagnostics.athenaDiagnostics.span("auth.bridge", "auth.bridge.start", { mode: "marathon" }) : null;
|
|
415
|
+
let attempts = 0;
|
|
416
|
+
let lastFailure = null;
|
|
417
|
+
while (attempts < maxAttempts) {
|
|
418
|
+
attempts += 1;
|
|
419
|
+
const outcome = await mintOnce(policy.timeoutMs);
|
|
420
|
+
if (cancelled || outcome.kind === "cancelled") return null;
|
|
421
|
+
if (outcome.kind === "absent") {
|
|
422
|
+
endBridgeSpan == null ? void 0 : endBridgeSpan("auth.bridge.absent", {
|
|
423
|
+
data: { mode: "marathon", status: outcome.status }
|
|
424
|
+
});
|
|
425
|
+
setState(
|
|
426
|
+
(prev) => prev.ready && prev.error == null ? prev : { ...prev, ready: true, error: null }
|
|
427
|
+
);
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
if (outcome.kind === "minted") {
|
|
431
|
+
const { data } = outcome;
|
|
432
|
+
if (endBridgeSpan) {
|
|
433
|
+
endBridgeSpan("auth.bridge.ready", {
|
|
434
|
+
data: { mode: "marathon", has_token: typeof data.token === "string", attempts }
|
|
435
|
+
});
|
|
436
|
+
athenaDiagnostics.athenaDiagnostics.emit("auth.token.acquired", {
|
|
437
|
+
level: "debug",
|
|
438
|
+
data: { source: "marathon", expires_at: data.expires_at }
|
|
439
|
+
});
|
|
440
|
+
} else {
|
|
441
|
+
athenaDiagnostics.athenaDiagnostics.emit("auth.token.refreshed", {
|
|
442
|
+
level: "debug",
|
|
443
|
+
data: { source: "marathon", expires_at: data.expires_at, attempts }
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
const token = typeof data.token === "string" ? data.token : null;
|
|
447
|
+
setState({
|
|
448
|
+
token,
|
|
449
|
+
...readAuthMintUrls(data),
|
|
450
|
+
hostAnswered: true,
|
|
451
|
+
ready: true,
|
|
452
|
+
error: null
|
|
453
|
+
});
|
|
454
|
+
tokenRef.current = token;
|
|
455
|
+
scheduleRefresh(data.expires_at);
|
|
456
|
+
return token;
|
|
457
|
+
}
|
|
458
|
+
lastFailure = outcome;
|
|
459
|
+
if (outcome.kind === "refused" || attempts >= maxAttempts) break;
|
|
460
|
+
const delayMs = getMintRetryDelay({ retry: attempts, policy });
|
|
461
|
+
athenaDiagnostics.athenaDiagnostics.emit("auth.bridge.retry", {
|
|
462
|
+
level: "warn",
|
|
463
|
+
data: {
|
|
464
|
+
mode: "marathon",
|
|
465
|
+
phase,
|
|
466
|
+
status: outcome.status,
|
|
467
|
+
reason: outcome.reason,
|
|
468
|
+
host_error: outcome.hostError,
|
|
469
|
+
attempt: attempts,
|
|
470
|
+
max_attempts: maxAttempts,
|
|
471
|
+
delay_ms: delayMs
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
await waitBeforeRetry(delayMs);
|
|
475
|
+
if (cancelled) return null;
|
|
476
|
+
}
|
|
477
|
+
if (!lastFailure) return null;
|
|
478
|
+
const error = mintFailureError({ failure: lastFailure, attempts, phase });
|
|
479
|
+
const failureData = {
|
|
480
|
+
mode: "marathon",
|
|
481
|
+
phase,
|
|
482
|
+
status: lastFailure.status,
|
|
483
|
+
reason: lastFailure.reason,
|
|
484
|
+
attempts
|
|
485
|
+
};
|
|
486
|
+
if (endBridgeSpan) {
|
|
487
|
+
endBridgeSpan(
|
|
488
|
+
error.code === "auth_bridge_timeout" ? "auth.bridge.timeout" : "auth.bridge.error",
|
|
489
|
+
{ data: failureData, error }
|
|
490
|
+
);
|
|
491
|
+
} else {
|
|
492
|
+
athenaDiagnostics.athenaDiagnostics.emit("auth.token.refresh_failed", {
|
|
493
|
+
level: "error",
|
|
494
|
+
data: failureData,
|
|
495
|
+
error
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
setState((prev) => ({ ...prev, error, ready: true }));
|
|
499
|
+
return null;
|
|
500
|
+
};
|
|
310
501
|
const fetchAuth = (options) => {
|
|
311
|
-
inFlight ?? (inFlight =
|
|
502
|
+
inFlight ?? (inFlight = runMintCycle(options).finally(() => {
|
|
312
503
|
inFlight = null;
|
|
313
504
|
}));
|
|
314
505
|
return inFlight;
|
|
315
506
|
};
|
|
316
507
|
renewerRef.current = ({ force }) => {
|
|
317
508
|
if (!force) return Promise.resolve(tokenRef.current);
|
|
318
|
-
return fetchAuth({
|
|
509
|
+
return fetchAuth({ initial: false }).then((token) => token ?? tokenRef.current);
|
|
319
510
|
};
|
|
320
|
-
void fetchAuth({
|
|
511
|
+
void fetchAuth({ initial: true });
|
|
321
512
|
return () => {
|
|
322
513
|
cancelled = true;
|
|
323
514
|
controller == null ? void 0 : controller.abort();
|
|
324
515
|
clearRefreshTimer();
|
|
516
|
+
endRetryWait();
|
|
325
517
|
renewerRef.current = null;
|
|
326
518
|
};
|
|
327
519
|
}, [isInIframe]);
|
|
@@ -337,10 +529,17 @@ function useParentBridge({
|
|
|
337
529
|
function useParentAuth() {
|
|
338
530
|
return useParentBridge().token;
|
|
339
531
|
}
|
|
340
|
-
exports.
|
|
532
|
+
exports.DEFAULT_MINT_RETRY_POLICY = DEFAULT_MINT_RETRY_POLICY;
|
|
533
|
+
exports.acceptBridgeConfigMessage = acceptBridgeConfigMessage;
|
|
534
|
+
exports.getMintRetryDelay = getMintRetryDelay;
|
|
341
535
|
exports.getTokenRefreshDelay = getTokenRefreshDelay;
|
|
536
|
+
exports.isCustomerAppHost = isCustomerAppHost;
|
|
537
|
+
exports.isRetryableMintStatus = isRetryableMintStatus;
|
|
538
|
+
exports.isTrustedParentOrigin = isTrustedParentOrigin;
|
|
342
539
|
exports.mergeBridgeConfigMessage = mergeBridgeConfigMessage;
|
|
540
|
+
exports.mintFailureError = mintFailureError;
|
|
343
541
|
exports.readAuthMintUrls = readAuthMintUrls;
|
|
542
|
+
exports.refuseBridgeUrl = refuseBridgeUrl;
|
|
344
543
|
exports.useParentAuth = useParentAuth;
|
|
345
544
|
exports.useParentBridge = useParentBridge;
|
|
346
545
|
//# sourceMappingURL=auth.cjs.map
|