@athenaintel/react 0.24.1 → 0.26.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 (186) hide show
  1. package/README.md +24 -0
  2. package/dist/assets/AssetPanel.cjs +4 -3
  3. package/dist/assets/AssetPanel.cjs.map +1 -1
  4. package/dist/assets/AssetPanel.js +4 -3
  5. package/dist/assets/AssetPanel.js.map +1 -1
  6. package/dist/assets/AthenaAssetEmbed.cjs +2 -1
  7. package/dist/assets/AthenaAssetEmbed.cjs.map +1 -1
  8. package/dist/assets/AthenaAssetEmbed.js +2 -1
  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-ui/composer/run-input-rules-on-enter.cjs +14 -0
  40. package/dist/chat-ui/composer/run-input-rules-on-enter.cjs.map +1 -0
  41. package/dist/chat-ui/composer/run-input-rules-on-enter.d.ts +10 -0
  42. package/dist/chat-ui/composer/run-input-rules-on-enter.js +14 -0
  43. package/dist/chat-ui/composer/run-input-rules-on-enter.js.map +1 -0
  44. package/dist/chat-ui/composer/tiptap-composer.cjs +7 -4
  45. package/dist/chat-ui/composer/tiptap-composer.cjs.map +1 -1
  46. package/dist/chat-ui/composer/tiptap-composer.js +7 -4
  47. package/dist/chat-ui/composer/tiptap-composer.js.map +1 -1
  48. package/dist/chat-ui/markdown-storage.cjs +16 -0
  49. package/dist/chat-ui/markdown-storage.cjs.map +1 -0
  50. package/dist/chat-ui/markdown-storage.d.ts +21 -0
  51. package/dist/chat-ui/markdown-storage.js +16 -0
  52. package/dist/chat-ui/markdown-storage.js.map +1 -0
  53. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs +20 -11
  54. package/dist/chat-ui/mentions/suggestions/data/api-client.cjs.map +1 -1
  55. package/dist/chat-ui/mentions/suggestions/data/api-client.d.ts +11 -2
  56. package/dist/chat-ui/mentions/suggestions/data/api-client.js +20 -11
  57. package/dist/chat-ui/mentions/suggestions/data/api-client.js.map +1 -1
  58. package/dist/chat-ui/rich-text/chat-node-views.cjs.map +1 -1
  59. package/dist/chat-ui/rich-text/chat-node-views.js.map +1 -1
  60. package/dist/chat-ui/rich-text/chat-text-schema.cjs +15 -10
  61. package/dist/chat-ui/rich-text/chat-text-schema.cjs.map +1 -1
  62. package/dist/chat-ui/rich-text/chat-text-schema.js +11 -6
  63. package/dist/chat-ui/rich-text/chat-text-schema.js.map +1 -1
  64. package/dist/collab/mint.cjs +6 -15
  65. package/dist/collab/mint.cjs.map +1 -1
  66. package/dist/collab/mint.d.ts +8 -0
  67. package/dist/collab/mint.js +6 -15
  68. package/dist/collab/mint.js.map +1 -1
  69. package/dist/collab/presence-mint.cjs +20 -16
  70. package/dist/collab/presence-mint.cjs.map +1 -1
  71. package/dist/collab/presence-mint.js +21 -17
  72. package/dist/collab/presence-mint.js.map +1 -1
  73. package/dist/collab/presence-react.cjs +12 -5
  74. package/dist/collab/presence-react.cjs.map +1 -1
  75. package/dist/collab/presence-react.d.ts +3 -1
  76. package/dist/collab/presence-react.js +12 -5
  77. package/dist/collab/presence-react.js.map +1 -1
  78. package/dist/collab/react.cjs +5 -2
  79. package/dist/collab/react.cjs.map +1 -1
  80. package/dist/collab/react.js +5 -2
  81. package/dist/collab/react.js.map +1 -1
  82. package/dist/dictation/use-athena-dictation-engine.cjs +12 -11
  83. package/dist/dictation/use-athena-dictation-engine.cjs.map +1 -1
  84. package/dist/dictation/use-athena-dictation-engine.d.ts +4 -3
  85. package/dist/dictation/use-athena-dictation-engine.js +12 -11
  86. package/dist/dictation/use-athena-dictation-engine.js.map +1 -1
  87. package/dist/index.d.ts +1 -1
  88. package/dist/packages/agent-runtime/src/conversation-list.cjs +3 -0
  89. package/dist/packages/agent-runtime/src/conversation-list.cjs.map +1 -1
  90. package/dist/packages/agent-runtime/src/conversation-list.js +3 -0
  91. package/dist/packages/agent-runtime/src/conversation-list.js.map +1 -1
  92. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +2 -2
  93. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  94. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +2 -2
  95. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  96. package/dist/packages/agent-runtime/src/use-conversation-list.cjs +1 -0
  97. package/dist/packages/agent-runtime/src/use-conversation-list.cjs.map +1 -1
  98. package/dist/packages/agent-runtime/src/use-conversation-list.js +1 -0
  99. package/dist/packages/agent-runtime/src/use-conversation-list.js.map +1 -1
  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 +23 -2
  106. package/dist/provider/AthenaProvider.cjs.map +1 -1
  107. package/dist/provider/AthenaProvider.js +23 -2
  108. package/dist/provider/AthenaProvider.js.map +1 -1
  109. package/dist/runtime/auth-refusal-restart.cjs +66 -0
  110. package/dist/runtime/auth-refusal-restart.cjs.map +1 -0
  111. package/dist/runtime/auth-refusal-restart.d.ts +35 -0
  112. package/dist/runtime/auth-refusal-restart.js +66 -0
  113. package/dist/runtime/auth-refusal-restart.js.map +1 -0
  114. package/dist/runtime/auth.cjs +58 -21
  115. package/dist/runtime/auth.cjs.map +1 -1
  116. package/dist/runtime/auth.d.ts +11 -1
  117. package/dist/runtime/auth.js +59 -22
  118. package/dist/runtime/auth.js.map +1 -1
  119. package/dist/runtime/credential-renewal.cjs +29 -0
  120. package/dist/runtime/credential-renewal.cjs.map +1 -0
  121. package/dist/runtime/credential-renewal.d.ts +35 -0
  122. package/dist/runtime/credential-renewal.js +29 -0
  123. package/dist/runtime/credential-renewal.js.map +1 -0
  124. package/dist/runtime/statewire-run-config.cjs +1 -1
  125. package/dist/runtime/statewire-run-config.cjs.map +1 -1
  126. package/dist/runtime/statewire-run-config.d.ts +1 -1
  127. package/dist/runtime/statewire-run-config.js +1 -1
  128. package/dist/runtime/statewire-run-config.js.map +1 -1
  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/threads/api.cjs +5 -1
  135. package/dist/threads/api.cjs.map +1 -1
  136. package/dist/threads/api.js +5 -1
  137. package/dist/threads/api.js.map +1 -1
  138. package/dist/threads/conversation-list-fetcher.cjs +5 -5
  139. package/dist/threads/conversation-list-fetcher.cjs.map +1 -1
  140. package/dist/threads/conversation-list-fetcher.d.ts +6 -5
  141. package/dist/threads/conversation-list-fetcher.js +5 -5
  142. package/dist/threads/conversation-list-fetcher.js.map +1 -1
  143. package/dist/threads/statewire-thread-list.cjs +13 -14
  144. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  145. package/dist/threads/statewire-thread-list.js +13 -14
  146. package/dist/threads/statewire-thread-list.js.map +1 -1
  147. package/dist/threads/use-rest-conversation-history.cjs +6 -2
  148. package/dist/threads/use-rest-conversation-history.cjs.map +1 -1
  149. package/dist/threads/use-rest-conversation-history.d.ts +4 -1
  150. package/dist/threads/use-rest-conversation-history.js +7 -3
  151. package/dist/threads/use-rest-conversation-history.js.map +1 -1
  152. package/dist/threads/use-statewire-conversation-history.cjs +9 -2
  153. package/dist/threads/use-statewire-conversation-history.cjs.map +1 -1
  154. package/dist/threads/use-statewire-conversation-history.d.ts +5 -1
  155. package/dist/threads/use-statewire-conversation-history.js +10 -3
  156. package/dist/threads/use-statewire-conversation-history.js.map +1 -1
  157. package/dist/threads/use-thread-archive.cjs +2 -1
  158. package/dist/threads/use-thread-archive.cjs.map +1 -1
  159. package/dist/threads/use-thread-archive.d.ts +3 -1
  160. package/dist/threads/use-thread-archive.js +2 -1
  161. package/dist/threads/use-thread-archive.js.map +1 -1
  162. package/dist/threads/use-thread-favorite.cjs.map +1 -1
  163. package/dist/threads/use-thread-favorite.d.ts +3 -1
  164. package/dist/threads/use-thread-favorite.js.map +1 -1
  165. package/dist/threads/use-thread-field-override.cjs +2 -1
  166. package/dist/threads/use-thread-field-override.cjs.map +1 -1
  167. package/dist/threads/use-thread-field-override.d.ts +3 -1
  168. package/dist/threads/use-thread-field-override.js +2 -1
  169. package/dist/threads/use-thread-field-override.js.map +1 -1
  170. package/dist/threads/use-thread-rename.cjs.map +1 -1
  171. package/dist/threads/use-thread-rename.d.ts +3 -1
  172. package/dist/threads/use-thread-rename.js.map +1 -1
  173. package/dist/tools/append-document-tool-ui.cjs +5 -0
  174. package/dist/tools/append-document-tool-ui.cjs.map +1 -1
  175. package/dist/tools/append-document-tool-ui.js +5 -0
  176. package/dist/tools/append-document-tool-ui.js.map +1 -1
  177. package/dist/tools/tool-uis.cjs +11 -15
  178. package/dist/tools/tool-uis.cjs.map +1 -1
  179. package/dist/tools/tool-uis.js +11 -15
  180. package/dist/tools/tool-uis.js.map +1 -1
  181. package/dist/voice/use-athena-voice-session.cjs +17 -5
  182. package/dist/voice/use-athena-voice-session.cjs.map +1 -1
  183. package/dist/voice/use-athena-voice-session.d.ts +2 -2
  184. package/dist/voice/use-athena-voice-session.js +17 -5
  185. package/dist/voice/use-athena-voice-session.js.map +1 -1
  186. package/package.json +13 -16
