@athenaintel/react 0.24.0 → 0.25.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.
Files changed (197) hide show
  1. package/README.md +29 -4
  2. package/dist/assets/AssetPanel.cjs +3 -2
  3. package/dist/assets/AssetPanel.cjs.map +1 -1
  4. package/dist/assets/AssetPanel.js +3 -2
  5. package/dist/assets/AssetPanel.js.map +1 -1
  6. package/dist/assets/AthenaAssetEmbed.cjs +1 -0
  7. package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
  8. package/dist/assets/AthenaAssetEmbed.js +1 -0
  9. package/dist/assets/AthenaAssetEmbed.js.map +1 -1
  10. package/dist/assets/use-asset-embed.cjs +22 -23
  11. package/dist/assets/use-asset-embed.cjs.map +1 -1
  12. package/dist/assets/use-asset-embed.d.ts +9 -0
  13. package/dist/assets/use-asset-embed.js +22 -23
  14. package/dist/assets/use-asset-embed.js.map +1 -1
  15. package/dist/auth/AthenaAuthProvider.cjs +32 -4
  16. package/dist/auth/AthenaAuthProvider.cjs.map +1 -1
  17. package/dist/auth/AthenaAuthProvider.d.ts +21 -0
  18. package/dist/auth/AthenaAuthProvider.js +33 -5
  19. package/dist/auth/AthenaAuthProvider.js.map +1 -1
  20. package/dist/auth/AthenaCredentialSourceContext.cjs +21 -0
  21. package/dist/auth/AthenaCredentialSourceContext.cjs.map +1 -0
  22. package/dist/auth/AthenaCredentialSourceContext.d.ts +14 -0
  23. package/dist/auth/AthenaCredentialSourceContext.js +21 -0
  24. package/dist/auth/AthenaCredentialSourceContext.js.map +1 -0
  25. package/dist/auth/AthenaSSOProvider.cjs +71 -47
  26. package/dist/auth/AthenaSSOProvider.cjs.map +1 -1
  27. package/dist/auth/AthenaSSOProvider.js +71 -47
  28. package/dist/auth/AthenaSSOProvider.js.map +1 -1
  29. package/dist/auth/credential-source.cjs +120 -0
  30. package/dist/auth/credential-source.cjs.map +1 -0
  31. package/dist/auth/credential-source.d.ts +68 -0
  32. package/dist/auth/credential-source.js +120 -0
  33. package/dist/auth/credential-source.js.map +1 -0
  34. package/dist/auth/types.d.ts +9 -0
  35. package/dist/chat/AthenaChat.cjs +8 -0
  36. package/dist/chat/AthenaChat.cjs.map +1 -1
  37. package/dist/chat/AthenaChat.js +8 -0
  38. package/dist/chat/AthenaChat.js.map +1 -1
  39. package/dist/chat/StatewireApprovalCard.cjs +31 -6
  40. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  41. package/dist/chat/StatewireApprovalCard.d.ts +34 -4
  42. package/dist/chat/StatewireApprovalCard.js +33 -8
  43. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  44. package/dist/chat-ui/index.d.ts +1 -1
  45. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs +20 -11
  46. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs.map +1 -1
  47. package/dist/chat-ui/mentions/suggestions/data/api-client.d.ts +11 -2
  48. package/dist/chat-ui/mentions/suggestions/data/api-client.js +20 -11
  49. package/dist/chat-ui/mentions/suggestions/data/api-client.js.map +1 -1
  50. package/dist/chat-ui/statewire.d.ts +1 -1
  51. package/dist/collab/mint.cjs +6 -15
  52. package/dist/collab/mint.cjs.map +1 -1
  53. package/dist/collab/mint.d.ts +8 -0
  54. package/dist/collab/mint.js +6 -15
  55. package/dist/collab/mint.js.map +1 -1
  56. package/dist/collab/presence-mint.cjs +20 -16
  57. package/dist/collab/presence-mint.cjs.map +1 -1
  58. package/dist/collab/presence-mint.js +21 -17
  59. package/dist/collab/presence-mint.js.map +1 -1
  60. package/dist/collab/presence-react.cjs +12 -5
  61. package/dist/collab/presence-react.cjs.map +1 -1
  62. package/dist/collab/presence-react.d.ts +3 -1
  63. package/dist/collab/presence-react.js +12 -5
  64. package/dist/collab/presence-react.js.map +1 -1
  65. package/dist/collab/react.cjs +5 -2
  66. package/dist/collab/react.cjs.map +1 -1
  67. package/dist/collab/react.js +5 -2
  68. package/dist/collab/react.js.map +1 -1
  69. package/dist/dictation/use-athena-dictation-engine.cjs +12 -11
  70. package/dist/dictation/use-athena-dictation-engine.cjs.map +1 -1
  71. package/dist/dictation/use-athena-dictation-engine.d.ts +4 -3
  72. package/dist/dictation/use-athena-dictation-engine.js +12 -11
  73. package/dist/dictation/use-athena-dictation-engine.js.map +1 -1
  74. package/dist/index.cjs +1 -0
  75. package/dist/index.cjs.map +1 -1
  76. package/dist/index.d.ts +3 -3
  77. package/dist/index.js +2 -1
  78. package/dist/packages/agent-runtime/src/conversation-list.cjs.map +1 -1
  79. package/dist/packages/agent-runtime/src/conversation-list.js.map +1 -1
  80. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +1 -0
  81. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  82. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +1 -0
  83. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  84. package/dist/packages/agent-runtime/src/interrupts.cjs +22 -2
  85. package/dist/packages/agent-runtime/src/interrupts.cjs.map +1 -1
  86. package/dist/packages/agent-runtime/src/interrupts.js +22 -2
  87. package/dist/packages/agent-runtime/src/interrupts.js.map +1 -1
  88. package/dist/packages/agent-runtime/src/messages.cjs +2 -1
  89. package/dist/packages/agent-runtime/src/messages.cjs.map +1 -1
  90. package/dist/packages/agent-runtime/src/messages.js +2 -1
  91. package/dist/packages/agent-runtime/src/messages.js.map +1 -1
  92. package/dist/packages/agent-runtime/src/voice-presentation.cjs +6 -0
  93. package/dist/packages/agent-runtime/src/voice-presentation.cjs.map +1 -1
  94. package/dist/packages/agent-runtime/src/voice-presentation.js +6 -0
  95. package/dist/packages/agent-runtime/src/voice-presentation.js.map +1 -1
  96. package/dist/packages/agent-runtime/src/voice-turn-grouping.cjs +68 -0
  97. package/dist/packages/agent-runtime/src/voice-turn-grouping.cjs.map +1 -0
  98. package/dist/packages/agent-runtime/src/voice-turn-grouping.js +68 -0
  99. package/dist/packages/agent-runtime/src/voice-turn-grouping.js.map +1 -0
  100. package/dist/packages/react/package.json.cjs +1 -1
  101. package/dist/packages/react/package.json.js +1 -1
  102. package/dist/provider/AthenaContext.cjs.map +1 -1
  103. package/dist/provider/AthenaContext.d.ts +7 -1
  104. package/dist/provider/AthenaContext.js.map +1 -1
  105. package/dist/provider/AthenaProvider.cjs +24 -2
  106. package/dist/provider/AthenaProvider.cjs.map +1 -1
  107. package/dist/provider/AthenaProvider.js +24 -2
  108. package/dist/provider/AthenaProvider.js.map +1 -1
  109. package/dist/provider/theme-style-context.cjs +12 -0
  110. package/dist/provider/theme-style-context.cjs.map +1 -0
  111. package/dist/provider/theme-style-context.d.ts +8 -0
  112. package/dist/provider/theme-style-context.js +12 -0
  113. package/dist/provider/theme-style-context.js.map +1 -0
  114. package/dist/runtime/auth-refusal-restart.cjs +66 -0
  115. package/dist/runtime/auth-refusal-restart.cjs.map +1 -0
  116. package/dist/runtime/auth-refusal-restart.d.ts +35 -0
  117. package/dist/runtime/auth-refusal-restart.js +66 -0
  118. package/dist/runtime/auth-refusal-restart.js.map +1 -0
  119. package/dist/runtime/auth.cjs +58 -21
  120. package/dist/runtime/auth.cjs.map +1 -1
  121. package/dist/runtime/auth.d.ts +11 -1
  122. package/dist/runtime/auth.js +59 -22
  123. package/dist/runtime/auth.js.map +1 -1
  124. package/dist/runtime/credential-renewal.cjs +29 -0
  125. package/dist/runtime/credential-renewal.cjs.map +1 -0
  126. package/dist/runtime/credential-renewal.d.ts +35 -0
  127. package/dist/runtime/credential-renewal.js +29 -0
  128. package/dist/runtime/credential-renewal.js.map +1 -0
  129. package/dist/search/useAthenaRepoSearch.cjs +3 -5
  130. package/dist/search/useAthenaRepoSearch.cjs.map +1 -1
  131. package/dist/search/useAthenaRepoSearch.d.ts +7 -7
  132. package/dist/search/useAthenaRepoSearch.js +3 -5
  133. package/dist/search/useAthenaRepoSearch.js.map +1 -1
  134. package/dist/styles.css +1 -1
  135. package/dist/threads/SessionList.cjs +87 -16
  136. package/dist/threads/SessionList.cjs.map +1 -1
  137. package/dist/threads/SessionList.js +88 -17
  138. package/dist/threads/SessionList.js.map +1 -1
  139. package/dist/threads/api.cjs +5 -1
  140. package/dist/threads/api.cjs.map +1 -1
  141. package/dist/threads/api.js +5 -1
  142. package/dist/threads/api.js.map +1 -1
  143. package/dist/threads/conversation-list-fetcher.cjs +5 -5
  144. package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
  145. package/dist/threads/conversation-list-fetcher.d.ts +6 -5
  146. package/dist/threads/conversation-list-fetcher.js +5 -5
  147. package/dist/threads/conversation-list-fetcher.js.map +1 -1
  148. package/dist/threads/session-list-model.cjs +13 -5
  149. package/dist/threads/session-list-model.cjs.map +1 -1
  150. package/dist/threads/session-list-model.d.ts +10 -3
  151. package/dist/threads/session-list-model.js +13 -5
  152. package/dist/threads/session-list-model.js.map +1 -1
  153. package/dist/threads/statewire-thread-list.cjs +13 -14
  154. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  155. package/dist/threads/statewire-thread-list.js +13 -14
  156. package/dist/threads/statewire-thread-list.js.map +1 -1
  157. package/dist/threads/use-rest-conversation-history.cjs +6 -2
  158. package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
  159. package/dist/threads/use-rest-conversation-history.d.ts +3 -1
  160. package/dist/threads/use-rest-conversation-history.js +7 -3
  161. package/dist/threads/use-rest-conversation-history.js.map +1 -1
  162. package/dist/threads/use-statewire-conversation-history.cjs +9 -2
  163. package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
  164. package/dist/threads/use-statewire-conversation-history.d.ts +3 -1
  165. package/dist/threads/use-statewire-conversation-history.js +10 -3
  166. package/dist/threads/use-statewire-conversation-history.js.map +1 -1
  167. package/dist/threads/use-thread-archive.cjs +2 -1
  168. package/dist/threads/use-thread-archive.cjs.map +1 -1
  169. package/dist/threads/use-thread-archive.d.ts +3 -1
  170. package/dist/threads/use-thread-archive.js +2 -1
  171. package/dist/threads/use-thread-archive.js.map +1 -1
  172. package/dist/threads/use-thread-favorite.cjs.map +1 -1
  173. package/dist/threads/use-thread-favorite.d.ts +3 -1
  174. package/dist/threads/use-thread-favorite.js.map +1 -1
  175. package/dist/threads/use-thread-field-override.cjs +2 -1
  176. package/dist/threads/use-thread-field-override.cjs.map +1 -1
  177. package/dist/threads/use-thread-field-override.d.ts +3 -1
  178. package/dist/threads/use-thread-field-override.js +2 -1
  179. package/dist/threads/use-thread-field-override.js.map +1 -1
  180. package/dist/threads/use-thread-rename.cjs.map +1 -1
  181. package/dist/threads/use-thread-rename.d.ts +3 -1
  182. package/dist/threads/use-thread-rename.js.map +1 -1
  183. package/dist/tools/tool-uis.cjs +11 -15
  184. package/dist/tools/tool-uis.cjs.map +1 -1
  185. package/dist/tools/tool-uis.js +11 -15
  186. package/dist/tools/tool-uis.js.map +1 -1
  187. package/dist/ui/popover.cjs +46 -0
  188. package/dist/ui/popover.cjs.map +1 -0
  189. package/dist/ui/popover.d.ts +6 -0
  190. package/dist/ui/popover.js +46 -0
  191. package/dist/ui/popover.js.map +1 -0
  192. package/dist/voice/use-athena-voice-session.cjs +17 -5
  193. package/dist/voice/use-athena-voice-session.cjs.map +1 -1
  194. package/dist/voice/use-athena-voice-session.d.ts +2 -2
  195. package/dist/voice/use-athena-voice-session.js +17 -5
  196. package/dist/voice/use-athena-voice-session.js.map +1 -1
  197. package/package.json +2 -2
@@ -41,6 +41,7 @@ function readAuthMintUrls(data) {
41
41
  return mergeBridgeConfigMessage(EMPTY_BRIDGE_URLS, data);
42
42
  }
43
43
  const BRIDGE_TIMEOUT_MS = 2e3;
44
+ const PARENT_TOKEN_RENEWAL_TIMEOUT_MS = 5e3;
44
45
  const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1e3;
45
46
  const TOKEN_REFRESH_RETRY_BASE_MS = 60 * 1e3;
46
47
  const TOKEN_REFRESH_RETRY_MAX_MS = 5 * 60 * 1e3;
@@ -92,6 +93,9 @@ function useParentBridge({
92
93
  });
93
94
  const readySignalSent = react.useRef(false);
94
95
  const configReceived = react.useRef(false);