@@ -1 +1 @@
1
- {"version":3,"file":"use-asset-embed.cjs","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n };\n}\n\n// Cache embed URLs to avoid re-generating tokens for the same asset\nconst embedCache = new Map<string, { url: string; expiresAt: number }>();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (!anchor || typeof anchor !== 'object' || !('type' in anchor) || typeof anchor.type !== 'string') {\n return;\n }\n\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n\n const excerpt = reference.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the effect dependency array. PropelAuth and similar providers refresh\n * the token value periodically (e.g. on window focus), but the underlying\n * session is the same — re-running the effect on every token rotation\n * would force the iframe to reload unnecessarily. The effect only re-runs\n * when the auth _mode_ changes (auth vs anonymous) or the asset changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n backendUrl,\n appUrl,\n apiKey,\n token,\n } = options;\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n // Keep the latest token in a ref so the effect can read it without\n // being re-triggered every time PropelAuth rotates the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n\n useEffect(() => {\n if (!assetId || !backendUrl) {\n setEmbedUrl(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n const cacheKey = `${assetId}:${readOnly}:${displayMode}:${authContext}:${backendUrl}:${appUrl ?? ''}`;\n const cached = embedCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now() / 1000) {\n setEmbedUrl(cached.url);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const endpoint = `${apiBaseUrl}/api/embed/generate-token`;\n\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setIsLoading(true);\n setError(null);\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n const currentToken = tokenRef.current;\n if (currentToken) {\n headers.Authorization = `Bearer ${currentToken}`;\n } else if (apiKey) {\n headers['X-API-KEY'] = apiKey;\n }\n\n fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n }),\n ),\n signal: controller.signal,\n })\n .then(async (res) => {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n return res.json();\n })\n .then((data: { embed_url: string; expires_at: number }) => {\n const resolvedEmbedUrl = resolveAssetEmbedUrl({\n embedUrl: data.embed_url,\n appUrl,\n backendUrl,\n });\n embedCache.set(cacheKey, {\n url: resolvedEmbedUrl,\n expiresAt: data.expires_at,\n });\n setEmbedUrl(resolvedEmbedUrl);\n setIsLoading(false);\n })\n .catch((err) => {\n if (err.name === 'AbortError') return;\n setError(err.message);\n setIsLoading(false);\n });\n\n return () => controller.abort();\n // Raw token intentionally excluded; authContext tracks user/tenant identity\n // while remaining stable across ordinary JWT refreshes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);\n\n return { embedUrl, isLoading, error };\n}\n"],"names":["deriveAthenaAppUrl","useState","useRef","useEffect"],"mappings":"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"}
1
+ {"version":3,"file":"use-asset-embed.cjs","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport { sendWithCredentialRenewal } from '../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n };\n}\n\n// Cache embed URLs to avoid re-generating tokens for the same asset\nconst embedCache = new Map<string, { url: string; expiresAt: number }>();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (!anchor || typeof anchor !== 'object' || !('type' in anchor) || typeof anchor.type !== 'string') {\n return;\n }\n\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n\n const excerpt = reference.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the effect dependency array. PropelAuth and similar providers refresh\n * the token value periodically (e.g. on window focus), but the underlying\n * session is the same — re-running the effect on every token rotation\n * would force the iframe to reload unnecessarily. The effect only re-runs\n * when the auth _mode_ changes (auth vs anonymous) or the asset changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n /**\n * Live token resolver (the provider's `config.getToken`) that renews the\n * held `token`: read before the embed-token request, and asked for a\n * renewed token (`force: true`) when the backend refuses it. Ignored when\n * no token is held, so the request always authenticates as the identity\n * the embed cache is keyed on.\n */\n getToken?: AthenaStatewireTokenGetter;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n } = options;\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n // Keep the latest token in a ref so the effect can read it without\n // being re-triggered every time PropelAuth rotates the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const getTokenRef = useRef(getToken);\n getTokenRef.current = getToken;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n\n useEffect(() => {\n if (!assetId || !backendUrl) {\n setEmbedUrl(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n const cacheKey = `${assetId}:${readOnly}:${displayMode}:${authContext}:${backendUrl}:${appUrl ?? ''}`;\n const cached = embedCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now() / 1000) {\n setEmbedUrl(cached.url);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const endpoint = `${apiBaseUrl}/api/embed/generate-token`;\n\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setIsLoading(true);\n setError(null);\n\n const heldToken = tokenRef.current;\n sendWithCredentialRenewal(\n { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : undefined },\n (headers) =>\n fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n }),\n ),\n signal: controller.signal,\n }),\n )\n .then(async (res) => {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n return res.json();\n })\n .then((data: { embed_url: string; expires_at: number }) => {\n const resolvedEmbedUrl = resolveAssetEmbedUrl({\n embedUrl: data.embed_url,\n appUrl,\n backendUrl,\n });\n embedCache.set(cacheKey, {\n url: resolvedEmbedUrl,\n expiresAt: data.expires_at,\n });\n setEmbedUrl(resolvedEmbedUrl);\n setIsLoading(false);\n })\n .catch((err) => {\n if (err.name === 'AbortError') return;\n setError(err.message);\n setIsLoading(false);\n });\n\n return () => controller.abort();\n // Raw token intentionally excluded; authContext tracks user/tenant identity\n // while remaining stable across ordinary JWT refreshes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);\n\n return { embedUrl, isLoading, error };\n}\n"],"names":["deriveAthenaAppUrl","useState","useRef","useEffect","sendWithCredentialRenewal"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';
1
2
  import type { AthenaAssetCitationReference } from './asset-embed-messages';
2
3
  export interface UseAssetEmbedOptions {
3
4
  readOnly?: boolean;
@@ -54,4 +55,12 @@ export declare function useAssetEmbed(assetId: string | null, options?: UseAsset
54
55
  appUrl?: string;
55
56
  apiKey?: string;
56
57
  token?: string | null;
58
+ /**
59
+ * Live token resolver (the provider's `config.getToken`) that renews the
60
+ * held `token`: read before the embed-token request, and asked for a
61
+ * renewed token (`force: true`) when the backend refuses it. Ignored when
62
+ * no token is held, so the request always authenticates as the identity
63
+ * the embed cache is keyed on.
64
+ */
65
+ getToken?: AthenaStatewireTokenGetter;
57
66
  }): UseAssetEmbedResult;
@@ -1,5 +1,6 @@
1
1
  import { useState, useRef, useEffect } from "react";
2
2
  import { deriveAthenaAppUrl } from "../lib/athena-urls.js";
3
+ import { sendWithCredentialRenewal } from "../runtime/credential-renewal.js";
3
4
  const DEFAULT_ASSET_EMBED_READ_ONLY = false;
4
5
  const DEFAULT_ASSET_EMBED_DISPLAY_MODE = "full";
5
6
  function buildAssetEmbedRequestBody({
@@ -277,7 +278,8 @@ function useAssetEmbed(assetId, options = {
277
278
  backendUrl,
278
279
  appUrl,
279
280
  apiKey,
280
- token
281
+ token,
282
+ getToken
281
283
  } = options;
282
284
  const [embedUrl, setEmbedUrl] = useState(null);
283
285
  const [isLoading, setIsLoading] = useState(false);
@@ -285,6 +287,8 @@ function useAssetEmbed(assetId, options = {
285
287
  const abortRef = useRef(null);
286
288
  const tokenRef = useRef(token);
287
289
  tokenRef.current = token;
290
+ const getTokenRef = useRef(getToken);
291
+ getTokenRef.current = getToken;
288
292
  const authContext = getAssetEmbedAuthContext({ token, apiKey });
289
293
  useEffect(() => {
290
294
  var _a;
@@ -309,28 +313,23 @@ function useAssetEmbed(assetId, options = {
309
313
  abortRef.current = controller;
310
314
  setIsLoading(true);
311
315
  setError(null);
312
- const headers = {
313
- "Content-Type": "application/json"
314
- };
315
- const currentToken = tokenRef.current;
316
- if (currentToken) {
317
- headers.Authorization = `Bearer ${currentToken}`;
318
- } else if (apiKey) {
319
- headers["X-API-KEY"] = apiKey;
320
- }
321
- fetch(endpoint, {
322
- method: "POST",
323
- headers,
324
- body: JSON.stringify(
325
- buildAssetEmbedRequestBody({
326
- assetId,
327
- readOnly,
328
- expiresInSeconds,
329
- displayMode
330
- })
331
- ),
332
- signal: controller.signal
333
- }).then(async (res) => {
316
+ const heldToken = tokenRef.current;
317
+ sendWithCredentialRenewal(
318
+ { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : void 0 },
319
+ (headers) => fetch(endpoint, {
320
+ method: "POST",
321
+ headers: { "Content-Type": "application/json", ...headers },
322
+ body: JSON.stringify(
323
+ buildAssetEmbedRequestBody({
324
+ assetId,
325
+ readOnly,
326
+ expiresInSeconds,
327
+ displayMode
328
+ })
329
+ ),
330
+ signal: controller.signal
331
+ })
332
+ ).then(async (res) => {
334
333
  if (!res.ok) {
335
334
  const text = await res.text().catch(() => "");
336
335
  throw new Error(`Failed to generate embed token (${res.status}): ${text}`);
@@ -1 +1 @@
1
- {"version":3,"file":"use-asset-embed.js","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n };\n}\n\n// Cache embed URLs to avoid re-generating tokens for the same asset\nconst embedCache = new Map<string, { url: string; expiresAt: number }>();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (!anchor || typeof anchor !== 'object' || !('type' in anchor) || typeof anchor.type !== 'string') {\n return;\n }\n\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n\n const excerpt = reference.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the effect dependency array. PropelAuth and similar providers refresh\n * the token value periodically (e.g. on window focus), but the underlying\n * session is the same — re-running the effect on every token rotation\n * would force the iframe to reload unnecessarily. The effect only re-runs\n * when the auth _mode_ changes (auth vs anonymous) or the asset changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n backendUrl,\n appUrl,\n apiKey,\n token,\n } = options;\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n // Keep the latest token in a ref so the effect can read it without\n // being re-triggered every time PropelAuth rotates the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n\n useEffect(() => {\n if (!assetId || !backendUrl) {\n setEmbedUrl(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n const cacheKey = `${assetId}:${readOnly}:${displayMode}:${authContext}:${backendUrl}:${appUrl ?? ''}`;\n const cached = embedCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now() / 1000) {\n setEmbedUrl(cached.url);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const endpoint = `${apiBaseUrl}/api/embed/generate-token`;\n\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setIsLoading(true);\n setError(null);\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n const currentToken = tokenRef.current;\n if (currentToken) {\n headers.Authorization = `Bearer ${currentToken}`;\n } else if (apiKey) {\n headers['X-API-KEY'] = apiKey;\n }\n\n fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n }),\n ),\n signal: controller.signal,\n })\n .then(async (res) => {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n return res.json();\n })\n .then((data: { embed_url: string; expires_at: number }) => {\n const resolvedEmbedUrl = resolveAssetEmbedUrl({\n embedUrl: data.embed_url,\n appUrl,\n backendUrl,\n });\n embedCache.set(cacheKey, {\n url: resolvedEmbedUrl,\n expiresAt: data.expires_at,\n });\n setEmbedUrl(resolvedEmbedUrl);\n setIsLoading(false);\n })\n .catch((err) => {\n if (err.name === 'AbortError') return;\n setError(err.message);\n setIsLoading(false);\n });\n\n return () => controller.abort();\n // Raw token intentionally excluded; authContext tracks user/tenant identity\n // while remaining stable across ordinary JWT refreshes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);\n\n return { embedUrl, isLoading, error };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-asset-embed.js","sources":["../../src/assets/use-asset-embed.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { deriveAthenaAppUrl } from '../lib/athena-urls';\nimport { sendWithCredentialRenewal } from '../runtime/credential-renewal';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport type { AthenaAssetCitationReference } from './asset-embed-messages';\n\nexport interface UseAssetEmbedOptions {\n readOnly?: boolean;\n expiresInSeconds?: number;\n /** Native Olympus surface rendered by the iframe. Defaults to `full`. */\n displayMode?: 'minimal' | 'full';\n}\n\nexport const DEFAULT_ASSET_EMBED_READ_ONLY = false;\nexport const DEFAULT_ASSET_EMBED_DISPLAY_MODE = 'full' as const;\n\nexport interface UseAssetEmbedResult {\n embedUrl: string | null;\n isLoading: boolean;\n error: string | null;\n}\n\nexport function buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n}: {\n assetId: string;\n readOnly: boolean;\n expiresInSeconds: number;\n displayMode: 'minimal' | 'full';\n}) {\n return {\n asset_id: assetId,\n read_only: readOnly,\n expires_in_seconds: expiresInSeconds,\n display_mode: displayMode,\n };\n}\n\n// Cache embed URLs to avoid re-generating tokens for the same asset\nconst embedCache = new Map<string, { url: string; expiresAt: number }>();\n\nconst ATHENA_APP_HOSTNAMES = new Set(['app.athenaintel.com', 'staging-app.athenaintel.com']);\nconst ATHENA_PREVIEW_HOSTNAME_SUFFIX = '.previews.athenaintel.com';\nconst ATHENA_WORKSPACE_HOSTNAME_PATTERN = /^.+--(\\d+)\\.(?:app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/;\nconst OLYMPUS_WORKSPACE_PORT = '8082';\n\nconst getUrl = (value?: string | null): URL | null => {\n if (!value) {\n return null;\n }\n\n try {\n return new URL(value);\n } catch {\n return null;\n }\n};\n\nconst getCurrentOrigin = (): string | null => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.location.origin;\n};\n\nconst getOrigin = (value?: string | null): string | null => getUrl(value)?.origin ?? null;\n\nconst getAthenaWorkspacePort = (url: URL): string | null => {\n const match = url.hostname.toLowerCase().match(ATHENA_WORKSPACE_HOSTNAME_PATTERN);\n return match?.[1] ?? null;\n};\n\nconst isEmbedCapableAthenaHost = (url: URL): boolean => {\n const hostname = url.hostname.toLowerCase();\n\n if (ATHENA_APP_HOSTNAMES.has(hostname) || hostname.endsWith(ATHENA_PREVIEW_HOSTNAME_SUFFIX)) {\n return true;\n }\n\n return getAthenaWorkspacePort(url) === OLYMPUS_WORKSPACE_PORT;\n};\n\nconst shouldPreferDerivedEmbedAppUrl = ({\n appUrl,\n derivedAppUrl,\n currentOrigin,\n}: {\n appUrl: string;\n derivedAppUrl: string | null;\n currentOrigin?: string | null;\n}): boolean => {\n if (!derivedAppUrl) {\n return false;\n }\n\n const resolvedAppUrl = getUrl(appUrl);\n if (!resolvedAppUrl) {\n return true;\n }\n\n const workspacePort = getAthenaWorkspacePort(resolvedAppUrl);\n if (workspacePort && workspacePort !== OLYMPUS_WORKSPACE_PORT) {\n return true;\n }\n\n if (isEmbedCapableAthenaHost(resolvedAppUrl)) {\n return false;\n }\n\n const resolvedCurrentOrigin = getOrigin(currentOrigin ?? getCurrentOrigin());\n return !!resolvedCurrentOrigin && resolvedAppUrl.origin === resolvedCurrentOrigin;\n};\n\nconst resolveEmbedAppUrl = ({\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string | null => {\n const derivedAppUrl = deriveAthenaAppUrl({ backendUrl });\n\n if (!appUrl) {\n return derivedAppUrl;\n }\n\n if (shouldPreferDerivedEmbedAppUrl({ appUrl, derivedAppUrl, currentOrigin })) {\n return derivedAppUrl;\n }\n\n return appUrl;\n};\n\nexport function resolveAssetEmbedUrl({\n embedUrl,\n appUrl,\n backendUrl,\n currentOrigin,\n}: {\n embedUrl: string;\n appUrl?: string;\n backendUrl?: string | null;\n currentOrigin?: string | null;\n}): string {\n try {\n const resolvedEmbedUrl = new URL(embedUrl);\n if (!resolvedEmbedUrl.pathname.startsWith('/embed/')) {\n return embedUrl;\n }\n\n const embedAppUrl = resolveEmbedAppUrl({\n appUrl,\n backendUrl,\n currentOrigin,\n });\n if (!embedAppUrl) {\n return embedUrl;\n }\n\n const resolvedAppUrl = new URL(embedAppUrl);\n resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;\n resolvedEmbedUrl.host = resolvedAppUrl.host;\n return resolvedEmbedUrl.toString();\n } catch {\n return embedUrl;\n }\n}\n\nfunction setFiniteNumberParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'number' && Number.isFinite(value)) {\n params.set(key, String(value));\n }\n}\n\nfunction setNonEmptyStringParam({ params, key, value }: { params: URLSearchParams; key: string; value: unknown }): void {\n if (typeof value === 'string' && value.length > 0) {\n params.set(key, value);\n }\n}\n\nexport function appendCitationReferenceParams({\n params,\n reference,\n}: {\n params: URLSearchParams;\n reference?: AthenaAssetCitationReference;\n}): void {\n const anchor = reference?.anchor;\n if (!anchor || typeof anchor !== 'object' || !('type' in anchor) || typeof anchor.type !== 'string') {\n return;\n }\n\n switch (anchor.type) {\n case 'page':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n break;\n case 'page_range':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'start_page',\n value: 'startPage' in anchor ? anchor.startPage : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'end_page',\n value: 'endPage' in anchor ? anchor.endPage : undefined,\n });\n break;\n case 'page_rect':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'x',\n value: 'x' in anchor ? anchor.x : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'y',\n value: 'y' in anchor ? anchor.y : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'width',\n value: 'width' in anchor ? anchor.width : undefined,\n });\n setFiniteNumberParam({\n params,\n key: 'height',\n value: 'height' in anchor ? anchor.height : undefined,\n });\n if (\n 'unit' in anchor &&\n (anchor.unit === 'percent' || anchor.unit === 'point')\n ) {\n params.set('unit', anchor.unit);\n }\n break;\n case 'page_text':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'page',\n value: 'page' in anchor ? anchor.page : undefined,\n });\n if ('text' in anchor && typeof anchor.text === 'string') {\n params.set('text', anchor.text);\n }\n // Optional context that disambiguates a repeated phrase, and the hash of\n // the cited unit's normalized text; the embed parses both with the shared\n // `@athenaintel/references` parser.\n setNonEmptyStringParam({ params, key: 'prefix', value: 'prefix' in anchor ? anchor.prefix : undefined });\n setNonEmptyStringParam({ params, key: 'suffix', value: 'suffix' in anchor ? anchor.suffix : undefined });\n setNonEmptyStringParam({ params, key: 'hash', value: 'hash' in anchor ? anchor.hash : undefined });\n break;\n // Sheet anchors ride the same query contract the embed page parses with\n // the shared `@athenaintel/references` parser.\n case 'sheet_cell':\n case 'sheet_range':\n case 'sheet_tab':\n case 'sheet_table':\n params.set('anchor', anchor.type);\n setFiniteNumberParam({\n params,\n key: 'sheet_id',\n value: 'sheetId' in anchor ? anchor.sheetId : undefined,\n });\n if ('cell' in anchor && typeof anchor.cell === 'string') {\n params.set('cell', anchor.cell);\n }\n if ('range' in anchor && typeof anchor.range === 'string') {\n params.set('range', anchor.range);\n }\n if ('tableName' in anchor && typeof anchor.tableName === 'string') {\n params.set('table_name', anchor.tableName);\n }\n if ('fallbackRange' in anchor && typeof anchor.fallbackRange === 'string') {\n params.set('fallback_range', anchor.fallbackRange);\n }\n break;\n }\n\n const excerpt = reference.meta?.excerpt;\n if (typeof excerpt === 'string' && excerpt) {\n params.set('excerpt', excerpt);\n }\n}\n\nexport function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string {\n try {\n const url = new URL(embedUrl);\n url.searchParams.set('athena_sdk_bridge', '1');\n appendCitationReferenceParams({ params: url.searchParams, reference });\n return url.toString();\n } catch {\n return embedUrl;\n }\n}\n\nconst getJwtPayload = (token: string): Record<string, unknown> | null => {\n try {\n const payloadSegment = token.split('.')[1];\n if (!payloadSegment || typeof atob !== 'function') return null;\n const normalized = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');\n const payload: unknown = JSON.parse(\n atob(normalized.padEnd(normalized.length + ((4 - (normalized.length % 4)) % 4), '=')),\n );\n return payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n};\n\nexport function getAssetEmbedAuthContext({ token, apiKey }: { token?: string | null; apiKey?: string }): string {\n if (!token) return apiKey ? `api-key:${apiKey}` : 'anonymous';\n\n const payload = getJwtPayload(token);\n const userId = payload?.user_id ?? payload?.sub;\n if (typeof userId !== 'string' || !userId) return `opaque-token:${token}`;\n\n return `jwt:${JSON.stringify({\n issuer: payload?.iss ?? null,\n userId,\n activeOrgId: payload?.active_org_id ?? payload?.org_id ?? null,\n workspaceId: payload?.workspace_id ?? null,\n orgMemberships: payload?.org_id_to_org_member_info ?? null,\n })}`;\n}\n\n/**\n * Hook to generate an embed URL for rendering an asset in an iframe.\n * Calls the Athena embed token endpoint.\n *\n * Note: The auth token is read via a ref and is intentionally NOT part\n * of the effect dependency array. PropelAuth and similar providers refresh\n * the token value periodically (e.g. on window focus), but the underlying\n * session is the same — re-running the effect on every token rotation\n * would force the iframe to reload unnecessarily. The effect only re-runs\n * when the auth _mode_ changes (auth vs anonymous) or the asset changes.\n */\nexport function useAssetEmbed(\n assetId: string | null,\n options: UseAssetEmbedOptions & {\n backendUrl: string;\n appUrl?: string;\n apiKey?: string;\n token?: string | null;\n /**\n * Live token resolver (the provider's `config.getToken`) that renews the\n * held `token`: read before the embed-token request, and asked for a\n * renewed token (`force: true`) when the backend refuses it. Ignored when\n * no token is held, so the request always authenticates as the identity\n * the embed cache is keyed on.\n */\n getToken?: AthenaStatewireTokenGetter;\n } = {\n backendUrl: '',\n },\n): UseAssetEmbedResult {\n const {\n readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,\n expiresInSeconds = 60 * 60 * 24 * 30,\n displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,\n backendUrl,\n appUrl,\n apiKey,\n token,\n getToken,\n } = options;\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n // Keep the latest token in a ref so the effect can read it without\n // being re-triggered every time PropelAuth rotates the token value.\n const tokenRef = useRef(token);\n tokenRef.current = token;\n const getTokenRef = useRef(getToken);\n getTokenRef.current = getToken;\n\n // Stable across normal JWT rotations, but changes when the user or tenant\n // changes so a signed URL can never be reused across auth boundaries.\n const authContext = getAssetEmbedAuthContext({ token, apiKey });\n\n useEffect(() => {\n if (!assetId || !backendUrl) {\n setEmbedUrl(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n const cacheKey = `${assetId}:${readOnly}:${displayMode}:${authContext}:${backendUrl}:${appUrl ?? ''}`;\n const cached = embedCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now() / 1000) {\n setEmbedUrl(cached.url);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n // Derive the Athena API base from backendUrl before calling the embed endpoint.\n const apiBaseUrl = backendUrl.replace(/\\/api\\/assistant-ui\\/?$/, '');\n const endpoint = `${apiBaseUrl}/api/embed/generate-token`;\n\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setIsLoading(true);\n setError(null);\n\n const heldToken = tokenRef.current;\n sendWithCredentialRenewal(\n { apiKey, token: heldToken, getToken: heldToken ? getTokenRef.current : undefined },\n (headers) =>\n fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify(\n buildAssetEmbedRequestBody({\n assetId,\n readOnly,\n expiresInSeconds,\n displayMode,\n }),\n ),\n signal: controller.signal,\n }),\n )\n .then(async (res) => {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`Failed to generate embed token (${res.status}): ${text}`);\n }\n return res.json();\n })\n .then((data: { embed_url: string; expires_at: number }) => {\n const resolvedEmbedUrl = resolveAssetEmbedUrl({\n embedUrl: data.embed_url,\n appUrl,\n backendUrl,\n });\n embedCache.set(cacheKey, {\n url: resolvedEmbedUrl,\n expiresAt: data.expires_at,\n });\n setEmbedUrl(resolvedEmbedUrl);\n setIsLoading(false);\n })\n .catch((err) => {\n if (err.name === 'AbortError') return;\n setError(err.message);\n setIsLoading(false);\n });\n\n return () => controller.abort();\n // Raw token intentionally excluded; authContext tracks user/tenant identity\n // while remaining stable across ordinary JWT refreshes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [assetId, readOnly, expiresInSeconds, displayMode, backendUrl, appUrl, authContext, apiKey]);\n\n return { embedUrl, isLoading, error };\n}\n"],"names":[],"mappings":"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"}
@@ -4,6 +4,27 @@ const jsxRuntime = require("react/jsx-runtime");
4
4
  const react = require("@propelauth/react");
5
5
  const react$1 = require("react");
6
6
  const AthenaAuthContext = require("./AthenaAuthContext.cjs");
7
+ function propelAuthTokenGetter(read) {
8
+ return async ({ force = false } = {}) => {
9
+ var _a, _b, _c;
10
+ const info = read();
11
+ if (info.loading || !info.isLoggedIn) return null;
12
+ if (force) {
13
+ try {
14
+ await ((_a = info.refreshAuthInfo) == null ? void 0 : _a.call(info));
15
+ } catch {
16
+ }
17
+ }
18
+ const latest = read();
19
+ let fresh;
20
+ try {
21
+ fresh = await ((_c = (_b = latest.tokens) == null ? void 0 : _b.getAccessToken) == null ? void 0 : _c.call(_b));
22
+ } catch {
23
+ fresh = void 0;
24
+ }
25
+ return fresh ?? (latest.isLoggedIn ? latest.accessToken ?? null : null);
26
+ };
27
+ }
7
28
  const ATHENA_LOCATION_CHANGE_EVENT = "athena:location-change";
8
29
  let historyPatched = false;
9
30
  function patchHistoryForLocationTracking() {
@@ -60,6 +81,9 @@ function AthenaAuthBridge({
60
81
  const logoutFnRef = react$1.useRef(logoutFn);
61
82
  logoutFnRef.current = logoutFn;
62
83
  const prevAuthStateRef = react$1.useRef(null);
84
+ const authInfoRef = react$1.useRef(authInfo);
85
+ authInfoRef.current = authInfo;
86
+ const [getToken] = react$1.useState(() => propelAuthTokenGetter(() => authInfoRef.current));
63
87
  const authState = react$1.useMemo(() => {
64
88
  var _a, _b;
65
89
  if (authInfo.loading) {
@@ -71,7 +95,8 @@ function AthenaAuthBridge({
71
95
  accessToken: null,
72
96
  orgs: [],
73
97
  logout: async () => {
74
- }
98
+ },
99
+ getToken
75
100
  };
76
101
  }
77
102
  if (!authInfo.isLoggedIn) {
@@ -82,7 +107,8 @@ function AthenaAuthBridge({
82
107
  user: null,
83
108
  accessToken: null,
84
109
  orgs: [],
85
- logout: async (redirect = true) => logoutFnRef.current(redirect)
110
+ logout: async (redirect = true) => logoutFnRef.current(redirect),
111
+ getToken
86
112
  };
87
113
  }
88
114
  const prev = prevAuthStateRef.current;
@@ -109,11 +135,12 @@ function AthenaAuthBridge({
109
135
  user,
110
136
  accessToken: authInfo.accessToken,
111
137
  orgs,
112
- logout: async (redirect = true) => logoutFnRef.current(redirect)
138
+ logout: async (redirect = true) => logoutFnRef.current(redirect),
139
+ getToken
113
140
  };
114
141
  prevAuthStateRef.current = newState;
115
142
  return newState;
116
- }, [authInfo]);
143
+ }, [authInfo, getToken]);
117
144
  if (authState.isLoading) {
118
145
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: displayWhileLoading ?? null });
119
146
  }
@@ -138,4 +165,5 @@ function AthenaAuthProvider({
138
165
  );
139
166
  }
140
167
  exports.AthenaAuthProvider = AthenaAuthProvider;
168
+ exports.propelAuthTokenGetter = propelAuthTokenGetter;
141
169
  //# sourceMappingURL=AthenaAuthProvider.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"AthenaAuthProvider.cjs","sources":["../../src/auth/AthenaAuthProvider.tsx"],"sourcesContent":["import {\n RequiredAuthProvider,\n RedirectToLogin,\n useAuthInfo,\n useLogoutFunction,\n} from '@propelauth/react';\nimport type { ReactNode } from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { AthenaAuthContext } from './AthenaAuthContext';\nimport type { AthenaAuthState, AthenaOrg, AthenaUser } from './types';\n\n// ─── Props ────────────────────────────────────────────────────────────\n\nexport interface AthenaAuthProviderProps {\n children: ReactNode;\n\n /**\n * Your PropelAuth auth URL (e.g. `https://auth.yourdomain.com`).\n * Found in the PropelAuth dashboard under Frontend Integration.\n */\n authUrl: string;\n\n /**\n * Where to redirect after a successful login.\n * Defaults to the current page URL.\n */\n postLoginRedirectUrl?: string;\n\n /**\n * Custom element displayed while the auth state is loading.\n * Defaults to `null` (renders nothing).\n */\n displayWhileLoading?: ReactNode;\n}\n\nconst ATHENA_LOCATION_CHANGE_EVENT = 'athena:location-change';\nlet historyPatched = false;\n\nfunction patchHistoryForLocationTracking(): void {\n if (historyPatched || typeof window === 'undefined') {\n return;\n }\n\n const notifyLocationChange = () => {\n window.dispatchEvent(new Event(ATHENA_LOCATION_CHANGE_EVENT));\n };\n\n const originalPushState = window.history.pushState;\n window.history.pushState = function pushState(...args) {\n const result = originalPushState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n const originalReplaceState = window.history.replaceState;\n window.history.replaceState = function replaceState(...args) {\n const result = originalReplaceState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n historyPatched = true;\n}\n\nfunction useCurrentHref(): string {\n const [href, setHref] = useState(\n typeof window !== 'undefined' ? window.location.href : '/',\n );\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n patchHistoryForLocationTracking();\n\n const updateHref = () => {\n setHref(window.location.href);\n };\n\n updateHref();\n window.addEventListener('popstate', updateHref);\n window.addEventListener('hashchange', updateHref);\n window.addEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n\n return () => {\n window.removeEventListener('popstate', updateHref);\n window.removeEventListener('hashchange', updateHref);\n window.removeEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n };\n }, []);\n\n return href;\n}\n\n// ─── Internal Bridge ──────────────────────────────────────────────────\n\n/**\n * Reads PropelAuth state via hooks and maps it into the SDK's\n * `AthenaAuthState`, which is placed on `AthenaAuthContext`.\n */\nfunction AthenaAuthBridge({\n children,\n displayWhileLoading,\n}: {\n children: ReactNode;\n displayWhileLoading?: ReactNode;\n}) {\n const authInfo = useAuthInfo();\n const logoutFn = useLogoutFunction();\n const logoutFnRef = useRef(logoutFn);\n logoutFnRef.current = logoutFn;\n const prevAuthStateRef = useRef<AthenaAuthState | null>(null);\n\n const authState: AthenaAuthState = useMemo(() => {\n if (authInfo.loading) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: true,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async () => {},\n };\n }\n\n if (!authInfo.isLoggedIn) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: false,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async (redirect = true) => logoutFnRef.current(redirect),\n };\n }\n\n // Referential stability: if the token and user identity haven't changed,\n // return the previous state object to prevent cascading context re-renders\n // from PropelAuth's focus-triggered token checks.\n const prev = prevAuthStateRef.current;\n if (\n prev?.isLoggedIn &&\n !prev.isLoading &&\n prev.accessToken === authInfo.accessToken &&\n prev.user?.userId === authInfo.user.userId\n ) {\n return prev;\n }\n\n const user: AthenaUser = {\n userId: authInfo.user.userId,\n email: authInfo.user.email,\n firstName: authInfo.user.firstName ?? undefined,\n lastName: authInfo.user.lastName ?? undefined,\n username: authInfo.user.username ?? undefined,\n pictureUrl: authInfo.user.pictureUrl ?? undefined,\n properties: authInfo.user.properties,\n };\n\n const orgs: AthenaOrg[] =\n authInfo.orgHelper?.getOrgs().map((org) => ({\n orgId: org.orgId,\n orgName: org.orgName,\n urlSafeOrgName: org.urlSafeOrgName,\n })) ?? [];\n\n const newState: AthenaAuthState = {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken: authInfo.accessToken,\n orgs,\n logout: async (redirect = true) => logoutFnRef.current(redirect),\n };\n prevAuthStateRef.current = newState;\n return newState;\n // logoutFn intentionally excluded — captured via logoutFnRef for\n // referential stability while always calling the latest function.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [authInfo]);\n\n if (authState.isLoading) {\n return <>{displayWhileLoading ?? null}</>;\n }\n\n return (\n <AthenaAuthContext.Provider value={authState}>\n {children}\n </AthenaAuthContext.Provider>\n );\n}\n\n// ─── Public Provider ──────────────────────────────────────────────────\n\n/**\n * Wraps your app with Athena authentication powered by PropelAuth.\n *\n * Place this **above** `<AthenaProvider>` in the component tree.\n * When present, `AthenaProvider` will automatically use the access token\n * from this provider — no need to pass `token` or `apiKey` manually.\n *\n * @example\n * ```tsx\n * import { AthenaAuthProvider } from '@athenaintel/react/auth';\n * import { AthenaProvider, AthenaChat } from '@athenaintel/react';\n *\n * function App() {\n * return (\n * <AthenaAuthProvider authUrl=\"https://auth.yourdomain.com\">\n * <AthenaProvider>\n * <AthenaChat />\n * </AthenaProvider>\n * </AthenaAuthProvider>\n * );\n * }\n * ```\n */\nexport function AthenaAuthProvider({\n children,\n authUrl,\n postLoginRedirectUrl,\n displayWhileLoading,\n}: AthenaAuthProviderProps) {\n const currentHref = useCurrentHref();\n const redirectUrl = postLoginRedirectUrl ?? currentHref;\n\n return (\n <RequiredAuthProvider\n authUrl={authUrl}\n displayWhileLoading={\n displayWhileLoading ? <>{displayWhileLoading}</> : undefined\n }\n displayIfLoggedOut={<RedirectToLogin postLoginRedirectUrl={redirectUrl} />}\n >\n <AthenaAuthBridge displayWhileLoading={displayWhileLoading}>\n {children}\n </AthenaAuthBridge>\n </RequiredAuthProvider>\n );\n}\n"],"names":["useState","useEffect","useAuthInfo","useLogoutFunction","useRef","useMemo","jsx","Fragment","AthenaAuthContext","RequiredAuthProvider","RedirectToLogin"],"mappings":"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"}
1
+ {"version":3,"file":"AthenaAuthProvider.cjs","sources":["../../src/auth/AthenaAuthProvider.tsx"],"sourcesContent":["import {\n RequiredAuthProvider,\n RedirectToLogin,\n useAuthInfo,\n useLogoutFunction,\n} from '@propelauth/react';\nimport type { ReactNode } from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport { AthenaAuthContext } from './AthenaAuthContext';\nimport type { AthenaAuthState, AthenaOrg, AthenaUser } from './types';\n\n/** The part of a PropelAuth `useAuthInfo()` snapshot the token getter reads. */\nexport interface PropelAuthTokenSnapshot {\n loading: boolean;\n isLoggedIn: boolean | undefined;\n accessToken: string | null | undefined;\n /** Absent on `@propelauth/react` releases that predate it. */\n refreshAuthInfo?: () => Promise<void>;\n /** Absent on `@propelauth/react` releases that predate it. */\n tokens?: { getAccessToken?: () => Promise<string | undefined> };\n}\n\n/**\n * The PropelAuth side of the SDK's credential source. `force` (the previous\n * token was refused) makes PropelAuth mint a new token first; either way the\n * token comes from PropelAuth's own `getAccessToken`, which renews one that is\n * about to expire. A failed renewal keeps the held token, so the caller's\n * attempt is refused by the host rather than failing in the client.\n */\nexport function propelAuthTokenGetter(\n read: () => PropelAuthTokenSnapshot,\n): AthenaStatewireTokenGetter {\n return async ({ force = false } = {}) => {\n const info = read();\n if (info.loading || !info.isLoggedIn) return null;\n if (force) {\n try {\n await info.refreshAuthInfo?.();\n } catch {\n // Keep the held token; the host decides.\n }\n }\n const latest = read();\n let fresh: string | undefined;\n try {\n fresh = await latest.tokens?.getAccessToken?.();\n } catch {\n fresh = undefined;\n }\n return fresh ?? (latest.isLoggedIn ? (latest.accessToken ?? null) : null);\n };\n}\n\n// ─── Props ────────────────────────────────────────────────────────────\n\nexport interface AthenaAuthProviderProps {\n children: ReactNode;\n\n /**\n * Your PropelAuth auth URL (e.g. `https://auth.yourdomain.com`).\n * Found in the PropelAuth dashboard under Frontend Integration.\n */\n authUrl: string;\n\n /**\n * Where to redirect after a successful login.\n * Defaults to the current page URL.\n */\n postLoginRedirectUrl?: string;\n\n /**\n * Custom element displayed while the auth state is loading.\n * Defaults to `null` (renders nothing).\n */\n displayWhileLoading?: ReactNode;\n}\n\nconst ATHENA_LOCATION_CHANGE_EVENT = 'athena:location-change';\nlet historyPatched = false;\n\nfunction patchHistoryForLocationTracking(): void {\n if (historyPatched || typeof window === 'undefined') {\n return;\n }\n\n const notifyLocationChange = () => {\n window.dispatchEvent(new Event(ATHENA_LOCATION_CHANGE_EVENT));\n };\n\n const originalPushState = window.history.pushState;\n window.history.pushState = function pushState(...args) {\n const result = originalPushState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n const originalReplaceState = window.history.replaceState;\n window.history.replaceState = function replaceState(...args) {\n const result = originalReplaceState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n historyPatched = true;\n}\n\nfunction useCurrentHref(): string {\n const [href, setHref] = useState(\n typeof window !== 'undefined' ? window.location.href : '/',\n );\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n patchHistoryForLocationTracking();\n\n const updateHref = () => {\n setHref(window.location.href);\n };\n\n updateHref();\n window.addEventListener('popstate', updateHref);\n window.addEventListener('hashchange', updateHref);\n window.addEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n\n return () => {\n window.removeEventListener('popstate', updateHref);\n window.removeEventListener('hashchange', updateHref);\n window.removeEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n };\n }, []);\n\n return href;\n}\n\n// ─── Internal Bridge ──────────────────────────────────────────────────\n\n/**\n * Reads PropelAuth state via hooks and maps it into the SDK's\n * `AthenaAuthState`, which is placed on `AthenaAuthContext`.\n */\nfunction AthenaAuthBridge({\n children,\n displayWhileLoading,\n}: {\n children: ReactNode;\n displayWhileLoading?: ReactNode;\n}) {\n const authInfo = useAuthInfo();\n const logoutFn = useLogoutFunction();\n const logoutFnRef = useRef(logoutFn);\n logoutFnRef.current = logoutFn;\n const prevAuthStateRef = useRef<AthenaAuthState | null>(null);\n const authInfoRef = useRef<PropelAuthTokenSnapshot>(authInfo);\n authInfoRef.current = authInfo;\n // One getter for the provider's lifetime: it reads the latest PropelAuth\n // snapshot, so its identity never changes and never re-renders consumers.\n const [getToken] = useState(() => propelAuthTokenGetter(() => authInfoRef.current));\n\n const authState: AthenaAuthState = useMemo(() => {\n if (authInfo.loading) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: true,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async () => {},\n getToken,\n };\n }\n\n if (!authInfo.isLoggedIn) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: false,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async (redirect = true) => logoutFnRef.current(redirect),\n getToken,\n };\n }\n\n // Referential stability: if the token and user identity haven't changed,\n // return the previous state object to prevent cascading context re-renders\n // from PropelAuth's focus-triggered token checks.\n const prev = prevAuthStateRef.current;\n if (\n prev?.isLoggedIn &&\n !prev.isLoading &&\n prev.accessToken === authInfo.accessToken &&\n prev.user?.userId === authInfo.user.userId\n ) {\n return prev;\n }\n\n const user: AthenaUser = {\n userId: authInfo.user.userId,\n email: authInfo.user.email,\n firstName: authInfo.user.firstName ?? undefined,\n lastName: authInfo.user.lastName ?? undefined,\n username: authInfo.user.username ?? undefined,\n pictureUrl: authInfo.user.pictureUrl ?? undefined,\n properties: authInfo.user.properties,\n };\n\n const orgs: AthenaOrg[] =\n authInfo.orgHelper?.getOrgs().map((org) => ({\n orgId: org.orgId,\n orgName: org.orgName,\n urlSafeOrgName: org.urlSafeOrgName,\n })) ?? [];\n\n const newState: AthenaAuthState = {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken: authInfo.accessToken,\n orgs,\n logout: async (redirect = true) => logoutFnRef.current(redirect),\n getToken,\n };\n prevAuthStateRef.current = newState;\n return newState;\n // logoutFn intentionally excluded — captured via logoutFnRef for\n // referential stability while always calling the latest function.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [authInfo, getToken]);\n\n if (authState.isLoading) {\n return <>{displayWhileLoading ?? null}</>;\n }\n\n return (\n <AthenaAuthContext.Provider value={authState}>\n {children}\n </AthenaAuthContext.Provider>\n );\n}\n\n// ─── Public Provider ──────────────────────────────────────────────────\n\n/**\n * Wraps your app with Athena authentication powered by PropelAuth.\n *\n * Place this **above** `<AthenaProvider>` in the component tree.\n * When present, `AthenaProvider` will automatically use the access token\n * from this provider — no need to pass `token` or `apiKey` manually.\n *\n * @example\n * ```tsx\n * import { AthenaAuthProvider } from '@athenaintel/react/auth';\n * import { AthenaProvider, AthenaChat } from '@athenaintel/react';\n *\n * function App() {\n * return (\n * <AthenaAuthProvider authUrl=\"https://auth.yourdomain.com\">\n * <AthenaProvider>\n * <AthenaChat />\n * </AthenaProvider>\n * </AthenaAuthProvider>\n * );\n * }\n * ```\n */\nexport function AthenaAuthProvider({\n children,\n authUrl,\n postLoginRedirectUrl,\n displayWhileLoading,\n}: AthenaAuthProviderProps) {\n const currentHref = useCurrentHref();\n const redirectUrl = postLoginRedirectUrl ?? currentHref;\n\n return (\n <RequiredAuthProvider\n authUrl={authUrl}\n displayWhileLoading={\n displayWhileLoading ? <>{displayWhileLoading}</> : undefined\n }\n displayIfLoggedOut={<RedirectToLogin postLoginRedirectUrl={redirectUrl} />}\n >\n <AthenaAuthBridge displayWhileLoading={displayWhileLoading}>\n {children}\n </AthenaAuthBridge>\n </RequiredAuthProvider>\n );\n}\n"],"names":["useState","useEffect","useAuthInfo","useLogoutFunction","useRef","useMemo","jsx","Fragment","AthenaAuthContext","RequiredAuthProvider","RedirectToLogin"],"mappings":"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"}
@@ -1,4 +1,25 @@
1
1
  import type { ReactNode } from 'react';
2
+ import type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';
3
+ /** The part of a PropelAuth `useAuthInfo()` snapshot the token getter reads. */
4
+ export interface PropelAuthTokenSnapshot {
5
+ loading: boolean;
6
+ isLoggedIn: boolean | undefined;
7
+ accessToken: string | null | undefined;
8
+ /** Absent on `@propelauth/react` releases that predate it. */
9
+ refreshAuthInfo?: () => Promise<void>;
10
+ /** Absent on `@propelauth/react` releases that predate it. */
11
+ tokens?: {
12
+ getAccessToken?: () => Promise<string | undefined>;
13
+ };
14
+ }
15
+ /**
16
+ * The PropelAuth side of the SDK's credential source. `force` (the previous
17
+ * token was refused) makes PropelAuth mint a new token first; either way the
18
+ * token comes from PropelAuth's own `getAccessToken`, which renews one that is
19
+ * about to expire. A failed renewal keeps the held token, so the caller's
20
+ * attempt is refused by the host rather than failing in the client.
21
+ */
22
+ export declare function propelAuthTokenGetter(read: () => PropelAuthTokenSnapshot): AthenaStatewireTokenGetter;
2
23
  export interface AthenaAuthProviderProps {
3
24
  children: ReactNode;
4
25
  /**
@@ -2,6 +2,27 @@ import { jsx, Fragment } from "react/jsx-runtime";
2
2
  import { RequiredAuthProvider, RedirectToLogin, useAuthInfo, useLogoutFunction } from "@propelauth/react";
3
3
  import { useState, useEffect, useRef, useMemo } from "react";
4
4
  import { AthenaAuthContext } from "./AthenaAuthContext.js";
5
+ function propelAuthTokenGetter(read) {
6
+ return async ({ force = false } = {}) => {
7
+ var _a, _b, _c;
8
+ const info = read();
9
+ if (info.loading || !info.isLoggedIn) return null;
10
+ if (force) {
11
+ try {
12
+ await ((_a = info.refreshAuthInfo) == null ? void 0 : _a.call(info));
13
+ } catch {
14
+ }
15
+ }
16
+ const latest = read();
17
+ let fresh;
18
+ try {
19
+ fresh = await ((_c = (_b = latest.tokens) == null ? void 0 : _b.getAccessToken) == null ? void 0 : _c.call(_b));
20
+ } catch {
21
+ fresh = void 0;
22
+ }
23
+ return fresh ?? (latest.isLoggedIn ? latest.accessToken ?? null : null);
24
+ };
25
+ }
5
26
  const ATHENA_LOCATION_CHANGE_EVENT = "athena:location-change";
6
27
  let historyPatched = false;
7
28
  function patchHistoryForLocationTracking() {
@@ -58,6 +79,9 @@ function AthenaAuthBridge({
58
79
  const logoutFnRef = useRef(logoutFn);
59
80
  logoutFnRef.current = logoutFn;
60
81
  const prevAuthStateRef = useRef(null);
82
+ const authInfoRef = useRef(authInfo);
83
+ authInfoRef.current = authInfo;
84
+ const [getToken] = useState(() => propelAuthTokenGetter(() => authInfoRef.current));
61
85
  const authState = useMemo(() => {
62
86
  var _a, _b;
63
87
  if (authInfo.loading) {
@@ -69,7 +93,8 @@ function AthenaAuthBridge({
69
93
  accessToken: null,
70
94
  orgs: [],
71
95
  logout: async () => {
72
- }
96
+ },
97
+ getToken
73
98
  };
74
99
  }
75
100
  if (!authInfo.isLoggedIn) {
@@ -80,7 +105,8 @@ function AthenaAuthBridge({
80
105
  user: null,
81
106
  accessToken: null,
82
107
  orgs: [],
83
- logout: async (redirect = true) => logoutFnRef.current(redirect)
108
+ logout: async (redirect = true) => logoutFnRef.current(redirect),
109
+ getToken
84
110
  };
85
111
  }
86
112
  const prev = prevAuthStateRef.current;
@@ -107,11 +133,12 @@ function AthenaAuthBridge({
107
133
  user,
108
134
  accessToken: authInfo.accessToken,
109
135
  orgs,
110
- logout: async (redirect = true) => logoutFnRef.current(redirect)
136
+ logout: async (redirect = true) => logoutFnRef.current(redirect),
137
+ getToken
111
138
  };
112
139
  prevAuthStateRef.current = newState;
113
140
  return newState;
114
- }, [authInfo]);
141
+ }, [authInfo, getToken]);
115
142
  if (authState.isLoading) {
116
143
  return /* @__PURE__ */ jsx(Fragment, { children: displayWhileLoading ?? null });
117
144
  }
@@ -136,6 +163,7 @@ function AthenaAuthProvider({
136
163
  );
137
164
  }
138
165
  export {
139
- AthenaAuthProvider
166
+ AthenaAuthProvider,
167
+ propelAuthTokenGetter
140
168
  };
141
169
  //# sourceMappingURL=AthenaAuthProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AthenaAuthProvider.js","sources":["../../src/auth/AthenaAuthProvider.tsx"],"sourcesContent":["import {\n RequiredAuthProvider,\n RedirectToLogin,\n useAuthInfo,\n useLogoutFunction,\n} from '@propelauth/react';\nimport type { ReactNode } from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { AthenaAuthContext } from './AthenaAuthContext';\nimport type { AthenaAuthState, AthenaOrg, AthenaUser } from './types';\n\n// ─── Props ────────────────────────────────────────────────────────────\n\nexport interface AthenaAuthProviderProps {\n children: ReactNode;\n\n /**\n * Your PropelAuth auth URL (e.g. `https://auth.yourdomain.com`).\n * Found in the PropelAuth dashboard under Frontend Integration.\n */\n authUrl: string;\n\n /**\n * Where to redirect after a successful login.\n * Defaults to the current page URL.\n */\n postLoginRedirectUrl?: string;\n\n /**\n * Custom element displayed while the auth state is loading.\n * Defaults to `null` (renders nothing).\n */\n displayWhileLoading?: ReactNode;\n}\n\nconst ATHENA_LOCATION_CHANGE_EVENT = 'athena:location-change';\nlet historyPatched = false;\n\nfunction patchHistoryForLocationTracking(): void {\n if (historyPatched || typeof window === 'undefined') {\n return;\n }\n\n const notifyLocationChange = () => {\n window.dispatchEvent(new Event(ATHENA_LOCATION_CHANGE_EVENT));\n };\n\n const originalPushState = window.history.pushState;\n window.history.pushState = function pushState(...args) {\n const result = originalPushState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n const originalReplaceState = window.history.replaceState;\n window.history.replaceState = function replaceState(...args) {\n const result = originalReplaceState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n historyPatched = true;\n}\n\nfunction useCurrentHref(): string {\n const [href, setHref] = useState(\n typeof window !== 'undefined' ? window.location.href : '/',\n );\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n patchHistoryForLocationTracking();\n\n const updateHref = () => {\n setHref(window.location.href);\n };\n\n updateHref();\n window.addEventListener('popstate', updateHref);\n window.addEventListener('hashchange', updateHref);\n window.addEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n\n return () => {\n window.removeEventListener('popstate', updateHref);\n window.removeEventListener('hashchange', updateHref);\n window.removeEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n };\n }, []);\n\n return href;\n}\n\n// ─── Internal Bridge ──────────────────────────────────────────────────\n\n/**\n * Reads PropelAuth state via hooks and maps it into the SDK's\n * `AthenaAuthState`, which is placed on `AthenaAuthContext`.\n */\nfunction AthenaAuthBridge({\n children,\n displayWhileLoading,\n}: {\n children: ReactNode;\n displayWhileLoading?: ReactNode;\n}) {\n const authInfo = useAuthInfo();\n const logoutFn = useLogoutFunction();\n const logoutFnRef = useRef(logoutFn);\n logoutFnRef.current = logoutFn;\n const prevAuthStateRef = useRef<AthenaAuthState | null>(null);\n\n const authState: AthenaAuthState = useMemo(() => {\n if (authInfo.loading) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: true,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async () => {},\n };\n }\n\n if (!authInfo.isLoggedIn) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: false,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async (redirect = true) => logoutFnRef.current(redirect),\n };\n }\n\n // Referential stability: if the token and user identity haven't changed,\n // return the previous state object to prevent cascading context re-renders\n // from PropelAuth's focus-triggered token checks.\n const prev = prevAuthStateRef.current;\n if (\n prev?.isLoggedIn &&\n !prev.isLoading &&\n prev.accessToken === authInfo.accessToken &&\n prev.user?.userId === authInfo.user.userId\n ) {\n return prev;\n }\n\n const user: AthenaUser = {\n userId: authInfo.user.userId,\n email: authInfo.user.email,\n firstName: authInfo.user.firstName ?? undefined,\n lastName: authInfo.user.lastName ?? undefined,\n username: authInfo.user.username ?? undefined,\n pictureUrl: authInfo.user.pictureUrl ?? undefined,\n properties: authInfo.user.properties,\n };\n\n const orgs: AthenaOrg[] =\n authInfo.orgHelper?.getOrgs().map((org) => ({\n orgId: org.orgId,\n orgName: org.orgName,\n urlSafeOrgName: org.urlSafeOrgName,\n })) ?? [];\n\n const newState: AthenaAuthState = {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken: authInfo.accessToken,\n orgs,\n logout: async (redirect = true) => logoutFnRef.current(redirect),\n };\n prevAuthStateRef.current = newState;\n return newState;\n // logoutFn intentionally excluded — captured via logoutFnRef for\n // referential stability while always calling the latest function.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [authInfo]);\n\n if (authState.isLoading) {\n return <>{displayWhileLoading ?? null}</>;\n }\n\n return (\n <AthenaAuthContext.Provider value={authState}>\n {children}\n </AthenaAuthContext.Provider>\n );\n}\n\n// ─── Public Provider ──────────────────────────────────────────────────\n\n/**\n * Wraps your app with Athena authentication powered by PropelAuth.\n *\n * Place this **above** `<AthenaProvider>` in the component tree.\n * When present, `AthenaProvider` will automatically use the access token\n * from this provider — no need to pass `token` or `apiKey` manually.\n *\n * @example\n * ```tsx\n * import { AthenaAuthProvider } from '@athenaintel/react/auth';\n * import { AthenaProvider, AthenaChat } from '@athenaintel/react';\n *\n * function App() {\n * return (\n * <AthenaAuthProvider authUrl=\"https://auth.yourdomain.com\">\n * <AthenaProvider>\n * <AthenaChat />\n * </AthenaProvider>\n * </AthenaAuthProvider>\n * );\n * }\n * ```\n */\nexport function AthenaAuthProvider({\n children,\n authUrl,\n postLoginRedirectUrl,\n displayWhileLoading,\n}: AthenaAuthProviderProps) {\n const currentHref = useCurrentHref();\n const redirectUrl = postLoginRedirectUrl ?? currentHref;\n\n return (\n <RequiredAuthProvider\n authUrl={authUrl}\n displayWhileLoading={\n displayWhileLoading ? <>{displayWhileLoading}</> : undefined\n }\n displayIfLoggedOut={<RedirectToLogin postLoginRedirectUrl={redirectUrl} />}\n >\n <AthenaAuthBridge displayWhileLoading={displayWhileLoading}>\n {children}\n </AthenaAuthBridge>\n </RequiredAuthProvider>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AthenaAuthProvider.js","sources":["../../src/auth/AthenaAuthProvider.tsx"],"sourcesContent":["import {\n RequiredAuthProvider,\n RedirectToLogin,\n useAuthInfo,\n useLogoutFunction,\n} from '@propelauth/react';\nimport type { ReactNode } from 'react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport type { AthenaStatewireTokenGetter } from '../runtime/statewire-auth-headers';\nimport { AthenaAuthContext } from './AthenaAuthContext';\nimport type { AthenaAuthState, AthenaOrg, AthenaUser } from './types';\n\n/** The part of a PropelAuth `useAuthInfo()` snapshot the token getter reads. */\nexport interface PropelAuthTokenSnapshot {\n loading: boolean;\n isLoggedIn: boolean | undefined;\n accessToken: string | null | undefined;\n /** Absent on `@propelauth/react` releases that predate it. */\n refreshAuthInfo?: () => Promise<void>;\n /** Absent on `@propelauth/react` releases that predate it. */\n tokens?: { getAccessToken?: () => Promise<string | undefined> };\n}\n\n/**\n * The PropelAuth side of the SDK's credential source. `force` (the previous\n * token was refused) makes PropelAuth mint a new token first; either way the\n * token comes from PropelAuth's own `getAccessToken`, which renews one that is\n * about to expire. A failed renewal keeps the held token, so the caller's\n * attempt is refused by the host rather than failing in the client.\n */\nexport function propelAuthTokenGetter(\n read: () => PropelAuthTokenSnapshot,\n): AthenaStatewireTokenGetter {\n return async ({ force = false } = {}) => {\n const info = read();\n if (info.loading || !info.isLoggedIn) return null;\n if (force) {\n try {\n await info.refreshAuthInfo?.();\n } catch {\n // Keep the held token; the host decides.\n }\n }\n const latest = read();\n let fresh: string | undefined;\n try {\n fresh = await latest.tokens?.getAccessToken?.();\n } catch {\n fresh = undefined;\n }\n return fresh ?? (latest.isLoggedIn ? (latest.accessToken ?? null) : null);\n };\n}\n\n// ─── Props ────────────────────────────────────────────────────────────\n\nexport interface AthenaAuthProviderProps {\n children: ReactNode;\n\n /**\n * Your PropelAuth auth URL (e.g. `https://auth.yourdomain.com`).\n * Found in the PropelAuth dashboard under Frontend Integration.\n */\n authUrl: string;\n\n /**\n * Where to redirect after a successful login.\n * Defaults to the current page URL.\n */\n postLoginRedirectUrl?: string;\n\n /**\n * Custom element displayed while the auth state is loading.\n * Defaults to `null` (renders nothing).\n */\n displayWhileLoading?: ReactNode;\n}\n\nconst ATHENA_LOCATION_CHANGE_EVENT = 'athena:location-change';\nlet historyPatched = false;\n\nfunction patchHistoryForLocationTracking(): void {\n if (historyPatched || typeof window === 'undefined') {\n return;\n }\n\n const notifyLocationChange = () => {\n window.dispatchEvent(new Event(ATHENA_LOCATION_CHANGE_EVENT));\n };\n\n const originalPushState = window.history.pushState;\n window.history.pushState = function pushState(...args) {\n const result = originalPushState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n const originalReplaceState = window.history.replaceState;\n window.history.replaceState = function replaceState(...args) {\n const result = originalReplaceState.apply(this, args);\n notifyLocationChange();\n return result;\n };\n\n historyPatched = true;\n}\n\nfunction useCurrentHref(): string {\n const [href, setHref] = useState(\n typeof window !== 'undefined' ? window.location.href : '/',\n );\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n patchHistoryForLocationTracking();\n\n const updateHref = () => {\n setHref(window.location.href);\n };\n\n updateHref();\n window.addEventListener('popstate', updateHref);\n window.addEventListener('hashchange', updateHref);\n window.addEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n\n return () => {\n window.removeEventListener('popstate', updateHref);\n window.removeEventListener('hashchange', updateHref);\n window.removeEventListener(ATHENA_LOCATION_CHANGE_EVENT, updateHref);\n };\n }, []);\n\n return href;\n}\n\n// ─── Internal Bridge ──────────────────────────────────────────────────\n\n/**\n * Reads PropelAuth state via hooks and maps it into the SDK's\n * `AthenaAuthState`, which is placed on `AthenaAuthContext`.\n */\nfunction AthenaAuthBridge({\n children,\n displayWhileLoading,\n}: {\n children: ReactNode;\n displayWhileLoading?: ReactNode;\n}) {\n const authInfo = useAuthInfo();\n const logoutFn = useLogoutFunction();\n const logoutFnRef = useRef(logoutFn);\n logoutFnRef.current = logoutFn;\n const prevAuthStateRef = useRef<AthenaAuthState | null>(null);\n const authInfoRef = useRef<PropelAuthTokenSnapshot>(authInfo);\n authInfoRef.current = authInfo;\n // One getter for the provider's lifetime: it reads the latest PropelAuth\n // snapshot, so its identity never changes and never re-renders consumers.\n const [getToken] = useState(() => propelAuthTokenGetter(() => authInfoRef.current));\n\n const authState: AthenaAuthState = useMemo(() => {\n if (authInfo.loading) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: true,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async () => {},\n getToken,\n };\n }\n\n if (!authInfo.isLoggedIn) {\n prevAuthStateRef.current = null;\n return {\n isLoggedIn: false,\n isLoading: false,\n user: null,\n accessToken: null,\n orgs: [],\n logout: async (redirect = true) => logoutFnRef.current(redirect),\n getToken,\n };\n }\n\n // Referential stability: if the token and user identity haven't changed,\n // return the previous state object to prevent cascading context re-renders\n // from PropelAuth's focus-triggered token checks.\n const prev = prevAuthStateRef.current;\n if (\n prev?.isLoggedIn &&\n !prev.isLoading &&\n prev.accessToken === authInfo.accessToken &&\n prev.user?.userId === authInfo.user.userId\n ) {\n return prev;\n }\n\n const user: AthenaUser = {\n userId: authInfo.user.userId,\n email: authInfo.user.email,\n firstName: authInfo.user.firstName ?? undefined,\n lastName: authInfo.user.lastName ?? undefined,\n username: authInfo.user.username ?? undefined,\n pictureUrl: authInfo.user.pictureUrl ?? undefined,\n properties: authInfo.user.properties,\n };\n\n const orgs: AthenaOrg[] =\n authInfo.orgHelper?.getOrgs().map((org) => ({\n orgId: org.orgId,\n orgName: org.orgName,\n urlSafeOrgName: org.urlSafeOrgName,\n })) ?? [];\n\n const newState: AthenaAuthState = {\n isLoggedIn: true,\n isLoading: false,\n user,\n accessToken: authInfo.accessToken,\n orgs,\n logout: async (redirect = true) => logoutFnRef.current(redirect),\n getToken,\n };\n prevAuthStateRef.current = newState;\n return newState;\n // logoutFn intentionally excluded — captured via logoutFnRef for\n // referential stability while always calling the latest function.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [authInfo, getToken]);\n\n if (authState.isLoading) {\n return <>{displayWhileLoading ?? null}</>;\n }\n\n return (\n <AthenaAuthContext.Provider value={authState}>\n {children}\n </AthenaAuthContext.Provider>\n );\n}\n\n// ─── Public Provider ──────────────────────────────────────────────────\n\n/**\n * Wraps your app with Athena authentication powered by PropelAuth.\n *\n * Place this **above** `<AthenaProvider>` in the component tree.\n * When present, `AthenaProvider` will automatically use the access token\n * from this provider — no need to pass `token` or `apiKey` manually.\n *\n * @example\n * ```tsx\n * import { AthenaAuthProvider } from '@athenaintel/react/auth';\n * import { AthenaProvider, AthenaChat } from '@athenaintel/react';\n *\n * function App() {\n * return (\n * <AthenaAuthProvider authUrl=\"https://auth.yourdomain.com\">\n * <AthenaProvider>\n * <AthenaChat />\n * </AthenaProvider>\n * </AthenaAuthProvider>\n * );\n * }\n * ```\n */\nexport function AthenaAuthProvider({\n children,\n authUrl,\n postLoginRedirectUrl,\n displayWhileLoading,\n}: AthenaAuthProviderProps) {\n const currentHref = useCurrentHref();\n const redirectUrl = postLoginRedirectUrl ?? currentHref;\n\n return (\n <RequiredAuthProvider\n authUrl={authUrl}\n displayWhileLoading={\n displayWhileLoading ? <>{displayWhileLoading}</> : undefined\n }\n displayIfLoggedOut={<RedirectToLogin postLoginRedirectUrl={redirectUrl} />}\n >\n <AthenaAuthBridge displayWhileLoading={displayWhileLoading}>\n {children}\n </AthenaAuthBridge>\n </RequiredAuthProvider>\n );\n}\n"],"names":[],"mappings":"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"}