96
+ const tokenRef = react.useRef(state.token);
97
+ tokenRef.current = state.token;
98
+ const renewerRef = react.useRef(null);
95
99
  react.useEffect(() => {
96
100
  if (!isInIframe) return;
97
101
  const endBridgeSpan = athenaDiagnostics.athenaDiagnostics.span("auth.bridge", "auth.bridge.start", {
@@ -103,6 +107,7 @@ function useParentBridge({
103
107
  spanEnded = true;
104
108
  return endBridgeSpan(name, options);
105
109
  };
110
+ const tokenWaiters = /* @__PURE__ */ new Set();
106
111
  const handler = (event) => {
107
112
  if (!isTrustedOrigin({ origin: event.origin, trustedOrigins: runtimeTrustedOrigins })) {
108
113
  return;
@@ -130,9 +135,24 @@ function useParentBridge({
130
135
  token: event.data.token,
131
136
  ready: true
132
137
  }));
138
+ tokenRef.current = event.data.token;
139
+ for (const resolve of [...tokenWaiters]) resolve(event.data.token);
133
140
  }
134
141
  };
135
142
  window.addEventListener("message", handler);
143
+ renewerRef.current = ({ force }) => {
144
+ if (!force) return Promise.resolve(tokenRef.current);
145
+ return new Promise((resolve) => {
146
+ const settle = (token) => {
147
+ clearTimeout(timer2);
148
+ tokenWaiters.delete(settle);
149
+ resolve(token ?? tokenRef.current);
150
+ };
151
+ const timer2 = setTimeout(() => settle(null), PARENT_TOKEN_RENEWAL_TIMEOUT_MS);
152
+ tokenWaiters.add(settle);
153
+ window.parent.postMessage({ type: "athena-auth-ready" }, "*");
154
+ });
155
+ };
136
156
  if (!readySignalSent.current) {
137
157
  window.parent.postMessage({ type: "athena-auth-ready" }, "*");
138
158
  readySignalSent.current = true;
@@ -150,22 +170,18 @@ function useParentBridge({
150
170
  return () => {
151
171
  window.removeEventListener("message", handler);
152
172
  clearTimeout(timer);
173
+ renewerRef.current = null;
174
+ for (const resolve of [...tokenWaiters]) resolve(null);
153
175
  };
154
176
  }, [isInIframe, runtimeTrustedOrigins]);
155
177
  react.useEffect(() => {
156
178
  if (isInIframe) return;
157
179
  if (typeof window === "undefined") return;
158
180
  let cancelled = false;
159
- let requestTimer = null;
160
181
  let refreshTimer = null;
161
182
  let controller = null;
183
+ let inFlight = null;
162
184
  let retryAttempt = 0;
163
- const clearRequestTimer = () => {
164
- if (requestTimer) {
165
- clearTimeout(requestTimer);
166
- requestTimer = null;
167
- }
168
- };
169
185
  const clearRefreshTimer = () => {
170
186
  if (refreshTimer) {
171
187
  clearTimeout(refreshTimer);
@@ -192,11 +208,11 @@ function useParentBridge({
192
208
  void fetchAuth({ markReadyOnFailure: false });
193
209
  }, delay);
194
210
  };
195
- const fetchAuth = async ({
211
+ const mintAuth = async ({
196
212
  markReadyOnFailure
197
213
  }) => {
198
- controller == null ? void 0 : controller.abort();
199
- controller = new AbortController();
214
+ const requestController = new AbortController();
215
+ controller = requestController;
200
216
  const isInitial = markReadyOnFailure;
201
217
  const rawEndSpan = isInitial ? athenaDiagnostics.athenaDiagnostics.span("auth.bridge", "auth.bridge.start", { mode: "marathon" }) : null;
202
218
  let spanEnded = false;
@@ -205,9 +221,8 @@ function useParentBridge({
205
221
  spanEnded = true;
206
222
  return rawEndSpan(name, options);
207
223
  } : null;
208
- clearRequestTimer();
209
- requestTimer = setTimeout(() => {
210
- controller == null ? void 0 : controller.abort();
224
+ const requestTimer = setTimeout(() => {
225
+ requestController.abort();
211
226
  endBridgeSpan == null ? void 0 : endBridgeSpan("auth.bridge.timeout", {
212
227
  data: { mode: "marathon", timeout_ms: BRIDGE_TIMEOUT_MS }
213
228
  });
@@ -219,9 +234,9 @@ function useParentBridge({
219
234
  const resp = await fetch("/_athena/auth", {
220
235
  credentials: "include",
221
236
  headers: { Accept: "application/json" },
222
- signal: controller.signal
237
+ signal: requestController.signal
223
238
  });
224
- if (cancelled) return;
239
+ if (cancelled) return null;
225
240
  if (!resp.ok) {
226
241
  if (resp.status === 404) {
227
242
  endBridgeSpan == null ? void 0 : endBridgeSpan("auth.bridge.absent", {
@@ -243,10 +258,10 @@ function useParentBridge({
243
258
  }
244
259
  scheduleRetry();
245
260
  }
246
- return;
261
+ return null;
247
262
  }
248
263
  const data = await resp.json();
249
- if (cancelled) return;
264
+ if (cancelled) return null;
250
265
  if (endBridgeSpan) {
251
266
  endBridgeSpan("auth.bridge.ready", {
252
267
  data: { mode: "marathon", has_token: typeof data.token === "string" }
@@ -261,13 +276,16 @@ function useParentBridge({
261
276
  data: { source: "marathon", expires_at: data.expires_at }
262
277
  });
263
278
  }
279
+ const token = typeof data.token === "string" ? data.token : null;
264
280
  setState({
265
- token: typeof data.token === "string" ? data.token : null,
281
+ token,
266
282
  ...readAuthMintUrls(data),
267
283
  hostAnswered: true,
268
284
  ready: true
269
285
  });
286
+ tokenRef.current = token;
270
287
  scheduleRefresh(data.expires_at);
288
+ return token;
271
289
  } catch (error) {
272
290
  if (!cancelled && !(error instanceof DOMException && error.name === "AbortError")) {
273
291
  const failure = { level: "warn", data: { mode: "marathon" }, error };
@@ -283,19 +301,38 @@ function useParentBridge({
283
301
  if (!cancelled) {
284
302
  scheduleRetry();
285
303
  }
304
+ return null;
286
305
  } finally {
287
- clearRequestTimer();
306
+ clearTimeout(requestTimer);
307
+ if (controller === requestController) controller = null;
288
308
  }
289
309
  };
310
+ const fetchAuth = (options) => {
311
+ inFlight ?? (inFlight = mintAuth(options).finally(() => {
312
+ inFlight = null;
313
+ }));
314
+ return inFlight;
315
+ };
316
+ renewerRef.current = ({ force }) => {
317
+ if (!force) return Promise.resolve(tokenRef.current);
318
+ return fetchAuth({ markReadyOnFailure: false }).then((token) => token ?? tokenRef.current);
319
+ };
290
320
  void fetchAuth({ markReadyOnFailure: true });
291
321
  return () => {
292
322
  cancelled = true;
293
323
  controller == null ? void 0 : controller.abort();
294
- clearRequestTimer();
295
324
  clearRefreshTimer();
325
+ renewerRef.current = null;
296
326
  };
297
327
  }, [isInIframe]);
298
- return state;
328
+ const renewToken = react.useCallback(
329
+ (options) => {
330
+ var _a;
331
+ return ((_a = renewerRef.current) == null ? void 0 : _a.call(renewerRef, options)) ?? Promise.resolve(tokenRef.current);
332
+ },
333
+ []
334
+ );
335
+ return react.useMemo(() => ({ ...state, renewToken }), [state, renewToken]);
299
336
  }
300
337
  function useParentAuth() {
301
338
  return useParentBridge().token;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.cjs","sources":["../../src/runtime/auth.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\n\n/**\n * Check whether a PostMessage origin is trusted.\n * Accepts:\n * - explicit trusted origins passed by the host app\n * - the current iframe origin (for same-origin wrappers like Marathon)\n * - any *.athenaintel.com subdomain and localhost (legacy/default behavior)\n */\nfunction isTrustedOrigin({\n origin,\n trustedOrigins,\n}: {\n origin: string;\n trustedOrigins: string[];\n}): boolean {\n try {\n const normalizedOrigin = new URL(origin).origin;\n if (trustedOrigins.includes(normalizedOrigin)) {\n return true;\n }\n\n const { hostname } = new URL(normalizedOrigin);\n return (\n hostname === 'athenaintel.com' ||\n hostname.endsWith('.athenaintel.com') ||\n hostname === 'localhost'\n );\n } catch {\n return false;\n }\n}\n\nexport interface ParentBridgeState {\n /** PropelAuth access token from the parent window. */\n token: string | null;\n /**\n * Statewire sync mount (`…/v2/threads`) minted by the host. `null` when the\n * host minted none; the provider then derives it from the configured\n * `apiUrl`'s origin (or the environment preset).\n */\n statewireSyncUrl: string | null;\n /**\n * Bare Agora API base (no path) minted by the host. `null` when the host\n * minted none; the provider then uses the configured `backendUrl` (or the\n * environment preset).\n */\n backendBaseUrl: string | null;\n /** Athena frontend origin provided by the parent wrapper. */\n appUrl: string | null;\n /**\n * True once a host answered the bridge — a `/_athena/auth` mint or an\n * `athena-config` message — whether or not it minted the statewire fields.\n * Lets the provider tell an outdated host (answered, no mount) from no host\n * at all (the environment preset applies).\n */\n hostAnswered: boolean;\n /** True once config has been received from the parent (or timeout fired). */\n ready: boolean;\n}\n\nconst BRIDGE_URL_FIELDS = ['statewireSyncUrl', 'backendBaseUrl', 'appUrl'] as const;\n\ntype BridgeUrlField = (typeof BRIDGE_URL_FIELDS)[number];\n\n/** The URL half of {@link ParentBridgeState} — what a host can hand the SDK. */\nexport type ParentBridgeUrls = Pick<ParentBridgeState, BridgeUrlField>;\n\nconst EMPTY_BRIDGE_URLS: ParentBridgeUrls = {\n statewireSyncUrl: null,\n backendBaseUrl: null,\n appUrl: null,\n};\n\n/**\n * Fold an `athena-config` postMessage payload over the current URL set. A\n * field is taken only when it is a string; anything else keeps the previous\n * value. Keys outside {@link BridgeUrlField} (a legacy parent's `apiUrl` /\n * `backendUrl` among them) are dropped.\n */\nexport function mergeBridgeConfigMessage(\n previous: ParentBridgeUrls,\n payload: Partial<Record<BridgeUrlField, unknown>>,\n): ParentBridgeUrls {\n const next: ParentBridgeUrls = {\n statewireSyncUrl: previous.statewireSyncUrl,\n backendBaseUrl: previous.backendBaseUrl,\n appUrl: previous.appUrl,\n };\n for (const field of BRIDGE_URL_FIELDS) {\n const value = payload[field];\n if (typeof value === 'string') {\n next[field] = value;\n }\n }\n return next;\n}\n\n/**\n * Read the URL set out of a `/_athena/auth` mint body. Non-string fields are\n * `null`.\n */\nexport function readAuthMintUrls(data: Partial<Record<BridgeUrlField, unknown>>): ParentBridgeUrls {\n return mergeBridgeConfigMessage(EMPTY_BRIDGE_URLS, data);\n}\n\nexport interface ParentBridgeOptions {\n /**\n * Exact parent origins that are allowed to send auth/config via postMessage.\n * Useful for private-cloud or custom-domain deployments.\n */\n trustedOrigins?: string[];\n}\n\n/** How long to wait for the parent bridge before falling back to defaults. */\nconst BRIDGE_TIMEOUT_MS = 2000;\nconst TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000;\nconst TOKEN_REFRESH_RETRY_BASE_MS = 60 * 1000;\nconst TOKEN_REFRESH_RETRY_MAX_MS = 5 * 60 * 1000;\nconst DEFAULT_TOKEN_REFRESH_MS = 55 * 60 * 1000;\nconst EMPTY_TRUSTED_ORIGINS: string[] = [];\n\n/** Untrusted shape of the `/_athena/auth` mint body; every field is type-guarded on read. */\ninterface AthenaAuthResponse {\n token?: unknown;\n statewireSyncUrl?: unknown;\n backendBaseUrl?: unknown;\n appUrl?: unknown;\n expires_at?: unknown;\n}\n\nconst normalizeOrigin = (value: string): string | null => {\n try {\n return new URL(value).origin;\n } catch {\n return null;\n }\n};\n\nexport const getTokenRefreshDelay = (expiresAt: unknown, nowMs: number): number => {\n if (typeof expiresAt !== 'string') {\n return DEFAULT_TOKEN_REFRESH_MS;\n }\n\n const expiresAtMs = Date.parse(expiresAt);\n if (!Number.isFinite(expiresAtMs)) {\n return DEFAULT_TOKEN_REFRESH_MS;\n }\n\n return Math.max(TOKEN_REFRESH_RETRY_BASE_MS, expiresAtMs - nowMs - TOKEN_REFRESH_BUFFER_MS);\n};\n\nexport const getAuthRetryDelay = (retryAttempt: number): number => {\n const safeAttempt = Number.isFinite(retryAttempt) ? Math.max(0, Math.floor(retryAttempt)) : 0;\n return Math.min(TOKEN_REFRESH_RETRY_MAX_MS, TOKEN_REFRESH_RETRY_BASE_MS * 2 ** safeAttempt);\n};\n\n/**\n * Listens for auth tokens AND environment config from the host environment.\n *\n * Two transport modes, picked automatically:\n *\n * 1. **Iframe (legacy / Olympus inline preview)**: a parent window posts\n * `athena-config` and `athena-auth` via PostMessage. The hook also posts\n * `athena-auth-ready` so the parent knows when to (re-)send.\n *\n * 2. **Standalone (Marathon-served deploy URL)**: top-level navigation, no\n * parent. The hook fetches `/_athena/auth` on its own origin. Marathon\n * validates its session cookie (sent by the browser automatically) and\n * returns `{ token, statewireSyncUrl, backendBaseUrl, appUrl }` — the\n * statewire mount, the bare Agora base and the app origin. A 404 means\n * we're not on a Marathon origin — the hook falls through with empty\n * state.\n *\n * In both modes a timeout safety net flips `ready: true` so the app never\n * blocks indefinitely.\n */\nexport function useParentBridge({\n trustedOrigins = EMPTY_TRUSTED_ORIGINS,\n}: ParentBridgeOptions = {}): ParentBridgeState {\n const isInIframe = typeof window !== 'undefined' && window.parent !== window;\n const runtimeTrustedOrigins = useMemo(() => {\n const origins = new Set<string>();\n\n if (typeof window !== 'undefined') {\n origins.add(window.location.origin);\n }\n\n for (const trustedOrigin of trustedOrigins) {\n const normalizedOrigin = normalizeOrigin(trustedOrigin);\n if (normalizedOrigin) {\n origins.add(normalizedOrigin);\n }\n }\n\n return [...origins];\n }, [trustedOrigins]);\n\n const [state, setState] = useState<ParentBridgeState>({\n token: null,\n ...EMPTY_BRIDGE_URLS,\n hostAnswered: false,\n ready: false,\n });\n const readySignalSent = useRef(false);\n const configReceived = useRef(false);\n\n useEffect(() => {\n if (!isInIframe) return;\n\n const endBridgeSpan = athenaDiagnostics.span('auth.bridge', 'auth.bridge.start', {\n mode: 'iframe',\n });\n let spanEnded = false;\n const endOnce: typeof endBridgeSpan = (name, options) => {\n if (spanEnded) return 0;\n spanEnded = true;\n return endBridgeSpan(name, options);\n };\n\n const handler = (event: MessageEvent) => {\n if (!isTrustedOrigin({ origin: event.origin, trustedOrigins: runtimeTrustedOrigins })) {\n return;\n }\n if (!event.data || typeof event.data !== 'object') return;\n\n if (event.data.type === 'athena-config') {\n configReceived.current = true;\n setState((prev) => ({\n ...prev,\n ...mergeBridgeConfigMessage(prev, event.data),\n hostAnswered: true,\n // Don't set ready here — wait for athena-auth (token) or the timeout.\n // Setting ready on config alone causes a race: the thread list fires\n // before the token arrives, falling back to X-API-KEY.\n }));\n }\n\n if (event.data.type === 'athena-auth' && typeof event.data.token === 'string') {\n endOnce('auth.bridge.ready', { data: { mode: 'iframe' } });\n athenaDiagnostics.emit('auth.token.acquired', {\n level: 'debug',\n data: { source: 'iframe-postmessage' },\n });\n setState((prev) => ({\n ...prev,\n token: event.data.token,\n ready: true,\n }));\n }\n };\n window.addEventListener('message', handler);\n\n if (!readySignalSent.current) {\n window.parent.postMessage({ type: 'athena-auth-ready' }, '*');\n readySignalSent.current = true;\n }\n\n const timer = setTimeout(() => {\n endOnce('auth.bridge.timeout', {\n data: {\n mode: 'iframe',\n timeout_ms: BRIDGE_TIMEOUT_MS,\n config_received: configReceived.current,\n },\n });\n setState((prev) => (prev.ready ? prev : { ...prev, ready: true }));\n }, BRIDGE_TIMEOUT_MS);\n\n return () => {\n window.removeEventListener('message', handler);\n clearTimeout(timer);\n };\n }, [isInIframe, runtimeTrustedOrigins]);\n\n useEffect(() => {\n if (isInIframe) return;\n if (typeof window === 'undefined') return;\n\n let cancelled = false;\n let requestTimer: ReturnType<typeof setTimeout> | null = null;\n let refreshTimer: ReturnType<typeof setTimeout> | null = null;\n let controller: AbortController | null = null;\n let retryAttempt = 0;\n\n const clearRequestTimer = () => {\n if (requestTimer) {\n clearTimeout(requestTimer);\n requestTimer = null;\n }\n };\n\n const clearRefreshTimer = () => {\n if (refreshTimer) {\n clearTimeout(refreshTimer);\n refreshTimer = null;\n }\n };\n\n const markReady = () => {\n if (cancelled) return;\n setState((prev) => (prev.ready ? prev : { ...prev, ready: true }));\n };\n\n const scheduleRefresh = (expiresAt: unknown) => {\n clearRefreshTimer();\n retryAttempt = 0;\n const delay = getTokenRefreshDelay(expiresAt, Date.now());\n refreshTimer = setTimeout(() => {\n void fetchAuth({ markReadyOnFailure: false });\n }, delay);\n };\n\n const scheduleRetry = () => {\n clearRefreshTimer();\n const delay = getAuthRetryDelay(retryAttempt);\n retryAttempt += 1;\n refreshTimer = setTimeout(() => {\n void fetchAuth({ markReadyOnFailure: false });\n }, delay);\n };\n\n const fetchAuth = async ({\n markReadyOnFailure,\n }: {\n markReadyOnFailure: boolean;\n }) => {\n controller?.abort();\n controller = new AbortController();\n\n // `markReadyOnFailure` distinguishes the initial acquisition (a span the\n // provider blocks on) from a background refresh. Idempotent: the abort\n // timer and the catch path can both reach an end call.\n const isInitial = markReadyOnFailure;\n const rawEndSpan = isInitial\n ? athenaDiagnostics.span('auth.bridge', 'auth.bridge.start', { mode: 'marathon' })\n : null;\n let spanEnded = false;\n const endBridgeSpan: typeof rawEndSpan = rawEndSpan\n ? (name, options) => {\n if (spanEnded) return 0;\n spanEnded = true;\n return rawEndSpan(name, options);\n }\n : null;\n\n clearRequestTimer();\n requestTimer = setTimeout(() => {\n controller?.abort();\n endBridgeSpan?.('auth.bridge.timeout', {\n data: { mode: 'marathon', timeout_ms: BRIDGE_TIMEOUT_MS },\n });\n if (markReadyOnFailure) {\n markReady();\n }\n }, BRIDGE_TIMEOUT_MS);\n\n try {\n const resp = await fetch('/_athena/auth', {\n credentials: 'include',\n headers: { Accept: 'application/json' },\n signal: controller.signal,\n });\n if (cancelled) return;\n\n if (!resp.ok) {\n if (resp.status === 404) {\n // Not a Marathon origin — expected for standalone/local dev.\n endBridgeSpan?.('auth.bridge.absent', {\n data: { mode: 'marathon', status: resp.status },\n });\n markReady();\n } else {\n const failure = {\n level: 'warn' as const,\n data: { mode: 'marathon', status: resp.status },\n };\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.error', failure);\n } else {\n athenaDiagnostics.emit('auth.token.refresh_failed', failure);\n }\n if (markReadyOnFailure) {\n markReady();\n }\n scheduleRetry();\n }\n return;\n }\n\n const data = (await resp.json()) as AthenaAuthResponse;\n if (cancelled) return;\n\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.ready', {\n data: { mode: 'marathon', has_token: typeof data.token === 'string' },\n });\n athenaDiagnostics.emit('auth.token.acquired', {\n level: 'debug',\n data: { source: 'marathon', expires_at: data.expires_at },\n });\n } else {\n athenaDiagnostics.emit('auth.token.refreshed', {\n level: 'debug',\n data: { source: 'marathon', expires_at: data.expires_at },\n });\n }\n\n setState({\n token: typeof data.token === 'string' ? data.token : null,\n ...readAuthMintUrls(data),\n hostAnswered: true,\n ready: true,\n });\n scheduleRefresh(data.expires_at);\n } catch (error) {\n if (!cancelled && !(error instanceof DOMException && error.name === 'AbortError')) {\n const failure = { level: 'warn' as const, data: { mode: 'marathon' }, error };\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.error', failure);\n } else {\n athenaDiagnostics.emit('auth.token.refresh_failed', failure);\n }\n }\n if (markReadyOnFailure) {\n markReady();\n }\n if (!cancelled) {\n scheduleRetry();\n }\n } finally {\n clearRequestTimer();\n }\n };\n\n void fetchAuth({ markReadyOnFailure: true });\n\n return () => {\n cancelled = true;\n controller?.abort();\n clearRequestTimer();\n clearRefreshTimer();\n };\n }, [isInIframe]);\n\n return state;\n}\n\n/**\n * Legacy hook — returns just the auth token from the parent bridge.\n * Prefer useParentBridge() for new code.\n */\nexport function useParentAuth(): string | null {\n return useParentBridge().token;\n}\n"],"names":["useMemo","useState","useRef","useEffect","athenaDiagnostics"],"mappings":"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"}
1
+ {"version":3,"file":"auth.cjs","sources":["../../src/runtime/auth.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { AthenaTokenRenewer } from '../auth/credential-source';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\n\n/**\n * Check whether a PostMessage origin is trusted.\n * Accepts:\n * - explicit trusted origins passed by the host app\n * - the current iframe origin (for same-origin wrappers like Marathon)\n * - any *.athenaintel.com subdomain and localhost (legacy/default behavior)\n */\nfunction isTrustedOrigin({\n origin,\n trustedOrigins,\n}: {\n origin: string;\n trustedOrigins: string[];\n}): boolean {\n try {\n const normalizedOrigin = new URL(origin).origin;\n if (trustedOrigins.includes(normalizedOrigin)) {\n return true;\n }\n\n const { hostname } = new URL(normalizedOrigin);\n return (\n hostname === 'athenaintel.com' ||\n hostname.endsWith('.athenaintel.com') ||\n hostname === 'localhost'\n );\n } catch {\n return false;\n }\n}\n\nexport interface ParentBridgeState {\n /** PropelAuth access token from the parent window. */\n token: string | null;\n /**\n * Statewire sync mount (`…/v2/threads`) minted by the host. `null` when the\n * host minted none; the provider then derives it from the configured\n * `apiUrl`'s origin (or the environment preset).\n */\n statewireSyncUrl: string | null;\n /**\n * Bare Agora API base (no path) minted by the host. `null` when the host\n * minted none; the provider then uses the configured `backendUrl` (or the\n * environment preset).\n */\n backendBaseUrl: string | null;\n /** Athena frontend origin provided by the parent wrapper. */\n appUrl: string | null;\n /**\n * True once a host answered the bridge — a `/_athena/auth` mint or an\n * `athena-config` message — whether or not it minted the statewire fields.\n * Lets the provider tell an outdated host (answered, no mount) from no host\n * at all (the environment preset applies).\n */\n hostAnswered: boolean;\n /** True once config has been received from the parent (or timeout fired). */\n ready: boolean;\n}\n\n/**\n * {@link ParentBridgeState} plus the bridge's token renewal: `force` (the\n * token was refused) asks the host for a new one — a fresh `/_athena/auth`\n * mint on a Marathon origin, or an `athena-auth-ready` request answered by the\n * parent window's next `athena-auth` — and resolves the held token otherwise.\n */\nexport type ParentBridge = ParentBridgeState & { renewToken: AthenaTokenRenewer };\n\nconst BRIDGE_URL_FIELDS = ['statewireSyncUrl', 'backendBaseUrl', 'appUrl'] as const;\n\ntype BridgeUrlField = (typeof BRIDGE_URL_FIELDS)[number];\n\n/** The URL half of {@link ParentBridgeState} — what a host can hand the SDK. */\nexport type ParentBridgeUrls = Pick<ParentBridgeState, BridgeUrlField>;\n\nconst EMPTY_BRIDGE_URLS: ParentBridgeUrls = {\n statewireSyncUrl: null,\n backendBaseUrl: null,\n appUrl: null,\n};\n\n/**\n * Fold an `athena-config` postMessage payload over the current URL set. A\n * field is taken only when it is a string; anything else keeps the previous\n * value. Keys outside {@link BridgeUrlField} (a legacy parent's `apiUrl` /\n * `backendUrl` among them) are dropped.\n */\nexport function mergeBridgeConfigMessage(\n previous: ParentBridgeUrls,\n payload: Partial<Record<BridgeUrlField, unknown>>,\n): ParentBridgeUrls {\n const next: ParentBridgeUrls = {\n statewireSyncUrl: previous.statewireSyncUrl,\n backendBaseUrl: previous.backendBaseUrl,\n appUrl: previous.appUrl,\n };\n for (const field of BRIDGE_URL_FIELDS) {\n const value = payload[field];\n if (typeof value === 'string') {\n next[field] = value;\n }\n }\n return next;\n}\n\n/**\n * Read the URL set out of a `/_athena/auth` mint body. Non-string fields are\n * `null`.\n */\nexport function readAuthMintUrls(data: Partial<Record<BridgeUrlField, unknown>>): ParentBridgeUrls {\n return mergeBridgeConfigMessage(EMPTY_BRIDGE_URLS, data);\n}\n\nexport interface ParentBridgeOptions {\n /**\n * Exact parent origins that are allowed to send auth/config via postMessage.\n * Useful for private-cloud or custom-domain deployments.\n */\n trustedOrigins?: string[];\n}\n\n/** How long to wait for the parent bridge before falling back to defaults. */\nconst BRIDGE_TIMEOUT_MS = 2000;\n/** How long a renewal waits for the parent window to answer `athena-auth-ready`. */\nconst PARENT_TOKEN_RENEWAL_TIMEOUT_MS = 5000;\nconst TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000;\nconst TOKEN_REFRESH_RETRY_BASE_MS = 60 * 1000;\nconst TOKEN_REFRESH_RETRY_MAX_MS = 5 * 60 * 1000;\nconst DEFAULT_TOKEN_REFRESH_MS = 55 * 60 * 1000;\nconst EMPTY_TRUSTED_ORIGINS: string[] = [];\n\n/** Untrusted shape of the `/_athena/auth` mint body; every field is type-guarded on read. */\ninterface AthenaAuthResponse {\n token?: unknown;\n statewireSyncUrl?: unknown;\n backendBaseUrl?: unknown;\n appUrl?: unknown;\n expires_at?: unknown;\n}\n\nconst normalizeOrigin = (value: string): string | null => {\n try {\n return new URL(value).origin;\n } catch {\n return null;\n }\n};\n\nexport const getTokenRefreshDelay = (expiresAt: unknown, nowMs: number): number => {\n if (typeof expiresAt !== 'string') {\n return DEFAULT_TOKEN_REFRESH_MS;\n }\n\n const expiresAtMs = Date.parse(expiresAt);\n if (!Number.isFinite(expiresAtMs)) {\n return DEFAULT_TOKEN_REFRESH_MS;\n }\n\n return Math.max(TOKEN_REFRESH_RETRY_BASE_MS, expiresAtMs - nowMs - TOKEN_REFRESH_BUFFER_MS);\n};\n\nexport const getAuthRetryDelay = (retryAttempt: number): number => {\n const safeAttempt = Number.isFinite(retryAttempt) ? Math.max(0, Math.floor(retryAttempt)) : 0;\n return Math.min(TOKEN_REFRESH_RETRY_MAX_MS, TOKEN_REFRESH_RETRY_BASE_MS * 2 ** safeAttempt);\n};\n\n/**\n * Listens for auth tokens AND environment config from the host environment.\n *\n * Two transport modes, picked automatically:\n *\n * 1. **Iframe (legacy / Olympus inline preview)**: a parent window posts\n * `athena-config` and `athena-auth` via PostMessage. The hook also posts\n * `athena-auth-ready` so the parent knows when to (re-)send.\n *\n * 2. **Standalone (Marathon-served deploy URL)**: top-level navigation, no\n * parent. The hook fetches `/_athena/auth` on its own origin. Marathon\n * validates its session cookie (sent by the browser automatically) and\n * returns `{ token, statewireSyncUrl, backendBaseUrl, appUrl }` — the\n * statewire mount, the bare Agora base and the app origin. A 404 means\n * we're not on a Marathon origin — the hook falls through with empty\n * state.\n *\n * In both modes a timeout safety net flips `ready: true` so the app never\n * blocks indefinitely.\n */\nexport function useParentBridge({\n trustedOrigins = EMPTY_TRUSTED_ORIGINS,\n}: ParentBridgeOptions = {}): ParentBridge {\n const isInIframe = typeof window !== 'undefined' && window.parent !== window;\n const runtimeTrustedOrigins = useMemo(() => {\n const origins = new Set<string>();\n\n if (typeof window !== 'undefined') {\n origins.add(window.location.origin);\n }\n\n for (const trustedOrigin of trustedOrigins) {\n const normalizedOrigin = normalizeOrigin(trustedOrigin);\n if (normalizedOrigin) {\n origins.add(normalizedOrigin);\n }\n }\n\n return [...origins];\n }, [trustedOrigins]);\n\n const [state, setState] = useState<ParentBridgeState>({\n token: null,\n ...EMPTY_BRIDGE_URLS,\n hostAnswered: false,\n ready: false,\n });\n const readySignalSent = useRef(false);\n const configReceived = useRef(false);\n const tokenRef = useRef<string | null>(state.token);\n tokenRef.current = state.token;\n // Installed by whichever transport mode is active (iframe or Marathon).\n const renewerRef = useRef<AthenaTokenRenewer | null>(null);\n\n useEffect(() => {\n if (!isInIframe) return;\n\n const endBridgeSpan = athenaDiagnostics.span('auth.bridge', 'auth.bridge.start', {\n mode: 'iframe',\n });\n let spanEnded = false;\n const endOnce: typeof endBridgeSpan = (name, options) => {\n if (spanEnded) return 0;\n spanEnded = true;\n return endBridgeSpan(name, options);\n };\n\n // Renewals waiting for the parent's next `athena-auth`.\n const tokenWaiters = new Set<(token: string | null) => void>();\n\n const handler = (event: MessageEvent) => {\n if (!isTrustedOrigin({ origin: event.origin, trustedOrigins: runtimeTrustedOrigins })) {\n return;\n }\n if (!event.data || typeof event.data !== 'object') return;\n\n if (event.data.type === 'athena-config') {\n configReceived.current = true;\n setState((prev) => ({\n ...prev,\n ...mergeBridgeConfigMessage(prev, event.data),\n hostAnswered: true,\n // Don't set ready here — wait for athena-auth (token) or the timeout.\n // Setting ready on config alone causes a race: the thread list fires\n // before the token arrives, falling back to X-API-KEY.\n }));\n }\n\n if (event.data.type === 'athena-auth' && typeof event.data.token === 'string') {\n endOnce('auth.bridge.ready', { data: { mode: 'iframe' } });\n athenaDiagnostics.emit('auth.token.acquired', {\n level: 'debug',\n data: { source: 'iframe-postmessage' },\n });\n setState((prev) => ({\n ...prev,\n token: event.data.token,\n ready: true,\n }));\n tokenRef.current = event.data.token;\n for (const resolve of [...tokenWaiters]) resolve(event.data.token);\n }\n };\n window.addEventListener('message', handler);\n\n // A renewal asks the parent to send its current token again — the same\n // `athena-auth-ready` handshake a parent already answers — and settles on\n // the next `athena-auth`, or on the held token when none arrives in time.\n renewerRef.current = ({ force }) => {\n if (!force) return Promise.resolve(tokenRef.current);\n return new Promise<string | null>((resolve) => {\n const settle = (token: string | null) => {\n clearTimeout(timer);\n tokenWaiters.delete(settle);\n resolve(token ?? tokenRef.current);\n };\n const timer = setTimeout(() => settle(null), PARENT_TOKEN_RENEWAL_TIMEOUT_MS);\n tokenWaiters.add(settle);\n window.parent.postMessage({ type: 'athena-auth-ready' }, '*');\n });\n };\n\n if (!readySignalSent.current) {\n window.parent.postMessage({ type: 'athena-auth-ready' }, '*');\n readySignalSent.current = true;\n }\n\n const timer = setTimeout(() => {\n endOnce('auth.bridge.timeout', {\n data: {\n mode: 'iframe',\n timeout_ms: BRIDGE_TIMEOUT_MS,\n config_received: configReceived.current,\n },\n });\n setState((prev) => (prev.ready ? prev : { ...prev, ready: true }));\n }, BRIDGE_TIMEOUT_MS);\n\n return () => {\n window.removeEventListener('message', handler);\n clearTimeout(timer);\n renewerRef.current = null;\n for (const resolve of [...tokenWaiters]) resolve(null);\n };\n }, [isInIframe, runtimeTrustedOrigins]);\n\n useEffect(() => {\n if (isInIframe) return;\n if (typeof window === 'undefined') return;\n\n let cancelled = false;\n let refreshTimer: ReturnType<typeof setTimeout> | null = null;\n let controller: AbortController | null = null;\n let inFlight: Promise<string | null> | null = null;\n let retryAttempt = 0;\n\n const clearRefreshTimer = () => {\n if (refreshTimer) {\n clearTimeout(refreshTimer);\n refreshTimer = null;\n }\n };\n\n const markReady = () => {\n if (cancelled) return;\n setState((prev) => (prev.ready ? prev : { ...prev, ready: true }));\n };\n\n const scheduleRefresh = (expiresAt: unknown) => {\n clearRefreshTimer();\n retryAttempt = 0;\n const delay = getTokenRefreshDelay(expiresAt, Date.now());\n refreshTimer = setTimeout(() => {\n void fetchAuth({ markReadyOnFailure: false });\n }, delay);\n };\n\n const scheduleRetry = () => {\n clearRefreshTimer();\n const delay = getAuthRetryDelay(retryAttempt);\n retryAttempt += 1;\n refreshTimer = setTimeout(() => {\n void fetchAuth({ markReadyOnFailure: false });\n }, delay);\n };\n\n const mintAuth = async ({\n markReadyOnFailure,\n }: {\n markReadyOnFailure: boolean;\n }): Promise<string | null> => {\n const requestController = new AbortController();\n controller = requestController;\n\n // `markReadyOnFailure` distinguishes the initial acquisition (a span the\n // provider blocks on) from a background refresh. Idempotent: the abort\n // timer and the catch path can both reach an end call.\n const isInitial = markReadyOnFailure;\n const rawEndSpan = isInitial\n ? athenaDiagnostics.span('auth.bridge', 'auth.bridge.start', { mode: 'marathon' })\n : null;\n let spanEnded = false;\n const endBridgeSpan: typeof rawEndSpan = rawEndSpan\n ? (name, options) => {\n if (spanEnded) return 0;\n spanEnded = true;\n return rawEndSpan(name, options);\n }\n : null;\n\n const requestTimer = setTimeout(() => {\n requestController.abort();\n endBridgeSpan?.('auth.bridge.timeout', {\n data: { mode: 'marathon', timeout_ms: BRIDGE_TIMEOUT_MS },\n });\n if (markReadyOnFailure) {\n markReady();\n }\n }, BRIDGE_TIMEOUT_MS);\n\n try {\n const resp = await fetch('/_athena/auth', {\n credentials: 'include',\n headers: { Accept: 'application/json' },\n signal: requestController.signal,\n });\n if (cancelled) return null;\n\n if (!resp.ok) {\n if (resp.status === 404) {\n // Not a Marathon origin — expected for standalone/local dev.\n endBridgeSpan?.('auth.bridge.absent', {\n data: { mode: 'marathon', status: resp.status },\n });\n markReady();\n } else {\n const failure = {\n level: 'warn' as const,\n data: { mode: 'marathon', status: resp.status },\n };\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.error', failure);\n } else {\n athenaDiagnostics.emit('auth.token.refresh_failed', failure);\n }\n if (markReadyOnFailure) {\n markReady();\n }\n scheduleRetry();\n }\n return null;\n }\n\n const data = (await resp.json()) as AthenaAuthResponse;\n if (cancelled) return null;\n\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.ready', {\n data: { mode: 'marathon', has_token: typeof data.token === 'string' },\n });\n athenaDiagnostics.emit('auth.token.acquired', {\n level: 'debug',\n data: { source: 'marathon', expires_at: data.expires_at },\n });\n } else {\n athenaDiagnostics.emit('auth.token.refreshed', {\n level: 'debug',\n data: { source: 'marathon', expires_at: data.expires_at },\n });\n }\n\n const token = typeof data.token === 'string' ? data.token : null;\n setState({\n token,\n ...readAuthMintUrls(data),\n hostAnswered: true,\n ready: true,\n });\n tokenRef.current = token;\n scheduleRefresh(data.expires_at);\n return token;\n } catch (error) {\n if (!cancelled && !(error instanceof DOMException && error.name === 'AbortError')) {\n const failure = { level: 'warn' as const, data: { mode: 'marathon' }, error };\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.error', failure);\n } else {\n athenaDiagnostics.emit('auth.token.refresh_failed', failure);\n }\n }\n if (markReadyOnFailure) {\n markReady();\n }\n if (!cancelled) {\n scheduleRetry();\n }\n return null;\n } finally {\n clearTimeout(requestTimer);\n if (controller === requestController) controller = null;\n }\n };\n\n // One mint at a time. The initial acquisition, a scheduled refresh, a\n // retry and a forced renewal that overlap share the request in flight\n // instead of aborting it, so each request keeps its own timeout and the\n // schedule never cancels a renewal a connection is waiting on.\n const fetchAuth = (options: { markReadyOnFailure: boolean }): Promise<string | null> => {\n inFlight ??= mintAuth(options).finally(() => {\n inFlight = null;\n });\n return inFlight;\n };\n\n // A forced renewal mints now instead of waiting for the scheduled refresh\n // (joining a mint already in flight); the schedule restarts from the new\n // token's expiry.\n renewerRef.current = ({ force }) => {\n if (!force) return Promise.resolve(tokenRef.current);\n return fetchAuth({ markReadyOnFailure: false }).then((token) => token ?? tokenRef.current);\n };\n\n void fetchAuth({ markReadyOnFailure: true });\n\n return () => {\n cancelled = true;\n controller?.abort();\n clearRefreshTimer();\n renewerRef.current = null;\n };\n }, [isInIframe]);\n\n const renewToken = useCallback<AthenaTokenRenewer>(\n (options) => renewerRef.current?.(options) ?? Promise.resolve(tokenRef.current),\n [],\n );\n\n return useMemo(() => ({ ...state, renewToken }), [state, renewToken]);\n}\n\n/**\n * Legacy hook — returns just the auth token from the parent bridge.\n * Prefer useParentBridge() for new code.\n */\nexport function useParentAuth(): string | null {\n return useParentBridge().token;\n}\n"],"names":["useMemo","useState","useRef","useEffect","athenaDiagnostics","timer","useCallback"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { AthenaTokenRenewer } from '../auth/credential-source';
1
2
  export interface ParentBridgeState {
2
3
  /** PropelAuth access token from the parent window. */
3
4
  token: string | null;
@@ -25,6 +26,15 @@ export interface ParentBridgeState {
25
26
  /** True once config has been received from the parent (or timeout fired). */
26
27
  ready: boolean;
27
28
  }
29
+ /**
30
+ * {@link ParentBridgeState} plus the bridge's token renewal: `force` (the
31
+ * token was refused) asks the host for a new one — a fresh `/_athena/auth`
32
+ * mint on a Marathon origin, or an `athena-auth-ready` request answered by the
33
+ * parent window's next `athena-auth` — and resolves the held token otherwise.
34
+ */
35
+ export type ParentBridge = ParentBridgeState & {
36
+ renewToken: AthenaTokenRenewer;
37
+ };
28
38
  declare const BRIDGE_URL_FIELDS: readonly ['statewireSyncUrl', 'backendBaseUrl', 'appUrl'];
29
39
  type BridgeUrlField = (typeof BRIDGE_URL_FIELDS)[number];
30
40
  /** The URL half of {@link ParentBridgeState} — what a host can hand the SDK. */
@@ -70,7 +80,7 @@ export declare const getAuthRetryDelay: (retryAttempt: number) => number;
70
80
  * In both modes a timeout safety net flips `ready: true` so the app never
71
81
  * blocks indefinitely.
72
82
  */
73
- export declare function useParentBridge({ trustedOrigins, }?: ParentBridgeOptions): ParentBridgeState;
83
+ export declare function useParentBridge({ trustedOrigins, }?: ParentBridgeOptions): ParentBridge;
74
84
  /**
75
85
  * Legacy hook — returns just the auth token from the parent bridge.
76
86
  * Prefer useParentBridge() for new code.
@@ -1,4 +1,4 @@
1
- import { useMemo, useState, useRef, useEffect } from "react";
1
+ import { useMemo, useState, useRef, useEffect, useCallback } from "react";
2
2
  import { athenaDiagnostics } from "../diagnostics/athena-diagnostics.js";
3
3
  function isTrustedOrigin({
4
4
  origin,
@@ -39,6 +39,7 @@ function readAuthMintUrls(data) {
39
39
  return mergeBridgeConfigMessage(EMPTY_BRIDGE_URLS, data);
40
40
  }
41
41
  const BRIDGE_TIMEOUT_MS = 2e3;
42
+ const PARENT_TOKEN_RENEWAL_TIMEOUT_MS = 5e3;
42
43
  const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1e3;
43
44
  const TOKEN_REFRESH_RETRY_BASE_MS = 60 * 1e3;
44
45
  const TOKEN_REFRESH_RETRY_MAX_MS = 5 * 60 * 1e3;
@@ -90,6 +91,9 @@ function useParentBridge({
90
91
  });
91
92
  const readySignalSent = useRef(false);
92
93
  const configReceived = useRef(false);
94
+ const tokenRef = useRef(state.token);
95
+ tokenRef.current = state.token;
96
+ const renewerRef = useRef(null);
93
97
  useEffect(() => {
94
98
  if (!isInIframe) return;
95
99
  const endBridgeSpan = athenaDiagnostics.span("auth.bridge", "auth.bridge.start", {
@@ -101,6 +105,7 @@ function useParentBridge({
101
105
  spanEnded = true;
102
106
  return endBridgeSpan(name, options);
103
107
  };
108
+ const tokenWaiters = /* @__PURE__ */ new Set();
104
109
  const handler = (event) => {
105
110
  if (!isTrustedOrigin({ origin: event.origin, trustedOrigins: runtimeTrustedOrigins })) {
106
111
  return;
@@ -128,9 +133,24 @@ function useParentBridge({
128
133
  token: event.data.token,
129
134
  ready: true
130
135
  }));
136
+ tokenRef.current = event.data.token;
137
+ for (const resolve of [...tokenWaiters]) resolve(event.data.token);
131
138
  }
132
139
  };
133
140
  window.addEventListener("message", handler);
141
+ renewerRef.current = ({ force }) => {
142
+ if (!force) return Promise.resolve(tokenRef.current);
143
+ return new Promise((resolve) => {
144
+ const settle = (token) => {
145
+ clearTimeout(timer2);
146
+ tokenWaiters.delete(settle);
147
+ resolve(token ?? tokenRef.current);
148
+ };
149
+ const timer2 = setTimeout(() => settle(null), PARENT_TOKEN_RENEWAL_TIMEOUT_MS);
150
+ tokenWaiters.add(settle);
151
+ window.parent.postMessage({ type: "athena-auth-ready" }, "*");
152
+ });
153
+ };
134
154
  if (!readySignalSent.current) {
135
155
  window.parent.postMessage({ type: "athena-auth-ready" }, "*");
136
156
  readySignalSent.current = true;
@@ -148,22 +168,18 @@ function useParentBridge({
148
168
  return () => {
149
169
  window.removeEventListener("message", handler);
150
170
  clearTimeout(timer);
171
+ renewerRef.current = null;
172
+ for (const resolve of [...tokenWaiters]) resolve(null);
151
173
  };
152
174
  }, [isInIframe, runtimeTrustedOrigins]);
153
175
  useEffect(() => {
154
176
  if (isInIframe) return;
155
177
  if (typeof window === "undefined") return;
156
178
  let cancelled = false;
157
- let requestTimer = null;
158
179
  let refreshTimer = null;
159
180
  let controller = null;
181
+ let inFlight = null;
160
182
  let retryAttempt = 0;
161
- const clearRequestTimer = () => {
162
- if (requestTimer) {
163
- clearTimeout(requestTimer);
164
- requestTimer = null;
165
- }
166
- };
167
183
  const clearRefreshTimer = () => {
168
184
  if (refreshTimer) {
169
185
  clearTimeout(refreshTimer);
@@ -190,11 +206,11 @@ function useParentBridge({
190
206
  void fetchAuth({ markReadyOnFailure: false });
191
207
  }, delay);
192
208
  };
193
- const fetchAuth = async ({
209
+ const mintAuth = async ({
194
210
  markReadyOnFailure
195
211
  }) => {
196
- controller == null ? void 0 : controller.abort();
197
- controller = new AbortController();
212
+ const requestController = new AbortController();
213
+ controller = requestController;
198
214
  const isInitial = markReadyOnFailure;
199
215
  const rawEndSpan = isInitial ? athenaDiagnostics.span("auth.bridge", "auth.bridge.start", { mode: "marathon" }) : null;
200
216
  let spanEnded = false;
@@ -203,9 +219,8 @@ function useParentBridge({
203
219
  spanEnded = true;
204
220
  return rawEndSpan(name, options);
205
221
  } : null;
206
- clearRequestTimer();
207
- requestTimer = setTimeout(() => {
208
- controller == null ? void 0 : controller.abort();
222
+ const requestTimer = setTimeout(() => {
223
+ requestController.abort();
209
224
  endBridgeSpan == null ? void 0 : endBridgeSpan("auth.bridge.timeout", {
210
225
  data: { mode: "marathon", timeout_ms: BRIDGE_TIMEOUT_MS }
211
226
  });
@@ -217,9 +232,9 @@ function useParentBridge({
217
232
  const resp = await fetch("/_athena/auth", {
218
233
  credentials: "include",
219
234
  headers: { Accept: "application/json" },
220
- signal: controller.signal
235
+ signal: requestController.signal
221
236
  });
222
- if (cancelled) return;
237
+ if (cancelled) return null;
223
238
  if (!resp.ok) {
224
239
  if (resp.status === 404) {
225
240
  endBridgeSpan == null ? void 0 : endBridgeSpan("auth.bridge.absent", {
@@ -241,10 +256,10 @@ function useParentBridge({
241
256
  }
242
257
  scheduleRetry();
243
258
  }
244
- return;
259
+ return null;
245
260
  }
246
261
  const data = await resp.json();
247
- if (cancelled) return;
262
+ if (cancelled) return null;
248
263
  if (endBridgeSpan) {
249
264
  endBridgeSpan("auth.bridge.ready", {
250
265
  data: { mode: "marathon", has_token: typeof data.token === "string" }
@@ -259,13 +274,16 @@ function useParentBridge({
259
274
  data: { source: "marathon", expires_at: data.expires_at }
260
275
  });
261
276
  }
277
+ const token = typeof data.token === "string" ? data.token : null;
262
278
  setState({
263
- token: typeof data.token === "string" ? data.token : null,
279
+ token,
264
280
  ...readAuthMintUrls(data),
265
281
  hostAnswered: true,
266
282
  ready: true
267
283
  });
284
+ tokenRef.current = token;
268
285
  scheduleRefresh(data.expires_at);
286
+ return token;
269
287
  } catch (error) {
270
288
  if (!cancelled && !(error instanceof DOMException && error.name === "AbortError")) {
271
289
  const failure = { level: "warn", data: { mode: "marathon" }, error };
@@ -281,19 +299,38 @@ function useParentBridge({
281
299
  if (!cancelled) {
282
300
  scheduleRetry();
283
301
  }
302
+ return null;
284
303
  } finally {
285
- clearRequestTimer();
304
+ clearTimeout(requestTimer);
305
+ if (controller === requestController) controller = null;
286
306
  }
287
307
  };
308
+ const fetchAuth = (options) => {
309
+ inFlight ?? (inFlight = mintAuth(options).finally(() => {
310
+ inFlight = null;
311
+ }));
312
+ return inFlight;
313
+ };
314
+ renewerRef.current = ({ force }) => {
315
+ if (!force) return Promise.resolve(tokenRef.current);
316
+ return fetchAuth({ markReadyOnFailure: false }).then((token) => token ?? tokenRef.current);
317
+ };
288
318
  void fetchAuth({ markReadyOnFailure: true });
289
319
  return () => {
290
320
  cancelled = true;
291
321
  controller == null ? void 0 : controller.abort();
292
- clearRequestTimer();
293
322
  clearRefreshTimer();
323
+ renewerRef.current = null;
294
324
  };
295
325
  }, [isInIframe]);
296
- return state;
326
+ const renewToken = useCallback(
327
+ (options) => {
328
+ var _a;
329
+ return ((_a = renewerRef.current) == null ? void 0 : _a.call(renewerRef, options)) ?? Promise.resolve(tokenRef.current);
330
+ },
331
+ []
332
+ );
333
+ return useMemo(() => ({ ...state, renewToken }), [state, renewToken]);
297
334
  }
298
335
  function useParentAuth() {
299
336
  return useParentBridge().token;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sources":["../../src/runtime/auth.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\n\n/**\n * Check whether a PostMessage origin is trusted.\n * Accepts:\n * - explicit trusted origins passed by the host app\n * - the current iframe origin (for same-origin wrappers like Marathon)\n * - any *.athenaintel.com subdomain and localhost (legacy/default behavior)\n */\nfunction isTrustedOrigin({\n origin,\n trustedOrigins,\n}: {\n origin: string;\n trustedOrigins: string[];\n}): boolean {\n try {\n const normalizedOrigin = new URL(origin).origin;\n if (trustedOrigins.includes(normalizedOrigin)) {\n return true;\n }\n\n const { hostname } = new URL(normalizedOrigin);\n return (\n hostname === 'athenaintel.com' ||\n hostname.endsWith('.athenaintel.com') ||\n hostname === 'localhost'\n );\n } catch {\n return false;\n }\n}\n\nexport interface ParentBridgeState {\n /** PropelAuth access token from the parent window. */\n token: string | null;\n /**\n * Statewire sync mount (`…/v2/threads`) minted by the host. `null` when the\n * host minted none; the provider then derives it from the configured\n * `apiUrl`'s origin (or the environment preset).\n */\n statewireSyncUrl: string | null;\n /**\n * Bare Agora API base (no path) minted by the host. `null` when the host\n * minted none; the provider then uses the configured `backendUrl` (or the\n * environment preset).\n */\n backendBaseUrl: string | null;\n /** Athena frontend origin provided by the parent wrapper. */\n appUrl: string | null;\n /**\n * True once a host answered the bridge — a `/_athena/auth` mint or an\n * `athena-config` message — whether or not it minted the statewire fields.\n * Lets the provider tell an outdated host (answered, no mount) from no host\n * at all (the environment preset applies).\n */\n hostAnswered: boolean;\n /** True once config has been received from the parent (or timeout fired). */\n ready: boolean;\n}\n\nconst BRIDGE_URL_FIELDS = ['statewireSyncUrl', 'backendBaseUrl', 'appUrl'] as const;\n\ntype BridgeUrlField = (typeof BRIDGE_URL_FIELDS)[number];\n\n/** The URL half of {@link ParentBridgeState} — what a host can hand the SDK. */\nexport type ParentBridgeUrls = Pick<ParentBridgeState, BridgeUrlField>;\n\nconst EMPTY_BRIDGE_URLS: ParentBridgeUrls = {\n statewireSyncUrl: null,\n backendBaseUrl: null,\n appUrl: null,\n};\n\n/**\n * Fold an `athena-config` postMessage payload over the current URL set. A\n * field is taken only when it is a string; anything else keeps the previous\n * value. Keys outside {@link BridgeUrlField} (a legacy parent's `apiUrl` /\n * `backendUrl` among them) are dropped.\n */\nexport function mergeBridgeConfigMessage(\n previous: ParentBridgeUrls,\n payload: Partial<Record<BridgeUrlField, unknown>>,\n): ParentBridgeUrls {\n const next: ParentBridgeUrls = {\n statewireSyncUrl: previous.statewireSyncUrl,\n backendBaseUrl: previous.backendBaseUrl,\n appUrl: previous.appUrl,\n };\n for (const field of BRIDGE_URL_FIELDS) {\n const value = payload[field];\n if (typeof value === 'string') {\n next[field] = value;\n }\n }\n return next;\n}\n\n/**\n * Read the URL set out of a `/_athena/auth` mint body. Non-string fields are\n * `null`.\n */\nexport function readAuthMintUrls(data: Partial<Record<BridgeUrlField, unknown>>): ParentBridgeUrls {\n return mergeBridgeConfigMessage(EMPTY_BRIDGE_URLS, data);\n}\n\nexport interface ParentBridgeOptions {\n /**\n * Exact parent origins that are allowed to send auth/config via postMessage.\n * Useful for private-cloud or custom-domain deployments.\n */\n trustedOrigins?: string[];\n}\n\n/** How long to wait for the parent bridge before falling back to defaults. */\nconst BRIDGE_TIMEOUT_MS = 2000;\nconst TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000;\nconst TOKEN_REFRESH_RETRY_BASE_MS = 60 * 1000;\nconst TOKEN_REFRESH_RETRY_MAX_MS = 5 * 60 * 1000;\nconst DEFAULT_TOKEN_REFRESH_MS = 55 * 60 * 1000;\nconst EMPTY_TRUSTED_ORIGINS: string[] = [];\n\n/** Untrusted shape of the `/_athena/auth` mint body; every field is type-guarded on read. */\ninterface AthenaAuthResponse {\n token?: unknown;\n statewireSyncUrl?: unknown;\n backendBaseUrl?: unknown;\n appUrl?: unknown;\n expires_at?: unknown;\n}\n\nconst normalizeOrigin = (value: string): string | null => {\n try {\n return new URL(value).origin;\n } catch {\n return null;\n }\n};\n\nexport const getTokenRefreshDelay = (expiresAt: unknown, nowMs: number): number => {\n if (typeof expiresAt !== 'string') {\n return DEFAULT_TOKEN_REFRESH_MS;\n }\n\n const expiresAtMs = Date.parse(expiresAt);\n if (!Number.isFinite(expiresAtMs)) {\n return DEFAULT_TOKEN_REFRESH_MS;\n }\n\n return Math.max(TOKEN_REFRESH_RETRY_BASE_MS, expiresAtMs - nowMs - TOKEN_REFRESH_BUFFER_MS);\n};\n\nexport const getAuthRetryDelay = (retryAttempt: number): number => {\n const safeAttempt = Number.isFinite(retryAttempt) ? Math.max(0, Math.floor(retryAttempt)) : 0;\n return Math.min(TOKEN_REFRESH_RETRY_MAX_MS, TOKEN_REFRESH_RETRY_BASE_MS * 2 ** safeAttempt);\n};\n\n/**\n * Listens for auth tokens AND environment config from the host environment.\n *\n * Two transport modes, picked automatically:\n *\n * 1. **Iframe (legacy / Olympus inline preview)**: a parent window posts\n * `athena-config` and `athena-auth` via PostMessage. The hook also posts\n * `athena-auth-ready` so the parent knows when to (re-)send.\n *\n * 2. **Standalone (Marathon-served deploy URL)**: top-level navigation, no\n * parent. The hook fetches `/_athena/auth` on its own origin. Marathon\n * validates its session cookie (sent by the browser automatically) and\n * returns `{ token, statewireSyncUrl, backendBaseUrl, appUrl }` — the\n * statewire mount, the bare Agora base and the app origin. A 404 means\n * we're not on a Marathon origin — the hook falls through with empty\n * state.\n *\n * In both modes a timeout safety net flips `ready: true` so the app never\n * blocks indefinitely.\n */\nexport function useParentBridge({\n trustedOrigins = EMPTY_TRUSTED_ORIGINS,\n}: ParentBridgeOptions = {}): ParentBridgeState {\n const isInIframe = typeof window !== 'undefined' && window.parent !== window;\n const runtimeTrustedOrigins = useMemo(() => {\n const origins = new Set<string>();\n\n if (typeof window !== 'undefined') {\n origins.add(window.location.origin);\n }\n\n for (const trustedOrigin of trustedOrigins) {\n const normalizedOrigin = normalizeOrigin(trustedOrigin);\n if (normalizedOrigin) {\n origins.add(normalizedOrigin);\n }\n }\n\n return [...origins];\n }, [trustedOrigins]);\n\n const [state, setState] = useState<ParentBridgeState>({\n token: null,\n ...EMPTY_BRIDGE_URLS,\n hostAnswered: false,\n ready: false,\n });\n const readySignalSent = useRef(false);\n const configReceived = useRef(false);\n\n useEffect(() => {\n if (!isInIframe) return;\n\n const endBridgeSpan = athenaDiagnostics.span('auth.bridge', 'auth.bridge.start', {\n mode: 'iframe',\n });\n let spanEnded = false;\n const endOnce: typeof endBridgeSpan = (name, options) => {\n if (spanEnded) return 0;\n spanEnded = true;\n return endBridgeSpan(name, options);\n };\n\n const handler = (event: MessageEvent) => {\n if (!isTrustedOrigin({ origin: event.origin, trustedOrigins: runtimeTrustedOrigins })) {\n return;\n }\n if (!event.data || typeof event.data !== 'object') return;\n\n if (event.data.type === 'athena-config') {\n configReceived.current = true;\n setState((prev) => ({\n ...prev,\n ...mergeBridgeConfigMessage(prev, event.data),\n hostAnswered: true,\n // Don't set ready here — wait for athena-auth (token) or the timeout.\n // Setting ready on config alone causes a race: the thread list fires\n // before the token arrives, falling back to X-API-KEY.\n }));\n }\n\n if (event.data.type === 'athena-auth' && typeof event.data.token === 'string') {\n endOnce('auth.bridge.ready', { data: { mode: 'iframe' } });\n athenaDiagnostics.emit('auth.token.acquired', {\n level: 'debug',\n data: { source: 'iframe-postmessage' },\n });\n setState((prev) => ({\n ...prev,\n token: event.data.token,\n ready: true,\n }));\n }\n };\n window.addEventListener('message', handler);\n\n if (!readySignalSent.current) {\n window.parent.postMessage({ type: 'athena-auth-ready' }, '*');\n readySignalSent.current = true;\n }\n\n const timer = setTimeout(() => {\n endOnce('auth.bridge.timeout', {\n data: {\n mode: 'iframe',\n timeout_ms: BRIDGE_TIMEOUT_MS,\n config_received: configReceived.current,\n },\n });\n setState((prev) => (prev.ready ? prev : { ...prev, ready: true }));\n }, BRIDGE_TIMEOUT_MS);\n\n return () => {\n window.removeEventListener('message', handler);\n clearTimeout(timer);\n };\n }, [isInIframe, runtimeTrustedOrigins]);\n\n useEffect(() => {\n if (isInIframe) return;\n if (typeof window === 'undefined') return;\n\n let cancelled = false;\n let requestTimer: ReturnType<typeof setTimeout> | null = null;\n let refreshTimer: ReturnType<typeof setTimeout> | null = null;\n let controller: AbortController | null = null;\n let retryAttempt = 0;\n\n const clearRequestTimer = () => {\n if (requestTimer) {\n clearTimeout(requestTimer);\n requestTimer = null;\n }\n };\n\n const clearRefreshTimer = () => {\n if (refreshTimer) {\n clearTimeout(refreshTimer);\n refreshTimer = null;\n }\n };\n\n const markReady = () => {\n if (cancelled) return;\n setState((prev) => (prev.ready ? prev : { ...prev, ready: true }));\n };\n\n const scheduleRefresh = (expiresAt: unknown) => {\n clearRefreshTimer();\n retryAttempt = 0;\n const delay = getTokenRefreshDelay(expiresAt, Date.now());\n refreshTimer = setTimeout(() => {\n void fetchAuth({ markReadyOnFailure: false });\n }, delay);\n };\n\n const scheduleRetry = () => {\n clearRefreshTimer();\n const delay = getAuthRetryDelay(retryAttempt);\n retryAttempt += 1;\n refreshTimer = setTimeout(() => {\n void fetchAuth({ markReadyOnFailure: false });\n }, delay);\n };\n\n const fetchAuth = async ({\n markReadyOnFailure,\n }: {\n markReadyOnFailure: boolean;\n }) => {\n controller?.abort();\n controller = new AbortController();\n\n // `markReadyOnFailure` distinguishes the initial acquisition (a span the\n // provider blocks on) from a background refresh. Idempotent: the abort\n // timer and the catch path can both reach an end call.\n const isInitial = markReadyOnFailure;\n const rawEndSpan = isInitial\n ? athenaDiagnostics.span('auth.bridge', 'auth.bridge.start', { mode: 'marathon' })\n : null;\n let spanEnded = false;\n const endBridgeSpan: typeof rawEndSpan = rawEndSpan\n ? (name, options) => {\n if (spanEnded) return 0;\n spanEnded = true;\n return rawEndSpan(name, options);\n }\n : null;\n\n clearRequestTimer();\n requestTimer = setTimeout(() => {\n controller?.abort();\n endBridgeSpan?.('auth.bridge.timeout', {\n data: { mode: 'marathon', timeout_ms: BRIDGE_TIMEOUT_MS },\n });\n if (markReadyOnFailure) {\n markReady();\n }\n }, BRIDGE_TIMEOUT_MS);\n\n try {\n const resp = await fetch('/_athena/auth', {\n credentials: 'include',\n headers: { Accept: 'application/json' },\n signal: controller.signal,\n });\n if (cancelled) return;\n\n if (!resp.ok) {\n if (resp.status === 404) {\n // Not a Marathon origin — expected for standalone/local dev.\n endBridgeSpan?.('auth.bridge.absent', {\n data: { mode: 'marathon', status: resp.status },\n });\n markReady();\n } else {\n const failure = {\n level: 'warn' as const,\n data: { mode: 'marathon', status: resp.status },\n };\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.error', failure);\n } else {\n athenaDiagnostics.emit('auth.token.refresh_failed', failure);\n }\n if (markReadyOnFailure) {\n markReady();\n }\n scheduleRetry();\n }\n return;\n }\n\n const data = (await resp.json()) as AthenaAuthResponse;\n if (cancelled) return;\n\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.ready', {\n data: { mode: 'marathon', has_token: typeof data.token === 'string' },\n });\n athenaDiagnostics.emit('auth.token.acquired', {\n level: 'debug',\n data: { source: 'marathon', expires_at: data.expires_at },\n });\n } else {\n athenaDiagnostics.emit('auth.token.refreshed', {\n level: 'debug',\n data: { source: 'marathon', expires_at: data.expires_at },\n });\n }\n\n setState({\n token: typeof data.token === 'string' ? data.token : null,\n ...readAuthMintUrls(data),\n hostAnswered: true,\n ready: true,\n });\n scheduleRefresh(data.expires_at);\n } catch (error) {\n if (!cancelled && !(error instanceof DOMException && error.name === 'AbortError')) {\n const failure = { level: 'warn' as const, data: { mode: 'marathon' }, error };\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.error', failure);\n } else {\n athenaDiagnostics.emit('auth.token.refresh_failed', failure);\n }\n }\n if (markReadyOnFailure) {\n markReady();\n }\n if (!cancelled) {\n scheduleRetry();\n }\n } finally {\n clearRequestTimer();\n }\n };\n\n void fetchAuth({ markReadyOnFailure: true });\n\n return () => {\n cancelled = true;\n controller?.abort();\n clearRequestTimer();\n clearRefreshTimer();\n };\n }, [isInIframe]);\n\n return state;\n}\n\n/**\n * Legacy hook — returns just the auth token from the parent bridge.\n * Prefer useParentBridge() for new code.\n */\nexport function useParentAuth(): string | null {\n return useParentBridge().token;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","sources":["../../src/runtime/auth.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { AthenaTokenRenewer } from '../auth/credential-source';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\n\n/**\n * Check whether a PostMessage origin is trusted.\n * Accepts:\n * - explicit trusted origins passed by the host app\n * - the current iframe origin (for same-origin wrappers like Marathon)\n * - any *.athenaintel.com subdomain and localhost (legacy/default behavior)\n */\nfunction isTrustedOrigin({\n origin,\n trustedOrigins,\n}: {\n origin: string;\n trustedOrigins: string[];\n}): boolean {\n try {\n const normalizedOrigin = new URL(origin).origin;\n if (trustedOrigins.includes(normalizedOrigin)) {\n return true;\n }\n\n const { hostname } = new URL(normalizedOrigin);\n return (\n hostname === 'athenaintel.com' ||\n hostname.endsWith('.athenaintel.com') ||\n hostname === 'localhost'\n );\n } catch {\n return false;\n }\n}\n\nexport interface ParentBridgeState {\n /** PropelAuth access token from the parent window. */\n token: string | null;\n /**\n * Statewire sync mount (`…/v2/threads`) minted by the host. `null` when the\n * host minted none; the provider then derives it from the configured\n * `apiUrl`'s origin (or the environment preset).\n */\n statewireSyncUrl: string | null;\n /**\n * Bare Agora API base (no path) minted by the host. `null` when the host\n * minted none; the provider then uses the configured `backendUrl` (or the\n * environment preset).\n */\n backendBaseUrl: string | null;\n /** Athena frontend origin provided by the parent wrapper. */\n appUrl: string | null;\n /**\n * True once a host answered the bridge — a `/_athena/auth` mint or an\n * `athena-config` message — whether or not it minted the statewire fields.\n * Lets the provider tell an outdated host (answered, no mount) from no host\n * at all (the environment preset applies).\n */\n hostAnswered: boolean;\n /** True once config has been received from the parent (or timeout fired). */\n ready: boolean;\n}\n\n/**\n * {@link ParentBridgeState} plus the bridge's token renewal: `force` (the\n * token was refused) asks the host for a new one — a fresh `/_athena/auth`\n * mint on a Marathon origin, or an `athena-auth-ready` request answered by the\n * parent window's next `athena-auth` — and resolves the held token otherwise.\n */\nexport type ParentBridge = ParentBridgeState & { renewToken: AthenaTokenRenewer };\n\nconst BRIDGE_URL_FIELDS = ['statewireSyncUrl', 'backendBaseUrl', 'appUrl'] as const;\n\ntype BridgeUrlField = (typeof BRIDGE_URL_FIELDS)[number];\n\n/** The URL half of {@link ParentBridgeState} — what a host can hand the SDK. */\nexport type ParentBridgeUrls = Pick<ParentBridgeState, BridgeUrlField>;\n\nconst EMPTY_BRIDGE_URLS: ParentBridgeUrls = {\n statewireSyncUrl: null,\n backendBaseUrl: null,\n appUrl: null,\n};\n\n/**\n * Fold an `athena-config` postMessage payload over the current URL set. A\n * field is taken only when it is a string; anything else keeps the previous\n * value. Keys outside {@link BridgeUrlField} (a legacy parent's `apiUrl` /\n * `backendUrl` among them) are dropped.\n */\nexport function mergeBridgeConfigMessage(\n previous: ParentBridgeUrls,\n payload: Partial<Record<BridgeUrlField, unknown>>,\n): ParentBridgeUrls {\n const next: ParentBridgeUrls = {\n statewireSyncUrl: previous.statewireSyncUrl,\n backendBaseUrl: previous.backendBaseUrl,\n appUrl: previous.appUrl,\n };\n for (const field of BRIDGE_URL_FIELDS) {\n const value = payload[field];\n if (typeof value === 'string') {\n next[field] = value;\n }\n }\n return next;\n}\n\n/**\n * Read the URL set out of a `/_athena/auth` mint body. Non-string fields are\n * `null`.\n */\nexport function readAuthMintUrls(data: Partial<Record<BridgeUrlField, unknown>>): ParentBridgeUrls {\n return mergeBridgeConfigMessage(EMPTY_BRIDGE_URLS, data);\n}\n\nexport interface ParentBridgeOptions {\n /**\n * Exact parent origins that are allowed to send auth/config via postMessage.\n * Useful for private-cloud or custom-domain deployments.\n */\n trustedOrigins?: string[];\n}\n\n/** How long to wait for the parent bridge before falling back to defaults. */\nconst BRIDGE_TIMEOUT_MS = 2000;\n/** How long a renewal waits for the parent window to answer `athena-auth-ready`. */\nconst PARENT_TOKEN_RENEWAL_TIMEOUT_MS = 5000;\nconst TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000;\nconst TOKEN_REFRESH_RETRY_BASE_MS = 60 * 1000;\nconst TOKEN_REFRESH_RETRY_MAX_MS = 5 * 60 * 1000;\nconst DEFAULT_TOKEN_REFRESH_MS = 55 * 60 * 1000;\nconst EMPTY_TRUSTED_ORIGINS: string[] = [];\n\n/** Untrusted shape of the `/_athena/auth` mint body; every field is type-guarded on read. */\ninterface AthenaAuthResponse {\n token?: unknown;\n statewireSyncUrl?: unknown;\n backendBaseUrl?: unknown;\n appUrl?: unknown;\n expires_at?: unknown;\n}\n\nconst normalizeOrigin = (value: string): string | null => {\n try {\n return new URL(value).origin;\n } catch {\n return null;\n }\n};\n\nexport const getTokenRefreshDelay = (expiresAt: unknown, nowMs: number): number => {\n if (typeof expiresAt !== 'string') {\n return DEFAULT_TOKEN_REFRESH_MS;\n }\n\n const expiresAtMs = Date.parse(expiresAt);\n if (!Number.isFinite(expiresAtMs)) {\n return DEFAULT_TOKEN_REFRESH_MS;\n }\n\n return Math.max(TOKEN_REFRESH_RETRY_BASE_MS, expiresAtMs - nowMs - TOKEN_REFRESH_BUFFER_MS);\n};\n\nexport const getAuthRetryDelay = (retryAttempt: number): number => {\n const safeAttempt = Number.isFinite(retryAttempt) ? Math.max(0, Math.floor(retryAttempt)) : 0;\n return Math.min(TOKEN_REFRESH_RETRY_MAX_MS, TOKEN_REFRESH_RETRY_BASE_MS * 2 ** safeAttempt);\n};\n\n/**\n * Listens for auth tokens AND environment config from the host environment.\n *\n * Two transport modes, picked automatically:\n *\n * 1. **Iframe (legacy / Olympus inline preview)**: a parent window posts\n * `athena-config` and `athena-auth` via PostMessage. The hook also posts\n * `athena-auth-ready` so the parent knows when to (re-)send.\n *\n * 2. **Standalone (Marathon-served deploy URL)**: top-level navigation, no\n * parent. The hook fetches `/_athena/auth` on its own origin. Marathon\n * validates its session cookie (sent by the browser automatically) and\n * returns `{ token, statewireSyncUrl, backendBaseUrl, appUrl }` — the\n * statewire mount, the bare Agora base and the app origin. A 404 means\n * we're not on a Marathon origin — the hook falls through with empty\n * state.\n *\n * In both modes a timeout safety net flips `ready: true` so the app never\n * blocks indefinitely.\n */\nexport function useParentBridge({\n trustedOrigins = EMPTY_TRUSTED_ORIGINS,\n}: ParentBridgeOptions = {}): ParentBridge {\n const isInIframe = typeof window !== 'undefined' && window.parent !== window;\n const runtimeTrustedOrigins = useMemo(() => {\n const origins = new Set<string>();\n\n if (typeof window !== 'undefined') {\n origins.add(window.location.origin);\n }\n\n for (const trustedOrigin of trustedOrigins) {\n const normalizedOrigin = normalizeOrigin(trustedOrigin);\n if (normalizedOrigin) {\n origins.add(normalizedOrigin);\n }\n }\n\n return [...origins];\n }, [trustedOrigins]);\n\n const [state, setState] = useState<ParentBridgeState>({\n token: null,\n ...EMPTY_BRIDGE_URLS,\n hostAnswered: false,\n ready: false,\n });\n const readySignalSent = useRef(false);\n const configReceived = useRef(false);\n const tokenRef = useRef<string | null>(state.token);\n tokenRef.current = state.token;\n // Installed by whichever transport mode is active (iframe or Marathon).\n const renewerRef = useRef<AthenaTokenRenewer | null>(null);\n\n useEffect(() => {\n if (!isInIframe) return;\n\n const endBridgeSpan = athenaDiagnostics.span('auth.bridge', 'auth.bridge.start', {\n mode: 'iframe',\n });\n let spanEnded = false;\n const endOnce: typeof endBridgeSpan = (name, options) => {\n if (spanEnded) return 0;\n spanEnded = true;\n return endBridgeSpan(name, options);\n };\n\n // Renewals waiting for the parent's next `athena-auth`.\n const tokenWaiters = new Set<(token: string | null) => void>();\n\n const handler = (event: MessageEvent) => {\n if (!isTrustedOrigin({ origin: event.origin, trustedOrigins: runtimeTrustedOrigins })) {\n return;\n }\n if (!event.data || typeof event.data !== 'object') return;\n\n if (event.data.type === 'athena-config') {\n configReceived.current = true;\n setState((prev) => ({\n ...prev,\n ...mergeBridgeConfigMessage(prev, event.data),\n hostAnswered: true,\n // Don't set ready here — wait for athena-auth (token) or the timeout.\n // Setting ready on config alone causes a race: the thread list fires\n // before the token arrives, falling back to X-API-KEY.\n }));\n }\n\n if (event.data.type === 'athena-auth' && typeof event.data.token === 'string') {\n endOnce('auth.bridge.ready', { data: { mode: 'iframe' } });\n athenaDiagnostics.emit('auth.token.acquired', {\n level: 'debug',\n data: { source: 'iframe-postmessage' },\n });\n setState((prev) => ({\n ...prev,\n token: event.data.token,\n ready: true,\n }));\n tokenRef.current = event.data.token;\n for (const resolve of [...tokenWaiters]) resolve(event.data.token);\n }\n };\n window.addEventListener('message', handler);\n\n // A renewal asks the parent to send its current token again — the same\n // `athena-auth-ready` handshake a parent already answers — and settles on\n // the next `athena-auth`, or on the held token when none arrives in time.\n renewerRef.current = ({ force }) => {\n if (!force) return Promise.resolve(tokenRef.current);\n return new Promise<string | null>((resolve) => {\n const settle = (token: string | null) => {\n clearTimeout(timer);\n tokenWaiters.delete(settle);\n resolve(token ?? tokenRef.current);\n };\n const timer = setTimeout(() => settle(null), PARENT_TOKEN_RENEWAL_TIMEOUT_MS);\n tokenWaiters.add(settle);\n window.parent.postMessage({ type: 'athena-auth-ready' }, '*');\n });\n };\n\n if (!readySignalSent.current) {\n window.parent.postMessage({ type: 'athena-auth-ready' }, '*');\n readySignalSent.current = true;\n }\n\n const timer = setTimeout(() => {\n endOnce('auth.bridge.timeout', {\n data: {\n mode: 'iframe',\n timeout_ms: BRIDGE_TIMEOUT_MS,\n config_received: configReceived.current,\n },\n });\n setState((prev) => (prev.ready ? prev : { ...prev, ready: true }));\n }, BRIDGE_TIMEOUT_MS);\n\n return () => {\n window.removeEventListener('message', handler);\n clearTimeout(timer);\n renewerRef.current = null;\n for (const resolve of [...tokenWaiters]) resolve(null);\n };\n }, [isInIframe, runtimeTrustedOrigins]);\n\n useEffect(() => {\n if (isInIframe) return;\n if (typeof window === 'undefined') return;\n\n let cancelled = false;\n let refreshTimer: ReturnType<typeof setTimeout> | null = null;\n let controller: AbortController | null = null;\n let inFlight: Promise<string | null> | null = null;\n let retryAttempt = 0;\n\n const clearRefreshTimer = () => {\n if (refreshTimer) {\n clearTimeout(refreshTimer);\n refreshTimer = null;\n }\n };\n\n const markReady = () => {\n if (cancelled) return;\n setState((prev) => (prev.ready ? prev : { ...prev, ready: true }));\n };\n\n const scheduleRefresh = (expiresAt: unknown) => {\n clearRefreshTimer();\n retryAttempt = 0;\n const delay = getTokenRefreshDelay(expiresAt, Date.now());\n refreshTimer = setTimeout(() => {\n void fetchAuth({ markReadyOnFailure: false });\n }, delay);\n };\n\n const scheduleRetry = () => {\n clearRefreshTimer();\n const delay = getAuthRetryDelay(retryAttempt);\n retryAttempt += 1;\n refreshTimer = setTimeout(() => {\n void fetchAuth({ markReadyOnFailure: false });\n }, delay);\n };\n\n const mintAuth = async ({\n markReadyOnFailure,\n }: {\n markReadyOnFailure: boolean;\n }): Promise<string | null> => {\n const requestController = new AbortController();\n controller = requestController;\n\n // `markReadyOnFailure` distinguishes the initial acquisition (a span the\n // provider blocks on) from a background refresh. Idempotent: the abort\n // timer and the catch path can both reach an end call.\n const isInitial = markReadyOnFailure;\n const rawEndSpan = isInitial\n ? athenaDiagnostics.span('auth.bridge', 'auth.bridge.start', { mode: 'marathon' })\n : null;\n let spanEnded = false;\n const endBridgeSpan: typeof rawEndSpan = rawEndSpan\n ? (name, options) => {\n if (spanEnded) return 0;\n spanEnded = true;\n return rawEndSpan(name, options);\n }\n : null;\n\n const requestTimer = setTimeout(() => {\n requestController.abort();\n endBridgeSpan?.('auth.bridge.timeout', {\n data: { mode: 'marathon', timeout_ms: BRIDGE_TIMEOUT_MS },\n });\n if (markReadyOnFailure) {\n markReady();\n }\n }, BRIDGE_TIMEOUT_MS);\n\n try {\n const resp = await fetch('/_athena/auth', {\n credentials: 'include',\n headers: { Accept: 'application/json' },\n signal: requestController.signal,\n });\n if (cancelled) return null;\n\n if (!resp.ok) {\n if (resp.status === 404) {\n // Not a Marathon origin — expected for standalone/local dev.\n endBridgeSpan?.('auth.bridge.absent', {\n data: { mode: 'marathon', status: resp.status },\n });\n markReady();\n } else {\n const failure = {\n level: 'warn' as const,\n data: { mode: 'marathon', status: resp.status },\n };\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.error', failure);\n } else {\n athenaDiagnostics.emit('auth.token.refresh_failed', failure);\n }\n if (markReadyOnFailure) {\n markReady();\n }\n scheduleRetry();\n }\n return null;\n }\n\n const data = (await resp.json()) as AthenaAuthResponse;\n if (cancelled) return null;\n\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.ready', {\n data: { mode: 'marathon', has_token: typeof data.token === 'string' },\n });\n athenaDiagnostics.emit('auth.token.acquired', {\n level: 'debug',\n data: { source: 'marathon', expires_at: data.expires_at },\n });\n } else {\n athenaDiagnostics.emit('auth.token.refreshed', {\n level: 'debug',\n data: { source: 'marathon', expires_at: data.expires_at },\n });\n }\n\n const token = typeof data.token === 'string' ? data.token : null;\n setState({\n token,\n ...readAuthMintUrls(data),\n hostAnswered: true,\n ready: true,\n });\n tokenRef.current = token;\n scheduleRefresh(data.expires_at);\n return token;\n } catch (error) {\n if (!cancelled && !(error instanceof DOMException && error.name === 'AbortError')) {\n const failure = { level: 'warn' as const, data: { mode: 'marathon' }, error };\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.error', failure);\n } else {\n athenaDiagnostics.emit('auth.token.refresh_failed', failure);\n }\n }\n if (markReadyOnFailure) {\n markReady();\n }\n if (!cancelled) {\n scheduleRetry();\n }\n return null;\n } finally {\n clearTimeout(requestTimer);\n if (controller === requestController) controller = null;\n }\n };\n\n // One mint at a time. The initial acquisition, a scheduled refresh, a\n // retry and a forced renewal that overlap share the request in flight\n // instead of aborting it, so each request keeps its own timeout and the\n // schedule never cancels a renewal a connection is waiting on.\n const fetchAuth = (options: { markReadyOnFailure: boolean }): Promise<string | null> => {\n inFlight ??= mintAuth(options).finally(() => {\n inFlight = null;\n });\n return inFlight;\n };\n\n // A forced renewal mints now instead of waiting for the scheduled refresh\n // (joining a mint already in flight); the schedule restarts from the new\n // token's expiry.\n renewerRef.current = ({ force }) => {\n if (!force) return Promise.resolve(tokenRef.current);\n return fetchAuth({ markReadyOnFailure: false }).then((token) => token ?? tokenRef.current);\n };\n\n void fetchAuth({ markReadyOnFailure: true });\n\n return () => {\n cancelled = true;\n controller?.abort();\n clearRefreshTimer();\n renewerRef.current = null;\n };\n }, [isInIframe]);\n\n const renewToken = useCallback<AthenaTokenRenewer>(\n (options) => renewerRef.current?.(options) ?? Promise.resolve(tokenRef.current),\n [],\n );\n\n return useMemo(() => ({ ...state, renewToken }), [state, renewToken]);\n}\n\n/**\n * Legacy hook — returns just the auth token from the parent bridge.\n * Prefer useParentBridge() for new code.\n */\nexport function useParentAuth(): string | null {\n return useParentBridge().token;\n}\n"],"names":["timer"],"mappings":";;AAWA,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AACF,GAGY;AACV,MAAI;AACF,UAAM,mBAAmB,IAAI,IAAI,MAAM,EAAE;AACzC,QAAI,eAAe,SAAS,gBAAgB,GAAG;AAC7C,aAAO;AAAA,IACT;AAEA,UAAM,EAAE,SAAA,IAAa,IAAI,IAAI,gBAAgB;AAC7C,WACE,aAAa,qBACb,SAAS,SAAS,kBAAkB,KACpC,aAAa;AAAA,EAEjB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAsCA,MAAM,oBAAoB,CAAC,oBAAoB,kBAAkB,QAAQ;AAOzE,MAAM,oBAAsC;AAAA,EAC1C,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,QAAQ;AACV;AAQO,SAAS,yBACd,UACA,SACkB;AAClB,QAAM,OAAyB;AAAA,IAC7B,kBAAkB,SAAS;AAAA,IAC3B,gBAAgB,SAAS;AAAA,IACzB,QAAQ,SAAS;AAAA,EAAA;AAEnB,aAAW,SAAS,mBAAmB;AACrC,UAAM,QAAQ,QAAQ,KAAK;AAC3B,QAAI,OAAO,UAAU,UAAU;AAC7B,WAAK,KAAK,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,iBAAiB,MAAkE;AACjG,SAAO,yBAAyB,mBAAmB,IAAI;AACzD;AAWA,MAAM,oBAAoB;AAE1B,MAAM,kCAAkC;AACxC,MAAM,0BAA0B,IAAI,KAAK;AACzC,MAAM,8BAA8B,KAAK;AACzC,MAAM,6BAA6B,IAAI,KAAK;AAC5C,MAAM,2BAA2B,KAAK,KAAK;AAC3C,MAAM,wBAAkC,CAAA;AAWxC,MAAM,kBAAkB,CAAC,UAAiC;AACxD,MAAI;AACF,WAAO,IAAI,IAAI,KAAK,EAAE;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,MAAM,uBAAuB,CAAC,WAAoB,UAA0B;AACjF,MAAI,OAAO,cAAc,UAAU;AACjC,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,KAAK,MAAM,SAAS;AACxC,MAAI,CAAC,OAAO,SAAS,WAAW,GAAG;AACjC,WAAO;AAAA,EACT;AAEA,SAAO,KAAK,IAAI,6BAA6B,cAAc,QAAQ,uBAAuB;AAC5F;AAEO,MAAM,oBAAoB,CAAC,iBAAiC;AACjE,QAAM,cAAc,OAAO,SAAS,YAAY,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,CAAC,IAAI;AAC5F,SAAO,KAAK,IAAI,4BAA4B,8BAA8B,KAAK,WAAW;AAC5F;AAsBO,SAAS,gBAAgB;AAAA,EAC9B,iBAAiB;AACnB,IAAyB,IAAkB;AACzC,QAAM,aAAa,OAAO,WAAW,eAAe,OAAO,WAAW;AACtE,QAAM,wBAAwB,QAAQ,MAAM;AAC1C,UAAM,8BAAc,IAAA;AAEpB,QAAI,OAAO,WAAW,aAAa;AACjC,cAAQ,IAAI,OAAO,SAAS,MAAM;AAAA,IACpC;AAEA,eAAW,iBAAiB,gBAAgB;AAC1C,YAAM,mBAAmB,gBAAgB,aAAa;AACtD,UAAI,kBAAkB;AACpB,gBAAQ,IAAI,gBAAgB;AAAA,MAC9B;AAAA,IACF;AAEA,WAAO,CAAC,GAAG,OAAO;AAAA,EACpB,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA4B;AAAA,IACpD,OAAO;AAAA,IACP,GAAG;AAAA,IACH,cAAc;AAAA,IACd,OAAO;AAAA,EAAA,CACR;AACD,QAAM,kBAAkB,OAAO,KAAK;AACpC,QAAM,iBAAiB,OAAO,KAAK;AACnC,QAAM,WAAW,OAAsB,MAAM,KAAK;AAClD,WAAS,UAAU,MAAM;AAEzB,QAAM,aAAa,OAAkC,IAAI;AAEzD,YAAU,MAAM;AACd,QAAI,CAAC,WAAY;AAEjB,UAAM,gBAAgB,kBAAkB,KAAK,eAAe,qBAAqB;AAAA,MAC/E,MAAM;AAAA,IAAA,CACP;AACD,QAAI,YAAY;AAChB,UAAM,UAAgC,CAAC,MAAM,YAAY;AACvD,UAAI,UAAW,QAAO;AACtB,kBAAY;AACZ,aAAO,cAAc,MAAM,OAAO;AAAA,IACpC;AAGA,UAAM,mCAAmB,IAAA;AAEzB,UAAM,UAAU,CAAC,UAAwB;AACvC,UAAI,CAAC,gBAAgB,EAAE,QAAQ,MAAM,QAAQ,gBAAgB,sBAAA,CAAuB,GAAG;AACrF;AAAA,MACF;AACA,UAAI,CAAC,MAAM,QAAQ,OAAO,MAAM,SAAS,SAAU;AAEnD,UAAI,MAAM,KAAK,SAAS,iBAAiB;AACvC,uBAAe,UAAU;AACzB,iBAAS,CAAC,UAAU;AAAA,UAClB,GAAG;AAAA,UACH,GAAG,yBAAyB,MAAM,MAAM,IAAI;AAAA,UAC5C,cAAc;AAAA;AAAA;AAAA;AAAA,QAAA,EAId;AAAA,MACJ;AAEA,UAAI,MAAM,KAAK,SAAS,iBAAiB,OAAO,MAAM,KAAK,UAAU,UAAU;AAC7E,gBAAQ,qBAAqB,EAAE,MAAM,EAAE,MAAM,SAAA,GAAY;AACzD,0BAAkB,KAAK,uBAAuB;AAAA,UAC5C,OAAO;AAAA,UACP,MAAM,EAAE,QAAQ,qBAAA;AAAA,QAAqB,CACtC;AACD,iBAAS,CAAC,UAAU;AAAA,UAClB,GAAG;AAAA,UACH,OAAO,MAAM,KAAK;AAAA,UAClB,OAAO;AAAA,QAAA,EACP;AACF,iBAAS,UAAU,MAAM,KAAK;AAC9B,mBAAW,WAAW,CAAC,GAAG,YAAY,EAAG,SAAQ,MAAM,KAAK,KAAK;AAAA,MACnE;AAAA,IACF;AACA,WAAO,iBAAiB,WAAW,OAAO;AAK1C,eAAW,UAAU,CAAC,EAAE,YAAY;AAClC,UAAI,CAAC,MAAO,QAAO,QAAQ,QAAQ,SAAS,OAAO;AACnD,aAAO,IAAI,QAAuB,CAAC,YAAY;AAC7C,cAAM,SAAS,CAAC,UAAyB;AACvC,uBAAaA,MAAK;AAClB,uBAAa,OAAO,MAAM;AAC1B,kBAAQ,SAAS,SAAS,OAAO;AAAA,QACnC;AACA,cAAMA,SAAQ,WAAW,MAAM,OAAO,IAAI,GAAG,+BAA+B;AAC5E,qBAAa,IAAI,MAAM;AACvB,eAAO,OAAO,YAAY,EAAE,MAAM,oBAAA,GAAuB,GAAG;AAAA,MAC9D,CAAC;AAAA,IACH;AAEA,QAAI,CAAC,gBAAgB,SAAS;AAC5B,aAAO,OAAO,YAAY,EAAE,MAAM,oBAAA,GAAuB,GAAG;AAC5D,sBAAgB,UAAU;AAAA,IAC5B;AAEA,UAAM,QAAQ,WAAW,MAAM;AAC7B,cAAQ,uBAAuB;AAAA,QAC7B,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,YAAY;AAAA,UACZ,iBAAiB,eAAe;AAAA,QAAA;AAAA,MAClC,CACD;AACD,eAAS,CAAC,SAAU,KAAK,QAAQ,OAAO,EAAE,GAAG,MAAM,OAAO,MAAO;AAAA,IACnE,GAAG,iBAAiB;AAEpB,WAAO,MAAM;AACX,aAAO,oBAAoB,WAAW,OAAO;AAC7C,mBAAa,KAAK;AAClB,iBAAW,UAAU;AACrB,iBAAW,WAAW,CAAC,GAAG,YAAY,WAAW,IAAI;AAAA,IACvD;AAAA,EACF,GAAG,CAAC,YAAY,qBAAqB,CAAC;AAEtC,YAAU,MAAM;AACd,QAAI,WAAY;AAChB,QAAI,OAAO,WAAW,YAAa;AAEnC,QAAI,YAAY;AAChB,QAAI,eAAqD;AACzD,QAAI,aAAqC;AACzC,QAAI,WAA0C;AAC9C,QAAI,eAAe;AAEnB,UAAM,oBAAoB,MAAM;AAC9B,UAAI,cAAc;AAChB,qBAAa,YAAY;AACzB,uBAAe;AAAA,MACjB;AAAA,IACF;AAEA,UAAM,YAAY,MAAM;AACtB,UAAI,UAAW;AACf,eAAS,CAAC,SAAU,KAAK,QAAQ,OAAO,EAAE,GAAG,MAAM,OAAO,MAAO;AAAA,IACnE;AAEA,UAAM,kBAAkB,CAAC,cAAuB;AAC9C,wBAAA;AACA,qBAAe;AACf,YAAM,QAAQ,qBAAqB,WAAW,KAAK,KAAK;AACxD,qBAAe,WAAW,MAAM;AAC9B,aAAK,UAAU,EAAE,oBAAoB,OAAO;AAAA,MAC9C,GAAG,KAAK;AAAA,IACV;AAEA,UAAM,gBAAgB,MAAM;AAC1B,wBAAA;AACA,YAAM,QAAQ,kBAAkB,YAAY;AAC5C,sBAAgB;AAChB,qBAAe,WAAW,MAAM;AAC9B,aAAK,UAAU,EAAE,oBAAoB,OAAO;AAAA,MAC9C,GAAG,KAAK;AAAA,IACV;AAEA,UAAM,WAAW,OAAO;AAAA,MACtB;AAAA,IAAA,MAG4B;AAC5B,YAAM,oBAAoB,IAAI,gBAAA;AAC9B,mBAAa;AAKb,YAAM,YAAY;AAClB,YAAM,aAAa,YACf,kBAAkB,KAAK,eAAe,qBAAqB,EAAE,MAAM,WAAA,CAAY,IAC/E;AACJ,UAAI,YAAY;AAChB,YAAM,gBAAmC,aACrC,CAAC,MAAM,YAAY;AACjB,YAAI,UAAW,QAAO;AACtB,oBAAY;AACZ,eAAO,WAAW,MAAM,OAAO;AAAA,MACjC,IACA;AAEJ,YAAM,eAAe,WAAW,MAAM;AACpC,0BAAkB,MAAA;AAClB,uDAAgB,uBAAuB;AAAA,UACrC,MAAM,EAAE,MAAM,YAAY,YAAY,kBAAA;AAAA,QAAkB;AAE1D,YAAI,oBAAoB;AACtB,oBAAA;AAAA,QACF;AAAA,MACF,GAAG,iBAAiB;AAEpB,UAAI;AACF,cAAM,OAAO,MAAM,MAAM,iBAAiB;AAAA,UACxC,aAAa;AAAA,UACb,SAAS,EAAE,QAAQ,mBAAA;AAAA,UACnB,QAAQ,kBAAkB;AAAA,QAAA,CAC3B;AACD,YAAI,UAAW,QAAO;AAEtB,YAAI,CAAC,KAAK,IAAI;AACZ,cAAI,KAAK,WAAW,KAAK;AAEvB,2DAAgB,sBAAsB;AAAA,cACpC,MAAM,EAAE,MAAM,YAAY,QAAQ,KAAK,OAAA;AAAA,YAAO;AAEhD,sBAAA;AAAA,UACF,OAAO;AACL,kBAAM,UAAU;AAAA,cACd,OAAO;AAAA,cACP,MAAM,EAAE,MAAM,YAAY,QAAQ,KAAK,OAAA;AAAA,YAAO;AAEhD,gBAAI,eAAe;AACjB,4BAAc,qBAAqB,OAAO;AAAA,YAC5C,OAAO;AACL,gCAAkB,KAAK,6BAA6B,OAAO;AAAA,YAC7D;AACA,gBAAI,oBAAoB;AACtB,wBAAA;AAAA,YACF;AACA,0BAAA;AAAA,UACF;AACA,iBAAO;AAAA,QACT;AAEA,cAAM,OAAQ,MAAM,KAAK,KAAA;AACzB,YAAI,UAAW,QAAO;AAEtB,YAAI,eAAe;AACjB,wBAAc,qBAAqB;AAAA,YACjC,MAAM,EAAE,MAAM,YAAY,WAAW,OAAO,KAAK,UAAU,SAAA;AAAA,UAAS,CACrE;AACD,4BAAkB,KAAK,uBAAuB;AAAA,YAC5C,OAAO;AAAA,YACP,MAAM,EAAE,QAAQ,YAAY,YAAY,KAAK,WAAA;AAAA,UAAW,CACzD;AAAA,QACH,OAAO;AACL,4BAAkB,KAAK,wBAAwB;AAAA,YAC7C,OAAO;AAAA,YACP,MAAM,EAAE,QAAQ,YAAY,YAAY,KAAK,WAAA;AAAA,UAAW,CACzD;AAAA,QACH;AAEA,cAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAC5D,iBAAS;AAAA,UACP;AAAA,UACA,GAAG,iBAAiB,IAAI;AAAA,UACxB,cAAc;AAAA,UACd,OAAO;AAAA,QAAA,CACR;AACD,iBAAS,UAAU;AACnB,wBAAgB,KAAK,UAAU;AAC/B,eAAO;AAAA,MACT,SAAS,OAAO;AACd,YAAI,CAAC,aAAa,EAAE,iBAAiB,gBAAgB,MAAM,SAAS,eAAe;AACjF,gBAAM,UAAU,EAAE,OAAO,QAAiB,MAAM,EAAE,MAAM,WAAA,GAAc,MAAA;AACtE,cAAI,eAAe;AACjB,0BAAc,qBAAqB,OAAO;AAAA,UAC5C,OAAO;AACL,8BAAkB,KAAK,6BAA6B,OAAO;AAAA,UAC7D;AAAA,QACF;AACA,YAAI,oBAAoB;AACtB,oBAAA;AAAA,QACF;AACA,YAAI,CAAC,WAAW;AACd,wBAAA;AAAA,QACF;AACA,eAAO;AAAA,MACT,UAAA;AACE,qBAAa,YAAY;AACzB,YAAI,eAAe,kBAAmB,cAAa;AAAA,MACrD;AAAA,IACF;AAMA,UAAM,YAAY,CAAC,YAAqE;AACtF,8BAAa,SAAS,OAAO,EAAE,QAAQ,MAAM;AAC3C,mBAAW;AAAA,MACb,CAAC;AACD,aAAO;AAAA,IACT;AAKA,eAAW,UAAU,CAAC,EAAE,YAAY;AAClC,UAAI,CAAC,MAAO,QAAO,QAAQ,QAAQ,SAAS,OAAO;AACnD,aAAO,UAAU,EAAE,oBAAoB,OAAO,EAAE,KAAK,CAAC,UAAU,SAAS,SAAS,OAAO;AAAA,IAC3F;AAEA,SAAK,UAAU,EAAE,oBAAoB,MAAM;AAE3C,WAAO,MAAM;AACX,kBAAY;AACZ,+CAAY;AACZ,wBAAA;AACA,iBAAW,UAAU;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,aAAa;AAAA,IACjB,CAAC;;AAAY,+BAAW,YAAX,oCAAqB,aAAY,QAAQ,QAAQ,SAAS,OAAO;AAAA;AAAA,IAC9E,CAAA;AAAA,EAAC;AAGH,SAAO,QAAQ,OAAO,EAAE,GAAG,OAAO,eAAe,CAAC,OAAO,UAAU,CAAC;AACtE;AAMO,SAAS,gBAA+B;AAC7C,SAAO,kBAAkB;AAC3B;"}