@vertesia/ui 1.5.0 → 1.6.0-dev.20260918.061604Z

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 (336) hide show
  1. package/README.md +125 -0
  2. package/lib/boot/branding.d.ts +58 -0
  3. package/lib/boot/branding.d.ts.map +1 -0
  4. package/lib/boot/branding.js +62 -0
  5. package/lib/boot/branding.js.map +1 -0
  6. package/lib/boot/index.d.ts +27 -0
  7. package/lib/boot/index.d.ts.map +1 -0
  8. package/lib/boot/index.js +325 -0
  9. package/lib/boot/index.js.map +1 -0
  10. package/lib/boot/vite.d.ts +63 -0
  11. package/lib/boot/vite.d.ts.map +1 -0
  12. package/lib/boot/vite.js +108 -0
  13. package/lib/boot/vite.js.map +1 -0
  14. package/lib/core/components/shadcn/button.d.ts.map +1 -1
  15. package/lib/core/components/shadcn/button.js +1 -2
  16. package/lib/core/components/shadcn/button.js.map +1 -1
  17. package/lib/core/components/shadcn/filters/filter-styles.d.ts +1 -1
  18. package/lib/core/components/shadcn/filters/filter-styles.d.ts.map +1 -1
  19. package/lib/core/components/shadcn/filters/filter-styles.js +4 -4
  20. package/lib/core/components/shadcn/label.js +1 -1
  21. package/lib/core/components/shadcn/label.js.map +1 -1
  22. package/lib/env/index.d.ts +32 -1
  23. package/lib/env/index.d.ts.map +1 -1
  24. package/lib/env/index.js +55 -3
  25. package/lib/env/index.js.map +1 -1
  26. package/lib/features/agent/PayloadBuilder.d.ts +4 -0
  27. package/lib/features/agent/PayloadBuilder.d.ts.map +1 -1
  28. package/lib/features/agent/PayloadBuilder.js +6 -1
  29. package/lib/features/agent/PayloadBuilder.js.map +1 -1
  30. package/lib/features/agent/chat/AgentRunFeedback.d.ts +66 -0
  31. package/lib/features/agent/chat/AgentRunFeedback.d.ts.map +1 -0
  32. package/lib/features/agent/chat/AgentRunFeedback.js +298 -0
  33. package/lib/features/agent/chat/AgentRunFeedback.js.map +1 -0
  34. package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  35. package/lib/features/agent/chat/ModernAgentConversation.js +7 -5
  36. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  37. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts +3 -1
  38. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  39. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +11 -10
  40. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  41. package/lib/features/agent/chat/ModernAgentOutput/Header.d.ts.map +1 -1
  42. package/lib/features/agent/chat/ModernAgentOutput/Header.js.map +1 -1
  43. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts +3 -1
  44. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
  45. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +28 -7
  46. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
  47. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.d.ts.map +1 -1
  48. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js +2 -0
  49. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js.map +1 -1
  50. package/lib/features/agent/chat/hooks/useAgentPlans.d.ts +1 -1
  51. package/lib/features/agent/chat/hooks/useAgentPlans.d.ts.map +1 -1
  52. package/lib/features/agent/chat/hooks/useAgentPlans.js +5 -5
  53. package/lib/features/agent/chat/hooks/useAgentPlans.js.map +1 -1
  54. package/lib/features/agent/chat/index.d.ts +1 -0
  55. package/lib/features/agent/chat/index.d.ts.map +1 -1
  56. package/lib/features/agent/chat/index.js +1 -0
  57. package/lib/features/agent/chat/index.js.map +1 -1
  58. package/lib/features/facets/AgentRunnerFacetsNav.d.ts +8 -0
  59. package/lib/features/facets/AgentRunnerFacetsNav.d.ts.map +1 -1
  60. package/lib/features/facets/AgentRunnerFacetsNav.js +12 -0
  61. package/lib/features/facets/AgentRunnerFacetsNav.js.map +1 -1
  62. package/lib/features/permissions/UserPermissionsProvider.d.ts +15 -1
  63. package/lib/features/permissions/UserPermissionsProvider.d.ts.map +1 -1
  64. package/lib/features/permissions/UserPermissionsProvider.js +20 -12
  65. package/lib/features/permissions/UserPermissionsProvider.js.map +1 -1
  66. package/lib/features/store/objects/search/DocumentSearchContext.d.ts.map +1 -1
  67. package/lib/features/store/objects/search/DocumentSearchContext.js +6 -1
  68. package/lib/features/store/objects/search/DocumentSearchContext.js.map +1 -1
  69. package/lib/features/store/objects/selection/SelectionActions.js +1 -1
  70. package/lib/features/store/objects/selection/SelectionActions.js.map +1 -1
  71. package/lib/features/store/types/TypeRegistry.d.ts +130 -0
  72. package/lib/features/store/types/TypeRegistry.d.ts.map +1 -1
  73. package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts +130 -0
  74. package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts.map +1 -1
  75. package/lib/i18n/index.d.ts +5 -4
  76. package/lib/i18n/index.d.ts.map +1 -1
  77. package/lib/i18n/index.js +16 -2
  78. package/lib/i18n/index.js.map +1 -1
  79. package/lib/i18n/locales/ar.json +30 -0
  80. package/lib/i18n/locales/de.json +30 -0
  81. package/lib/i18n/locales/en.json +30 -0
  82. package/lib/i18n/locales/es.json +30 -0
  83. package/lib/i18n/locales/fr.json +30 -0
  84. package/lib/i18n/locales/it.json +30 -0
  85. package/lib/i18n/locales/ja.json +30 -0
  86. package/lib/i18n/locales/ko.json +30 -0
  87. package/lib/i18n/locales/pt.json +30 -0
  88. package/lib/i18n/locales/ru.json +30 -0
  89. package/lib/i18n/locales/tr.json +30 -0
  90. package/lib/i18n/locales/zh-TW.json +30 -0
  91. package/lib/i18n/locales/zh.json +30 -0
  92. package/lib/layout/ErrorPage.js +1 -1
  93. package/lib/layout/ErrorPage.js.map +1 -1
  94. package/lib/layout/Navbar.d.ts.map +1 -1
  95. package/lib/layout/Navbar.js +3 -2
  96. package/lib/layout/Navbar.js.map +1 -1
  97. package/lib/session/UserSession.d.ts.map +1 -1
  98. package/lib/session/UserSession.js +2 -3
  99. package/lib/session/UserSession.js.map +1 -1
  100. package/lib/session/UserSessionProvider.d.ts +2 -1
  101. package/lib/session/UserSessionProvider.d.ts.map +1 -1
  102. package/lib/session/UserSessionProvider.js +13 -4
  103. package/lib/session/UserSessionProvider.js.map +1 -1
  104. package/lib/session/auth/composable.d.ts.map +1 -1
  105. package/lib/session/auth/composable.js +88 -3
  106. package/lib/session/auth/composable.js.map +1 -1
  107. package/lib/session/auth/domainRouting.d.ts +22 -12
  108. package/lib/session/auth/domainRouting.d.ts.map +1 -1
  109. package/lib/session/auth/domainRouting.js +35 -21
  110. package/lib/session/auth/domainRouting.js.map +1 -1
  111. package/lib/session/auth/firebase.d.ts.map +1 -1
  112. package/lib/session/auth/firebase.js +10 -0
  113. package/lib/session/auth/firebase.js.map +1 -1
  114. package/lib/session/auth/useAuthState.d.ts +9 -4
  115. package/lib/session/auth/useAuthState.d.ts.map +1 -1
  116. package/lib/session/auth/useAuthState.js +9 -4
  117. package/lib/session/auth/useAuthState.js.map +1 -1
  118. package/lib/session/index.d.ts.map +1 -1
  119. package/lib/session/index.js +3 -1
  120. package/lib/session/index.js.map +1 -1
  121. package/lib/shell/AuthScreenPreview.d.ts +12 -0
  122. package/lib/shell/AuthScreenPreview.d.ts.map +1 -0
  123. package/lib/shell/AuthScreenPreview.js +140 -0
  124. package/lib/shell/AuthScreenPreview.js.map +1 -0
  125. package/lib/shell/BrandedAuthScreens.d.ts +19 -0
  126. package/lib/shell/BrandedAuthScreens.d.ts.map +1 -0
  127. package/lib/shell/BrandedAuthScreens.js +64 -0
  128. package/lib/shell/BrandedAuthScreens.js.map +1 -0
  129. package/lib/shell/SplashScreen.d.ts +13 -2
  130. package/lib/shell/SplashScreen.d.ts.map +1 -1
  131. package/lib/shell/SplashScreen.js +20 -19
  132. package/lib/shell/SplashScreen.js.map +1 -1
  133. package/lib/shell/VertesiaShell.d.ts +15 -4
  134. package/lib/shell/VertesiaShell.d.ts.map +1 -1
  135. package/lib/shell/VertesiaShell.js +8 -3
  136. package/lib/shell/VertesiaShell.js.map +1 -1
  137. package/lib/shell/apps/StandaloneApp.js +5 -1
  138. package/lib/shell/apps/StandaloneApp.js.map +1 -1
  139. package/lib/shell/index.d.ts +6 -0
  140. package/lib/shell/index.d.ts.map +1 -1
  141. package/lib/shell/index.js +5 -0
  142. package/lib/shell/index.js.map +1 -1
  143. package/lib/shell/login/SignInAuthPending.js +1 -1
  144. package/lib/shell/login/SignInAuthPending.js.map +1 -1
  145. package/lib/shell/login/SignInEmailStep.d.ts.map +1 -1
  146. package/lib/shell/login/SignInEmailStep.js +2 -2
  147. package/lib/shell/login/SignInEmailStep.js.map +1 -1
  148. package/lib/shell/login/SignInFlow.d.ts +68 -0
  149. package/lib/shell/login/SignInFlow.d.ts.map +1 -0
  150. package/lib/shell/login/SignInFlow.js +108 -0
  151. package/lib/shell/login/SignInFlow.js.map +1 -0
  152. package/lib/shell/login/SignInIcons.d.ts +1 -1
  153. package/lib/shell/login/SignInIcons.d.ts.map +1 -1
  154. package/lib/shell/login/SignInPageShell.d.ts +13 -0
  155. package/lib/shell/login/SignInPageShell.d.ts.map +1 -0
  156. package/lib/shell/login/SignInPageShell.js +14 -0
  157. package/lib/shell/login/SignInPageShell.js.map +1 -0
  158. package/lib/shell/login/SignInPrimitives.d.ts +3 -3
  159. package/lib/shell/login/SignInPrimitives.d.ts.map +1 -1
  160. package/lib/shell/login/SignInPrimitives.js +13 -11
  161. package/lib/shell/login/SignInPrimitives.js.map +1 -1
  162. package/lib/shell/login/SignInRestrictedEnvStep.d.ts +5 -0
  163. package/lib/shell/login/SignInRestrictedEnvStep.d.ts.map +1 -1
  164. package/lib/shell/login/SignInRestrictedEnvStep.js +8 -4
  165. package/lib/shell/login/SignInRestrictedEnvStep.js.map +1 -1
  166. package/lib/shell/login/SigninScreen.d.ts +38 -3
  167. package/lib/shell/login/SigninScreen.d.ts.map +1 -1
  168. package/lib/shell/login/SigninScreen.js +33 -86
  169. package/lib/shell/login/SigninScreen.js.map +1 -1
  170. package/lib/shell/login/SignupForm.d.ts +3 -1
  171. package/lib/shell/login/SignupForm.d.ts.map +1 -1
  172. package/lib/shell/login/SignupForm.js +7 -3
  173. package/lib/shell/login/SignupForm.js.map +1 -1
  174. package/lib/shell/login/signInUtils.d.ts +12 -1
  175. package/lib/shell/login/signInUtils.d.ts.map +1 -1
  176. package/lib/shell/login/signInUtils.js +34 -11
  177. package/lib/shell/login/signInUtils.js.map +1 -1
  178. package/lib/vertesia-ui-boot.js +237 -0
  179. package/lib/vertesia-ui-boot.js.map +1 -0
  180. package/lib/vertesia-ui-core.js +88 -46
  181. package/lib/vertesia-ui-core.js.map +1 -1
  182. package/lib/vertesia-ui-env.js +1 -1
  183. package/lib/vertesia-ui-env.js.map +1 -1
  184. package/lib/vertesia-ui-features.js +350 -317
  185. package/lib/vertesia-ui-features.js.map +1 -1
  186. package/lib/vertesia-ui-i18n.B3NFclwl.chunk.js +5 -0
  187. package/lib/vertesia-ui-i18n.B3NFclwl.chunk.js.map +1 -0
  188. package/lib/vertesia-ui-i18n.BWSrNIxc.chunk.js +5 -0
  189. package/lib/vertesia-ui-i18n.BWSrNIxc.chunk.js.map +1 -0
  190. package/lib/vertesia-ui-i18n.BWvWeB1p.chunk.js +5 -0
  191. package/lib/vertesia-ui-i18n.BWvWeB1p.chunk.js.map +1 -0
  192. package/lib/vertesia-ui-i18n.BZCXvTC1.chunk.js +5 -0
  193. package/lib/vertesia-ui-i18n.BZCXvTC1.chunk.js.map +1 -0
  194. package/lib/vertesia-ui-i18n.BbJMvFZW.chunk.js +5 -0
  195. package/lib/vertesia-ui-i18n.BbJMvFZW.chunk.js.map +1 -0
  196. package/lib/vertesia-ui-i18n.CE5ldwpt.chunk.js +5 -0
  197. package/lib/vertesia-ui-i18n.CE5ldwpt.chunk.js.map +1 -0
  198. package/lib/vertesia-ui-i18n.CMC0IShY.chunk.js +5 -0
  199. package/lib/vertesia-ui-i18n.CMC0IShY.chunk.js.map +1 -0
  200. package/lib/vertesia-ui-i18n.CkcRqBKU.chunk.js +5 -0
  201. package/lib/vertesia-ui-i18n.CkcRqBKU.chunk.js.map +1 -0
  202. package/lib/vertesia-ui-i18n.D-Mq1sw_.chunk.js +5 -0
  203. package/lib/vertesia-ui-i18n.D-Mq1sw_.chunk.js.map +1 -0
  204. package/lib/vertesia-ui-i18n.DkK68-A-.chunk.js +5 -0
  205. package/lib/vertesia-ui-i18n.DkK68-A-.chunk.js.map +1 -0
  206. package/lib/vertesia-ui-i18n.XE_dcCg6.chunk.js +5 -0
  207. package/lib/vertesia-ui-i18n.XE_dcCg6.chunk.js.map +1 -0
  208. package/lib/vertesia-ui-i18n.js +2 -2
  209. package/lib/vertesia-ui-i18n.js.map +1 -1
  210. package/lib/vertesia-ui-i18n.qgNqMPvK.chunk.js +5 -0
  211. package/lib/vertesia-ui-i18n.qgNqMPvK.chunk.js.map +1 -0
  212. package/lib/vertesia-ui-layout.js +43 -19
  213. package/lib/vertesia-ui-layout.js.map +1 -1
  214. package/lib/vertesia-ui-rich-text.js +123 -74
  215. package/lib/vertesia-ui-rich-text.js.map +1 -1
  216. package/lib/vertesia-ui-router.js.map +1 -1
  217. package/lib/vertesia-ui-session.js +1 -1
  218. package/lib/vertesia-ui-session.js.map +1 -1
  219. package/lib/vertesia-ui-shell.js +87 -33
  220. package/lib/vertesia-ui-shell.js.map +1 -1
  221. package/lib/vertesia-ui-widgets.js +154 -98
  222. package/lib/vertesia-ui-widgets.js.map +1 -1
  223. package/lib/widgets/xml-viewer/hooks/useCollapsible.d.ts +1 -1
  224. package/llms.txt +6 -2
  225. package/package.json +45 -36
  226. package/src/__tests__/a11y.test.tsx +25 -0
  227. package/src/__tests__/theme.test.ts +91 -0
  228. package/src/boot/branding.test.ts +72 -0
  229. package/src/boot/branding.ts +108 -0
  230. package/src/boot/index.test.ts +216 -0
  231. package/src/boot/index.ts +356 -0
  232. package/src/boot/vite.test.ts +93 -0
  233. package/src/boot/vite.ts +118 -0
  234. package/src/core/components/shadcn/button.tsx +1 -2
  235. package/src/core/components/shadcn/filters/filter-styles.ts +4 -4
  236. package/src/core/components/shadcn/label.tsx +1 -1
  237. package/src/env/index.test.ts +127 -0
  238. package/src/env/index.ts +73 -3
  239. package/src/features/agent/PayloadBuilder.tsx +10 -1
  240. package/src/features/agent/chat/AgentRunFeedback.test.tsx +347 -0
  241. package/src/features/agent/chat/AgentRunFeedback.tsx +465 -0
  242. package/src/features/agent/chat/ModernAgentConversation.test.tsx +1 -1
  243. package/src/features/agent/chat/ModernAgentConversation.tsx +52 -47
  244. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +145 -0
  245. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +42 -24
  246. package/src/features/agent/chat/ModernAgentOutput/Header.tsx +4 -0
  247. package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +38 -5
  248. package/src/features/agent/chat/ModernAgentOutput/SummaryConversation.ts +2 -0
  249. package/src/features/agent/chat/ModernAgentOutput/utils.test.ts +14 -0
  250. package/src/features/agent/chat/hooks/useAgentPlans.test.ts +52 -0
  251. package/src/features/agent/chat/hooks/useAgentPlans.ts +5 -5
  252. package/src/features/agent/chat/hooks/useFileProcessing.test.tsx +0 -5
  253. package/src/features/agent/chat/index.ts +6 -0
  254. package/src/features/facets/AgentRunnerFacetsNav.tsx +26 -0
  255. package/src/features/permissions/UserPermissionsProvider.test.tsx +50 -1
  256. package/src/features/permissions/UserPermissionsProvider.tsx +82 -44
  257. package/src/features/store/objects/search/DocumentSearchContext.ts +8 -3
  258. package/src/features/store/objects/selection/SelectionActions.tsx +4 -1
  259. package/src/i18n/index.tsx +25 -2
  260. package/src/i18n/locales/ar.json +30 -0
  261. package/src/i18n/locales/de.json +30 -0
  262. package/src/i18n/locales/en.json +30 -0
  263. package/src/i18n/locales/es.json +30 -0
  264. package/src/i18n/locales/fr.json +30 -0
  265. package/src/i18n/locales/it.json +30 -0
  266. package/src/i18n/locales/ja.json +30 -0
  267. package/src/i18n/locales/ko.json +30 -0
  268. package/src/i18n/locales/pt.json +30 -0
  269. package/src/i18n/locales/ru.json +30 -0
  270. package/src/i18n/locales/tr.json +30 -0
  271. package/src/i18n/locales/zh-TW.json +30 -0
  272. package/src/i18n/locales/zh.json +30 -0
  273. package/src/layout/ErrorPage.tsx +1 -1
  274. package/src/layout/Navbar.test.tsx +58 -0
  275. package/src/layout/Navbar.tsx +5 -2
  276. package/src/router/path.test.ts +5 -5
  277. package/src/session/UserSession.ts +2 -4
  278. package/src/session/UserSessionProvider.strict-mode.test.tsx +44 -0
  279. package/src/session/UserSessionProvider.test.ts +62 -1
  280. package/src/session/UserSessionProvider.tsx +12 -4
  281. package/src/session/auth/composable.renewal.test.ts +170 -0
  282. package/src/session/auth/composable.test.ts +23 -1
  283. package/src/session/auth/composable.ts +101 -2
  284. package/src/session/auth/domainRouting.test.ts +35 -65
  285. package/src/session/auth/domainRouting.ts +51 -34
  286. package/src/session/auth/firebase.test.ts +25 -0
  287. package/src/session/auth/firebase.ts +10 -0
  288. package/src/session/auth/useAuthState.ts +11 -4
  289. package/src/session/index.ts +3 -1
  290. package/src/shell/AuthScreenPreview.test.tsx +44 -0
  291. package/src/shell/AuthScreenPreview.tsx +228 -0
  292. package/src/shell/BrandedAuthScreens.test.tsx +181 -0
  293. package/src/shell/BrandedAuthScreens.tsx +117 -0
  294. package/src/shell/SplashScreen.test.tsx +37 -0
  295. package/src/shell/SplashScreen.tsx +44 -39
  296. package/src/shell/VertesiaShell.tsx +49 -22
  297. package/src/shell/apps/StandaloneApp.tsx +5 -1
  298. package/src/shell/index.tsx +6 -0
  299. package/src/shell/login/SignInAuthPending.tsx +3 -3
  300. package/src/shell/login/SignInEmailStep.test.tsx +32 -0
  301. package/src/shell/login/SignInEmailStep.tsx +13 -9
  302. package/src/shell/login/SignInFlow.tsx +213 -0
  303. package/src/shell/login/SignInPageShell.tsx +62 -0
  304. package/src/shell/login/SignInPrimitives.tsx +14 -11
  305. package/src/shell/login/SignInRestrictedEnvStep.tsx +19 -7
  306. package/src/shell/login/SigninScreen.test.tsx +104 -0
  307. package/src/shell/login/SigninScreen.tsx +115 -145
  308. package/src/shell/login/SignupForm.tsx +8 -3
  309. package/src/shell/login/signInUtils.ts +42 -9
  310. package/src/widgets/markdown/CollaborativeMarkdownRenderer.test.tsx +11 -3
  311. package/src/widgets/markdown/markdown.css +21 -21
  312. package/lib/vertesia-ui-i18n.BfN7iOHb.chunk.js +0 -5
  313. package/lib/vertesia-ui-i18n.BfN7iOHb.chunk.js.map +0 -1
  314. package/lib/vertesia-ui-i18n.BjH2zEZw.chunk.js +0 -5
  315. package/lib/vertesia-ui-i18n.BjH2zEZw.chunk.js.map +0 -1
  316. package/lib/vertesia-ui-i18n.BjSwAZXl.chunk.js +0 -5
  317. package/lib/vertesia-ui-i18n.BjSwAZXl.chunk.js.map +0 -1
  318. package/lib/vertesia-ui-i18n.C7PUm5Au.chunk.js +0 -5
  319. package/lib/vertesia-ui-i18n.C7PUm5Au.chunk.js.map +0 -1
  320. package/lib/vertesia-ui-i18n.CF_1mUS5.chunk.js +0 -5
  321. package/lib/vertesia-ui-i18n.CF_1mUS5.chunk.js.map +0 -1
  322. package/lib/vertesia-ui-i18n.ChfUl3_-.chunk.js +0 -5
  323. package/lib/vertesia-ui-i18n.ChfUl3_-.chunk.js.map +0 -1
  324. package/lib/vertesia-ui-i18n.D-Sk-5d1.chunk.js +0 -5
  325. package/lib/vertesia-ui-i18n.D-Sk-5d1.chunk.js.map +0 -1
  326. package/lib/vertesia-ui-i18n.D4rnu1_S.chunk.js +0 -5
  327. package/lib/vertesia-ui-i18n.D4rnu1_S.chunk.js.map +0 -1
  328. package/lib/vertesia-ui-i18n.DDf0-laK.chunk.js +0 -5
  329. package/lib/vertesia-ui-i18n.DDf0-laK.chunk.js.map +0 -1
  330. package/lib/vertesia-ui-i18n.DN79y06J.chunk.js +0 -5
  331. package/lib/vertesia-ui-i18n.DN79y06J.chunk.js.map +0 -1
  332. package/lib/vertesia-ui-i18n.DWLIciYQ.chunk.js +0 -5
  333. package/lib/vertesia-ui-i18n.DWLIciYQ.chunk.js.map +0 -1
  334. package/lib/vertesia-ui-i18n.DvF_YaAJ.chunk.js +0 -5
  335. package/lib/vertesia-ui-i18n.DvF_YaAJ.chunk.js.map +0 -1
  336. package/src/session/auth/authState.test.ts +0 -35
@@ -1 +1 @@
1
- {"version":3,"file":"vertesia-ui-session.js","names":["_jsx","_jsx"],"sources":["session/constants.js","session/auth/firebase.js","session/auth/composable.js","session/auth/authState.js","session/auth/domainRouting.js","session/auth/useAuthState.js","session/UserSession.js","session/auth/useCurrentTenant.js","session/DevSessionProvider.js","session/UserSessionProvider.js","session/useUXTracking.js"],"sourcesContent":["export const LastSelectedAccountId_KEY = 'composableai.lastSelectedAccountId';\nexport const LastSelectedProjectId_KEY = 'composableai.lastSelectedProjectId';\n//# sourceMappingURL=constants.js.map","import { Env } from '@vertesia/ui/env';\nimport { getAnalytics } from 'firebase/analytics';\nimport { initializeApp } from 'firebase/app';\nimport { getAuth } from 'firebase/auth';\n// Use lazy initialization to avoid accessing Env before it's initialized\nlet _firebaseApp = null;\nlet _analytics = null;\nlet _firebaseAuth = null;\n// Getters that lazily initialize Firebase components when first accessed\nexport function getFirebaseApp() {\n if (!_firebaseApp) {\n try {\n if (!Env.firebase) {\n throw new Error('Firebase configuration is not available in the environment');\n }\n _firebaseApp = initializeApp(Env.firebase);\n }\n catch (error) {\n console.error('Failed to initialize Firebase app:', error);\n throw new Error('Firebase initialization failed - environment may not be properly initialized', {\n cause: error,\n });\n }\n }\n return _firebaseApp;\n}\nexport function getFirebaseAnalytics() {\n if (!_analytics) {\n _analytics = getAnalytics(getFirebaseApp());\n }\n return _analytics;\n}\nexport function getFirebaseAuth() {\n if (!_firebaseAuth) {\n _firebaseAuth = getAuth(getFirebaseApp());\n }\n return _firebaseAuth;\n}\nexport async function setFirebaseTenant(tenantEmail) {\n if (!tenantEmail) {\n console.log('No tenant name or email specified, skipping tenant setup');\n return;\n }\n if (!Env.firebase) {\n console.log('Firebase configuration is not available in the environment');\n return;\n }\n try {\n if (tenantEmail)\n console.log(`Resolving tenant ID from email: ${tenantEmail}`);\n // Add retry logic with exponential backoff\n let retries = 3;\n let retryDelay = 250; // Start with 250ms delay\n while (retries > 0) {\n try {\n // Call the API endpoint to resolve the tenant ID\n const response = await fetch('/api/resolve-tenant', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n tenantEmail: tenantEmail,\n }),\n // Add timeout to prevent hanging requests\n signal: AbortSignal.timeout(5000),\n });\n // Check for network errors\n if (!response) {\n throw new Error('No response received from tenant API');\n }\n // Handle HTTP error responses\n if (!response.ok) {\n // Try to parse the error response\n try {\n const errorData = await response.json();\n console.error('Failed to resolve tenant ID:', errorData.error);\n }\n catch {\n console.error(`Failed to resolve tenant ID: HTTP ${response.status}`);\n }\n // If the error is 404 Not Found, no need to retry\n if (response.status === 404) {\n console.warn(`Tenant not found for ${tenantEmail}`);\n return;\n }\n throw new Error(`HTTP error ${response.status}`);\n }\n // Successfully got a response, parse it\n const data = (await response.json());\n if (data?.firebaseTenantId) {\n const auth = getFirebaseAuth();\n auth.tenantId = data.firebaseTenantId;\n Env.firebase.providerType = data.provider ?? 'oidc';\n console.log(`Tenant ID set to ${auth.tenantId}`);\n return data;\n }\n else {\n console.error(`Invalid response format, missing tenantId for ${tenantEmail}`);\n return; // No need to retry for invalid response format\n }\n }\n catch (fetchError) {\n // Only retry for network-related errors\n if (retries > 1) {\n console.warn(`Tenant resolution failed, retrying in ${retryDelay}ms...`, fetchError);\n await new Promise((resolve) => setTimeout(resolve, retryDelay));\n retryDelay *= 2; // Exponential backoff\n retries--;\n }\n else {\n throw fetchError; // Last retry failed, propagate error\n }\n }\n }\n }\n catch (error) {\n // Final error handler\n console.error('Error setting Firebase tenant:', error instanceof Error ? error.message : 'Unknown error');\n // Continue without tenant ID - authentication will work without multi-tenancy\n // but the user will access the default tenant\n }\n}\nexport async function getFirebaseAuthToken(refresh) {\n const auth = getFirebaseAuth();\n const user = auth.currentUser;\n if (user) {\n return user\n .getIdToken(refresh)\n .then((token) => {\n Env.logger.debug('Got Firebase token', {\n vertesia: {\n user_email: user.email,\n user_name: user.displayName,\n user_id: user.uid,\n refresh: refresh,\n },\n });\n return token;\n })\n .catch((err) => {\n Env.logger.error('Failed to get Firebase token', {\n vertesia: {\n user_email: user.email,\n user_name: user.displayName,\n user_id: user.uid,\n refresh: refresh,\n error: err,\n },\n });\n console.error('Failed to get access token', err);\n return null;\n });\n }\n else {\n Env.logger.warn('No user found');\n return Promise.resolve(null);\n }\n}\n//# sourceMappingURL=firebase.js.map","/**\n * Handle client caching and refresh of auth token\n */\nimport { NO_ACCESSIBLE_ACCOUNT_ERROR_CODE, REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE, RESTRICTED_ENVIRONMENT_ERROR_CODE, } from '@vertesia/common';\nimport { Env } from '@vertesia/ui/env';\nimport { jwtDecode } from 'jwt-decode';\nimport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY } from '../constants';\nimport { getFirebaseAuth, getFirebaseAuthToken } from './firebase';\nlet AUTH_TOKEN_RAW;\nlet AUTH_TOKEN;\nfunction clearRejectedPersistedScope(accountId, projectId) {\n if (!accountId)\n return;\n const projectKey = `${LastSelectedProjectId_KEY}-${accountId}`;\n if (projectId) {\n const persistedProjectMatches = localStorage.getItem(projectKey) === projectId;\n if (persistedProjectMatches) {\n localStorage.removeItem(projectKey);\n }\n if (persistedProjectMatches && localStorage.getItem(LastSelectedAccountId_KEY) === accountId) {\n localStorage.removeItem(LastSelectedAccountId_KEY);\n }\n return;\n }\n if (localStorage.getItem(LastSelectedAccountId_KEY) === accountId) {\n localStorage.removeItem(LastSelectedAccountId_KEY);\n localStorage.removeItem(projectKey);\n }\n}\nfunction identityFromAcceptedToken(token) {\n try {\n const payload = jwtDecode(token);\n if (typeof payload.email !== 'string' || !payload.email)\n return undefined;\n return {\n email: payload.email,\n name: typeof payload.name === 'string' && payload.name ? payload.name : undefined,\n };\n }\n catch {\n return undefined;\n }\n}\nexport function resolveAuthSelection(currentUrl) {\n const urlAccount = currentUrl.searchParams.get('a') ?? undefined;\n const urlProject = currentUrl.searchParams.get('p') ?? undefined;\n const accountId = urlAccount ??\n (urlProject === undefined ? (localStorage.getItem(LastSelectedAccountId_KEY) ?? undefined) : undefined);\n const projectId = urlProject ?? localStorage.getItem(`${LastSelectedProjectId_KEY}-${accountId}`) ?? undefined;\n return { accountId, projectId };\n}\nfunction normalizeIssuer(value) {\n return value?.replace(/\\/+$/, '');\n}\nfunction decodeToken(token) {\n return jwtDecode(token);\n}\nfunction isVertesiaIssuedToken(token) {\n if (!token)\n return false;\n try {\n const decoded = decodeToken(token);\n return normalizeIssuer(decoded.iss) === normalizeIssuer(Env.endpoints.sts);\n }\n catch {\n return false;\n }\n}\nfunction canUseVertesiaTokenDirectly(token, accountId, projectId) {\n const decoded = decodeToken(token);\n if (!decoded.exp || decoded.exp <= Date.now() / 1000 + 300) {\n return false;\n }\n const hasAuthorizationClaims = Boolean(decoded.permissions?.length ||\n decoded.account_roles?.length ||\n decoded.project_roles?.length ||\n decoded.apps?.length);\n if (!hasAuthorizationClaims) {\n return false;\n }\n if (accountId && decoded.account?.id !== accountId) {\n return false;\n }\n if (projectId && decoded.project?.id !== projectId) {\n return false;\n }\n return true;\n}\nexport async function fetchComposableToken(getIdToken, accountId, projectId, ttl, retryCount = 0) {\n Env.logger.debug('Getting/refreshing composable token', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n retry_count: retryCount,\n },\n });\n const idToken = await getIdToken(); //get from firebase\n if (!idToken) {\n throw new Error('No id token found');\n }\n // Use STS endpoint - either configured or default to sts.vertesia.io\n const stsEndpoint = Env.endpoints.sts;\n Env.logger.debug('Using STS for token generation', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n sts_url: stsEndpoint,\n },\n });\n try {\n // Call STS to generate a user token\n const stsUrl = new URL(`${stsEndpoint}/token/issue`);\n const requestBody = {\n type: 'user',\n account_id: accountId,\n project_id: projectId,\n expires_at: ttl ? Math.floor(Date.now() / 1000) + ttl : undefined,\n };\n const stsRes = await fetch(stsUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${idToken}`, // Firebase token for authentication\n },\n body: JSON.stringify(requestBody),\n }).catch((error) => {\n console.error('Failed to call STS endpoint', error);\n Env.logger.error('Failed to call STS endpoint', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n error: error,\n },\n });\n throw new AuthenticationServiceError('The authentication service could not be reached', stsEndpoint, {\n cause: error,\n });\n });\n if (idToken && stsRes?.status === 404) {\n // User not found in token-server - call ensure-user endpoint\n Env.logger.debug('404: User not found - calling ensure-user endpoint', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes?.status,\n },\n });\n const ensureResponse = await fetch(`${Env.endpoints.studio}/auth/ensure-user`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${idToken}`,\n 'Content-Type': 'application/json',\n },\n });\n if (ensureResponse.status === 412) {\n // No invite - trigger signup\n Env.logger.debug('412: No invite found - signup required', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n },\n });\n const idTokenDecoded = jwtDecode(idToken);\n if (!idTokenDecoded?.email) {\n Env.logger.error('No email found in id token');\n throw new Error('No email found in id token');\n }\n throw new UserNotFoundError('User not found - signup required', idTokenDecoded.email);\n }\n if (ensureResponse.status === 403) {\n // SigninScreen keys the invite-required view off this message.\n Env.logger.warn('403: Customer-domain user requires an invite to join', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n },\n });\n throw new Error('Customer-domain user requires an invite to join');\n }\n if (!ensureResponse.ok) {\n console.error('Failed to ensure user exists', ensureResponse.status);\n Env.logger.error('Failed to ensure user exists', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: ensureResponse.status,\n },\n });\n throw new Error('Failed to ensure user exists');\n }\n // User created/exists - retry token generation\n Env.logger.debug('User ensured - retrying token generation', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n },\n });\n return fetchComposableToken(getIdToken, accountId, projectId, ttl, retryCount);\n }\n if (idToken && stsRes?.status === 412) {\n Env.logger.info(\"412: auth succeeded but user doesn't exist - signup required\", {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes?.status,\n },\n });\n const idTokenDecoded = jwtDecode(idToken);\n if (!idTokenDecoded?.email) {\n Env.logger.error('No email found in id token');\n throw new Error('No email found in id token');\n }\n Env.logger.info('User not found', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n email: idTokenDecoded.email,\n },\n });\n throw new UserNotFoundError('User not found', idTokenDecoded.email);\n }\n if (stsRes.status === 401) {\n Env.logger.warn('STS rejected the identity credential', {\n vertesia: { account_id: accountId, project_id: projectId, status: stsRes.status },\n });\n throw new CredentialError('Your sign-in credentials could not be verified', stsEndpoint, {\n status: stsRes.status,\n });\n }\n if (stsRes.status === 403) {\n const body = (await stsRes.json().catch(() => undefined));\n const identity = identityFromAcceptedToken(idToken);\n if (body?.errorCode === RESTRICTED_ENVIRONMENT_ERROR_CODE) {\n Env.logger.warn('403: User lacks early-access for this restricted environment', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n },\n });\n throw new RestrictedEnvironmentError(body.message ?? \"You don't have sufficient permission to visit unstable environments\");\n }\n if (body?.errorCode === NO_ACCESSIBLE_ACCOUNT_ERROR_CODE) {\n Env.logger.warn('STS identity has no accessible account', {\n vertesia: { error_code: body.errorCode, status: stsRes.status },\n });\n throw new NoAccessibleAccountError(stsEndpoint, body.message, identity);\n }\n // During rolling deployment an older STS may return an uncoded user-token 403.\n // Treat it as a requested-scope rejection; all calls in this function issue user tokens.\n if (!body?.errorCode || body.errorCode === REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE) {\n Env.logger.warn('STS rejected the requested account or project', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes.status,\n error_code: body?.errorCode ?? REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE,\n },\n });\n throw new RequestedScopeUnavailableError(stsEndpoint, accountId, projectId, body?.errorCode ? body.message : undefined, identity);\n }\n Env.logger.error('STS returned an unrecognized forbidden response', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes.status,\n error_code: body.errorCode,\n },\n });\n throw new AuthenticationServiceError('The authentication service rejected the request', stsEndpoint, {\n status: stsRes.status,\n errorCode: body.errorCode,\n cause: body,\n });\n }\n if (!stsRes.ok) {\n const errorText = await stsRes.text();\n console.error('STS token generation failed:', stsRes.status, errorText);\n Env.logger.error('STS token generation failed', {\n vertesia: {\n status: stsRes.status,\n error: errorText,\n account_id: accountId,\n project_id: projectId,\n },\n });\n throw new AuthenticationServiceError('The authentication service could not complete sign-in', stsEndpoint, {\n status: stsRes.status,\n cause: errorText,\n });\n }\n const { token } = await stsRes.json();\n Env.logger.debug('Successfully got token from STS');\n return token;\n }\n catch (error) {\n if (error instanceof UserNotFoundError ||\n error instanceof STSError ||\n error instanceof RestrictedEnvironmentError) {\n // Re-throw typed auth errors to be handled separately in the caller\n throw error;\n }\n if (error instanceof Error && error.message === 'Customer-domain user requires an invite to join')\n throw error;\n console.error('Failed to get composable token from STS', error);\n Env.logger.error('Failed to get composable token from STS', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n error: error,\n },\n });\n throw new AuthenticationServiceError('The authentication service could not complete sign-in', stsEndpoint, {\n cause: error,\n });\n }\n}\n/**\n *\n * @param accountId\n * @param projectId\n * @param ttl time to live for the token in seconds\n * @returns\n */\nexport async function fetchComposableTokenFromFirebaseToken(accountId, projectId, ttl) {\n return fetchComposableToken(getFirebaseAuthToken, accountId, projectId, ttl);\n}\n/**\n * Mint a scoped Vertesia token from an existing Vertesia JWT. STS accepts STS-issued\n * tokens on /token/issue, so this works for sessions established via Central Auth where\n * the browser has no Firebase user.\n */\nexport async function fetchComposableTokenFromVertesiaToken(vertesiaToken, accountId, projectId, ttl) {\n return fetchComposableToken(() => Promise.resolve(vertesiaToken), accountId, projectId, ttl);\n}\n/** Returns the cached Vertesia raw JWT, if any. Does not refresh. */\nexport function getCurrentVertesiaToken() {\n return AUTH_TOKEN_RAW;\n}\nexport async function getComposableToken(accountId, projectId, initToken, forceRefresh = false, useInternalAuth = false) {\n const selectedAccount = accountId ??\n (projectId === undefined ? (localStorage.getItem(LastSelectedAccountId_KEY) ?? undefined) : undefined);\n const selectedProject = projectId ?? localStorage.getItem(`${LastSelectedProjectId_KEY}-${selectedAccount}`) ?? undefined;\n const devAuthToken = Env.isLocalDev ? Env.devAuthToken : undefined;\n const suppliedToken = devAuthToken ?? initToken ?? AUTH_TOKEN_RAW;\n const cachedTokenMatchesScope = (!selectedAccount || AUTH_TOKEN?.account?.id === selectedAccount) &&\n (!selectedProject || AUTH_TOKEN?.project?.id === selectedProject);\n // Token is still valid for more than 5 minutes and belongs to the requested scope.\n if (!forceRefresh &&\n AUTH_TOKEN_RAW &&\n AUTH_TOKEN &&\n cachedTokenMatchesScope &&\n AUTH_TOKEN.exp > Date.now() / 1000 + 300) {\n return { rawToken: AUTH_TOKEN_RAW, token: AUTH_TOKEN, error: false };\n }\n if (!forceRefresh &&\n isVertesiaIssuedToken(suppliedToken) &&\n canUseVertesiaTokenDirectly(suppliedToken, selectedAccount, selectedProject)) {\n AUTH_TOKEN_RAW = suppliedToken;\n AUTH_TOKEN = decodeToken(AUTH_TOKEN_RAW);\n if (!AUTH_TOKEN.exp) {\n throw new Error('Invalid composable token');\n }\n return { rawToken: AUTH_TOKEN_RAW, token: AUTH_TOKEN, error: false };\n }\n try {\n //token is close to expire, refresh it\n if (!devAuthToken && !useInternalAuth && getFirebaseAuth().currentUser) {\n //we have a firebase user, get the token from there\n AUTH_TOKEN_RAW = await fetchComposableTokenFromFirebaseToken(selectedAccount, selectedProject);\n }\n else if (!devAuthToken) {\n // Embedded apps can reacquire a fresh credential from their host after their cached token expires.\n const refreshCredential = (await Env.authTokenProvider?.()) ?? initToken ?? AUTH_TOKEN_RAW;\n if (refreshCredential &&\n isVertesiaIssuedToken(refreshCredential) &&\n canUseVertesiaTokenDirectly(refreshCredential, selectedAccount, selectedProject)) {\n AUTH_TOKEN_RAW = refreshCredential;\n }\n else if (refreshCredential) {\n AUTH_TOKEN_RAW = await fetchComposableToken(() => Promise.resolve(refreshCredential), selectedAccount, selectedProject);\n }\n else {\n AUTH_TOKEN_RAW = undefined;\n }\n }\n else if (devAuthToken) {\n AUTH_TOKEN_RAW = devAuthToken;\n }\n }\n catch (error) {\n if (error instanceof RequestedScopeUnavailableError ||\n error instanceof NoAccessibleAccountError ||\n error instanceof CredentialError) {\n AUTH_TOKEN_RAW = undefined;\n AUTH_TOKEN = undefined;\n if (error instanceof RequestedScopeUnavailableError) {\n clearRejectedPersistedScope(selectedAccount, selectedProject);\n }\n }\n throw error;\n }\n if (!AUTH_TOKEN_RAW) {\n Env.logger.error('Cannot acquire a composable token', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n throw new Error('Cannot acquire a composable token');\n }\n AUTH_TOKEN = decodeToken(AUTH_TOKEN_RAW);\n if (!AUTH_TOKEN?.exp || !AUTH_TOKEN_RAW) {\n console.error('Invalid composable token', AUTH_TOKEN);\n Env.logger.error('Invalid composable token', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n throw new Error('Invalid composable token');\n }\n return { rawToken: AUTH_TOKEN_RAW, token: AUTH_TOKEN, error: false };\n}\nexport class UserNotFoundError extends Error {\n email;\n constructor(message, email) {\n super(message);\n this.name = 'UserNotFoundError';\n this.email = email;\n }\n}\nexport class STSError extends Error {\n stsURL;\n status;\n errorCode;\n constructor(message, stsURL, options) {\n super(message, options);\n this.name = 'STSError';\n this.stsURL = stsURL;\n this.status = options?.status;\n this.errorCode = options?.errorCode;\n }\n}\nexport class RequestedScopeUnavailableError extends STSError {\n accountId;\n projectId;\n identity;\n requestedScope;\n selectorSource;\n recoveryUrl;\n constructor(stsURL, accountId, projectId, responseMessage, identity) {\n super(responseMessage ?? 'The requested account or project is not available.', stsURL, {\n status: 403,\n errorCode: REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE,\n });\n this.accountId = accountId;\n this.projectId = projectId;\n this.identity = identity;\n this.name = 'RequestedScopeUnavailableError';\n this.requestedScope = projectId ? 'project' : 'account';\n }\n}\n/** @deprecated Use RequestedScopeUnavailableError. */\nexport const TokenAuthorizationError = RequestedScopeUnavailableError;\nexport class NoAccessibleAccountError extends STSError {\n identity;\n constructor(stsURL, responseMessage, identity) {\n super(responseMessage ?? 'No accessible account is available for this user.', stsURL, {\n status: 403,\n errorCode: NO_ACCESSIBLE_ACCOUNT_ERROR_CODE,\n });\n this.identity = identity;\n this.name = 'NoAccessibleAccountError';\n }\n}\nexport class CredentialError extends STSError {\n constructor(message, stsURL, options) {\n super(message, stsURL, options);\n this.name = 'CredentialError';\n }\n}\nexport class AuthenticationServiceError extends STSError {\n constructor(message, stsURL, options) {\n super(message, stsURL, options);\n this.name = 'AuthenticationServiceError';\n }\n}\nexport function isRequestedScopeUnavailableError(error) {\n return error instanceof RequestedScopeUnavailableError;\n}\nexport function isNoAccessibleAccountError(error) {\n return error instanceof NoAccessibleAccountError;\n}\nexport function isCredentialError(error) {\n return error instanceof CredentialError;\n}\nexport function isAuthenticationServiceError(error) {\n return error instanceof AuthenticationServiceError;\n}\n/**\n * Thrown when the STS rejects sign-in because the current environment (preview/preprod) is\n * restricted to early-access users and this user lacks the `early-access` annotation. The\n * sign-in screen renders a dedicated \"restricted environment\" step for this error.\n * See docs/restrict-access-to-non-production-envs.md.\n */\nexport class RestrictedEnvironmentError extends Error {\n constructor(message) {\n super(message);\n this.name = 'RestrictedEnvironmentError';\n }\n}\n//# sourceMappingURL=composable.js.map","/**\n * The `state` parameter binding a Central Auth round-trip to the tab that started it.\n *\n * Plain functions rather than a hook, because the round-trip is not always started from a\n * component: an expired session is renewed from the token layer, which has no React context to\n * call {@link useAuthState} from. The hook wraps these so both paths write the same record.\n */\nconst AUTH_STATE_KEY = 'auth_state';\nconst STATE_EXPIRY_KEY = 'auth_state_expiry';\nconst STATE_TTL = 5 * 60 * 1000; // 5 min\n/** Mint a state value and record it for {@link verifyAuthState}. */\nexport function generateAuthState() {\n const state = crypto.randomUUID();\n sessionStorage.setItem(AUTH_STATE_KEY, state);\n sessionStorage.setItem(STATE_EXPIRY_KEY, String(Date.now() + STATE_TTL));\n return state;\n}\n/** The reason `returnedState` is unacceptable, or undefined when it is good. */\nexport function verifyAuthState(returnedState) {\n if (!returnedState) {\n return 'Missing state';\n }\n const savedState = sessionStorage.getItem(AUTH_STATE_KEY);\n const expiryTime = parseInt(sessionStorage.getItem(STATE_EXPIRY_KEY) || '0', 10);\n if (savedState !== returnedState) {\n return `State mismatched (${savedState} !== ${returnedState})`;\n }\n if (Date.now() > expiryTime) {\n return 'State expired';\n }\n return undefined;\n}\nexport function clearAuthState() {\n sessionStorage.removeItem(AUTH_STATE_KEY);\n sessionStorage.removeItem(STATE_EXPIRY_KEY);\n}\n//# sourceMappingURL=authState.js.map","import { Env } from '@vertesia/ui/env';\nimport { generateAuthState } from './authState';\nexport function shouldUseFirebaseAuth(_hostname) {\n return window.AUTH_MODE === 'firebase';\n}\nexport function shouldRedirectToCentralAuth(_hostname) {\n return !shouldUseFirebaseAuth();\n}\n/** Build a central-auth round trip without losing the app's requested account/project selection. */\nexport function centralAuthRedirectUrl({ centralAuthUrl, stsEndpoint, returnUrl, state, accountId, projectId, }) {\n const selectedReturnUrl = new URL(returnUrl);\n if (projectId)\n selectedReturnUrl.searchParams.set('p', projectId);\n if (accountId)\n selectedReturnUrl.searchParams.set('a', accountId);\n const url = new URL(centralAuthUrl);\n url.searchParams.set('sts', stsEndpoint);\n url.searchParams.set('redirect_uri', selectedReturnUrl.toString());\n url.searchParams.set('state', state);\n return url;\n}\n/**\n * The page URL to return to after a central-auth round-trip (`redirect_uri`).\n *\n * Apps served under a deep gateway mount carry a `<base href=\"/tenants/<t>/apps/<app>/.../app/\">`\n * that the app-gateway injects at serve time. The in-app router rewrites the address bar relative\n * to the origin rather than that mount, so by the time the auth flow runs `window.location` can\n * read as the bare origin `/`. Building `redirect_uri` from that drops the app path, and the\n * post-login token bounces back to a URL that serves no app.\n *\n * Prefer the live location when it still sits under the mount (preserves the in-app deep route);\n * otherwise fall back to `document.baseURI` — the mount, which the router cannot clobber. With no\n * `<base>` element (the Studio UI), `document.baseURI` is the document URL and its `pathname` is a\n * prefix of the live location, so the live URL is used unchanged — no behavior change there.\n */\nexport function authReturnUrl() {\n const base = new URL(document.baseURI);\n const current = new URL(window.location.href);\n const target = current.pathname.startsWith(base.pathname) ? current : base;\n target.hash = '';\n return target;\n}\n/**\n * The app's mount root URL — the served `<base href>` (or the origin root for the Studio UI).\n *\n * Used for full-reload navigations that intentionally reset to the app root (logout, account /\n * project switch). Building these from a bare `/` drops a gateway-mounted app off its mount and\n * lands on a URL that serves no app; resolving against `document.baseURI` keeps the reload inside\n * the mount. For the Studio UI (no `<base>` element) `document.baseURI` is the origin root, so the\n * behavior is unchanged.\n */\nexport function mountRootUrl() {\n const url = new URL(document.baseURI);\n url.hash = '';\n url.search = '';\n return url;\n}\n/**\n * A short-lived marker saying \"a Central Auth round-trip is in flight for this browser\".\n *\n * It exists for the server that generates index.html. Preload hints are markup, so they start\n * fetching before any script runs: a page load that is going to bounce off to the broker cannot\n * avoid downloading the application by any decision made in the page itself. The server can, but\n * only if it can tell the outgoing leg from the return leg -- and the token comes back in the URL\n * *fragment*, which a server never sees. Hence a cookie: set here, immediately before navigating to\n * the broker, and cleared by the app as soon as it mounts.\n *\n * It carries no identity and grants nothing; it is a hint about which of two page loads this is.\n * The TTL bounds the cost of the case where the app never mounts (the visitor abandons the login\n * screen): after it lapses, cold loads are back to withholding the application preloads.\n */\nconst AUTH_ROUND_TRIP_COOKIE = 'vtsauth';\nconst AUTH_ROUND_TRIP_TTL_SECONDS = 300;\nfunction authRoundTripCookie(value, maxAgeSeconds) {\n // Secure only over https: setting it on http://localhost would make the browser drop the\n // cookie, and local development would silently lose the return-leg preloads.\n const secure = window.location.protocol === 'https:' ? '; Secure' : '';\n return `${AUTH_ROUND_TRIP_COOKIE}=${value}; Path=/; Max-Age=${maxAgeSeconds}; SameSite=Lax${secure}`;\n}\n/** Record that this browser is on its way to the broker. Called for you by {@link redirectToCentralAuth}. */\nexport function markCentralAuthRoundTripStarted() {\n // biome-ignore lint/suspicious/noDocumentCookie: CookieStore is async; this must land before location.replace()\n document.cookie = authRoundTripCookie('1', AUTH_ROUND_TRIP_TTL_SECONDS);\n}\n/**\n * Drop the marker. An app calls this once it is actually rendering, so that its *next* cold load is\n * recognized as an outgoing leg again and does not preload an application it is about to discard.\n */\nexport function clearCentralAuthRoundTripMarker() {\n // biome-ignore lint/suspicious/noDocumentCookie: pairs with the write above; see that comment.\n document.cookie = authRoundTripCookie('', 0);\n}\n/**\n * Start a Central Auth round-trip for the current page.\n *\n * The single place the broker URL is assembled, so the boot-time shortcut in an app's entry module\n * and {@link isCentralAuthRedirectPending}'s counterpart inside `UserSessionProvider` cannot drift\n * into producing different `redirect_uri` / `state` / `sts` values for the same page load.\n */\nexport function redirectToCentralAuth(selection = {}) {\n const url = centralAuthRedirectUrl({\n centralAuthUrl: 'https://internal-auth.vertesia.app/',\n stsEndpoint: Env.endpoints.sts ?? 'https://sts.vertesia.io',\n returnUrl: authReturnUrl(),\n state: generateAuthState(),\n ...selection,\n });\n markCentralAuthRoundTripStarted();\n location.replace(url.toString());\n}\n/**\n * Whether this page load is already certain to end in a Central Auth round-trip, decidable before\n * any application module has run.\n *\n * A fresh `UserSession` holds no token and nothing persists one across loads, so on a cold load\n * `session.isLoggedIn()` in `UserSessionProvider` is always false. Every condition that can keep\n * that provider from redirecting is therefore knowable up front, and this predicate enumerates\n * them in the same order:\n *\n * - Apps configured with Firebase auth sign in in place rather than at the broker;\n * - a host app that injects a token through `Env.authTokenProvider` may not need the broker\n * (and when its token turns out to be empty the provider still redirects — answering `false`\n * here just lets the normal flow decide, which is the safe direction);\n * - local development with a configured dev token skips auth entirely;\n * - a load carrying `#token=…&state=…` *is* the return leg and must be allowed to exchange it.\n *\n * When this returns true the app is going to navigate away, so an entry module can redirect\n * immediately instead of mounting a full React tree, initializing telemetry and warming route\n * chunks that the navigation is about to discard.\n */\nexport function isCentralAuthRedirectPending() {\n if (!shouldRedirectToCentralAuth())\n return false;\n if (Env.authTokenProvider)\n return false;\n if (Env.isLocalDev && Env.devAuthToken)\n return false;\n const hashParams = new URLSearchParams(window.location.hash.substring(1));\n return !(hashParams.get('token') && hashParams.get('state'));\n}\n//# sourceMappingURL=domainRouting.js.map","/** React wrappers for the state record shared with boot-time Central Auth redirects. */\nimport { useCallback } from 'react';\nimport { clearAuthState, generateAuthState, verifyAuthState } from './authState';\nexport function useAuthState() {\n const generateState = useCallback(generateAuthState, []);\n const verifyState = useCallback(verifyAuthState, []);\n const clearState = useCallback(clearAuthState, []);\n return { generateState, verifyState, clearState };\n}\n//# sourceMappingURL=useAuthState.js.map","import { VertesiaClient } from '@vertesia/client';\nimport { Env } from '@vertesia/ui/env';\nimport { jwtDecode } from 'jwt-decode';\nimport { createContext, useContext } from 'react';\nimport { getComposableToken } from './auth/composable';\nimport { authReturnUrl, mountRootUrl, shouldRedirectToCentralAuth } from './auth/domainRouting';\nimport { getFirebaseAuth } from './auth/firebase';\nimport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY } from './constants';\nexport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY };\nconst CENTRAL_AUTH_REDIRECT = 'https://internal-auth.vertesia.app/';\nclass UserSession {\n isLoading = true;\n client;\n authError;\n authToken;\n setSession;\n lastSelectedAccount;\n lastSelectedProject;\n onboardingComplete;\n constructor(client, setSession) {\n if (client) {\n this.client = client;\n }\n else {\n this.client = new VertesiaClient({\n serverUrl: Env.endpoints.studio,\n storeUrl: Env.endpoints.zeno,\n tokenServerUrl: Env.endpoints.sts,\n });\n }\n if (setSession) {\n this.setSession = setSession;\n }\n this.logout = this.logout.bind(this);\n this.signOut = this.signOut.bind(this);\n }\n get store() {\n return this.client.store;\n }\n get user() {\n //compatibility\n return this.authToken;\n }\n get account() {\n //compatibility\n return this.authToken?.account;\n }\n get project() {\n return this.authToken?.project;\n }\n get accounts() {\n //compatibility\n return this.authToken?.accounts;\n }\n get authCallback() {\n return this.rawAuthToken.then((token) => `Bearer ${token}`);\n }\n get rawAuthToken() {\n return getComposableToken().then((res) => {\n const token = res?.rawToken;\n if (!token) {\n throw new Error('No token available');\n }\n this.authToken = jwtDecode(token);\n return token;\n });\n }\n /**\n * Force a fresh Vertesia JWT from STS, bypassing the in-memory cache.\n * Use this when the current token's claims (e.g. `apps`) are suspected\n * stale — STS recomputes the `apps` claim from ACEs on every issuance.\n */\n async refreshAuthToken() {\n const res = await getComposableToken(undefined, undefined, undefined, true);\n const token = res?.rawToken;\n if (!token) {\n throw new Error('No token available');\n }\n this.authToken = jwtDecode(token);\n this.setSession?.(this.clone());\n return this.authToken;\n }\n signOut() {\n //compatibility\n this.logout();\n }\n getAccount() {\n return this.authToken?.account;\n }\n async login(token, options = {}) {\n this.authError = undefined;\n this.isLoading = false;\n this.client.withAuthCallback(() => this.authCallback);\n this.authToken = jwtDecode(token);\n console.log(`Logging in as ${this.authToken?.name} with account ${this.authToken?.account.name} (${this.authToken?.account.id}, and project ${this.authToken?.project?.name} (${this.authToken?.project?.id})`);\n //store selected account in local storage\n localStorage.setItem(LastSelectedAccountId_KEY, this.authToken.account.id);\n localStorage.setItem(`${LastSelectedProjectId_KEY}-${this.authToken.account.id}`, this.authToken.project?.id ?? '');\n // notify the host app of the login\n Env.onLogin?.(this.authToken);\n if (options.loadOnboardingStatus ?? true) {\n await this.fetchOnboardingStatus();\n }\n return Promise.resolve();\n }\n isLoggedIn() {\n return !!this.authToken;\n }\n logout() {\n console.log('Logging out');\n if (shouldRedirectToCentralAuth()) {\n // Redirect to central auth for logout\n // Central auth will handle Firebase logout\n console.log('Using central auth logout');\n this.authError = undefined;\n this.isLoading = false;\n this.authToken = undefined;\n this.setSession = undefined;\n this.client.withAuthCallback(undefined);\n const logoutUrl = new URL(CENTRAL_AUTH_REDIRECT);\n const currentUrl = authReturnUrl();\n logoutUrl.pathname = '/logout';\n logoutUrl.searchParams.set('redirect_uri', currentUrl.toString());\n location.replace(logoutUrl.toString());\n }\n else {\n // Use Firebase logout directly\n console.log('Using Firebase logout');\n const wasLoggedIn = !!this.authToken;\n if (this.authToken) {\n void getFirebaseAuth().signOut();\n }\n this.authError = undefined;\n this.isLoading = false;\n this.authToken = undefined;\n this.setSession = undefined;\n this.client.withAuthCallback(undefined);\n // Navigate to the app root to avoid React rendering errors when\n // unmounting deeply nested route components during logout.\n // Use the mount root (not bare '/') so a gateway-mounted app stays\n // on its mount. Only navigate if user was actually logged in to\n // avoid an infinite reload loop on fresh/incognito sessions.\n if (wasLoggedIn) {\n location.replace(mountRootUrl().toString());\n }\n }\n }\n async switchAccount(targetAccountId) {\n localStorage.setItem(LastSelectedAccountId_KEY, targetAccountId);\n if (this) {\n if (this.account && this.project) {\n localStorage.setItem(`${LastSelectedProjectId_KEY}-${this.account.id}`, this.project.id);\n }\n else if (this.account) {\n localStorage.removeItem(`${LastSelectedProjectId_KEY}-${this.account.id}`);\n }\n }\n const url = mountRootUrl();\n url.searchParams.set('a', targetAccountId);\n window.location.replace(url.toString());\n }\n async switchProject(targetProjectId) {\n if (this.account) {\n localStorage.setItem(`${LastSelectedProjectId_KEY}-${this.account.id}`, targetProjectId);\n }\n const url = mountRootUrl();\n if (this.account?.id) {\n url.searchParams.set('a', this.account.id);\n }\n url.searchParams.set('p', targetProjectId);\n window.location.replace(url.toString());\n }\n async fetchAccounts() {\n return this.client.accounts\n .list()\n .then((accounts) => {\n if (!this.authToken) {\n throw new Error('No token available');\n }\n this.authToken.accounts = accounts;\n this.setSession?.(this.clone());\n })\n .catch((err) => {\n console.error('Failed to fetch accounts', err);\n throw err;\n });\n }\n async fetchProjects(accountId) {\n return this.client.projects\n .list([accountId])\n .then((projects) => {\n if (!this.authToken) {\n throw new Error('No token available');\n }\n this.authToken = {\n ...this.authToken,\n accounts: this.authToken.accounts?.map((account) => {\n if (account.id === accountId) {\n return {\n ...account,\n projects: projects.filter((project) => project.account === accountId),\n };\n }\n return account;\n }),\n };\n this.setSession?.(this.clone());\n })\n .catch((err) => {\n console.error('Failed to fetch projects', err);\n throw err;\n });\n }\n async fetchOnboardingStatus() {\n if (!this.project?.id) {\n return false;\n }\n if (this.onboardingComplete) {\n console.log('Onboarding already completed');\n return false;\n }\n const previousStatus = this.onboardingComplete;\n try {\n const onboarding = await this.client.account.onboardingProgress();\n this.onboardingComplete = Object.values(onboarding).every((value) => value === true);\n if (previousStatus !== this.onboardingComplete) {\n this.setSession?.(this.clone());\n }\n return previousStatus === false && this.onboardingComplete;\n }\n catch (error) {\n console.error('Error fetching onboarding status:', error);\n this.onboardingComplete = false;\n if (previousStatus !== this.onboardingComplete) {\n this.setSession?.(this.clone());\n }\n }\n return false;\n }\n clone() {\n const session = new UserSession(this.client);\n session.isLoading = this.isLoading;\n session.authError = this.authError;\n session.authToken = this.authToken;\n session.setSession = this.setSession;\n session.lastSelectedAccount = this.lastSelectedAccount;\n session.switchAccount = this.switchAccount;\n session.onboardingComplete = this.onboardingComplete;\n return session;\n }\n}\nconst UserSessionContext = createContext(undefined);\nexport function useUserSession() {\n const session = useContext(UserSessionContext);\n if (!session) {\n throw new Error('useUserSession must be used within a UserSessionProvider');\n }\n return session;\n}\nexport { UserSession, UserSessionContext };\n//# sourceMappingURL=UserSession.js.map","import { i18nInstance, NAMESPACE } from '@vertesia/ui/i18n';\nimport { useEffect, useState } from 'react';\nimport { useUserSession } from '../UserSession';\nexport function useCurrentTenant() {\n const { user } = useUserSession();\n const [currentTenant, setCurrentTenant] = useState(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState(null);\n useEffect(() => {\n const loadCurrentTenant = async () => {\n if (!user?.email) {\n setCurrentTenant(null);\n setIsLoading(false);\n return;\n }\n try {\n const response = await fetch('/api/resolve-tenant', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n tenantEmail: user.email,\n }),\n });\n if (response.ok) {\n const tenantData = await response.json();\n if (tenantData) {\n // Convert the resolved tenant data to our TenantConfig format\n setCurrentTenant({\n tenantKey: tenantData.name || 'unknown',\n name: tenantData.label || tenantData.name || 'Unknown',\n domain: tenantData.domain || [],\n firebaseTenantId: tenantData.firebaseTenantId,\n provider: tenantData.provider,\n logo: tenantData.logo,\n });\n }\n else {\n setCurrentTenant(null);\n }\n }\n else {\n setCurrentTenant(null);\n }\n }\n catch (error) {\n console.error('Error loading current tenant:', error);\n setError(i18nInstance.getFixedT(null, NAMESPACE)('errors.failedToLoadTenantConfig'));\n setCurrentTenant(null);\n }\n finally {\n setIsLoading(false);\n }\n };\n void loadCurrentTenant();\n }, [user?.email]);\n return {\n currentTenant,\n isLoading,\n error,\n };\n}\n//# sourceMappingURL=useCurrentTenant.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { useMemo } from 'react';\nimport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY } from './constants';\nimport { UserSession, UserSessionContext } from './UserSession';\nfunction decodeJwtPayload(token) {\n const [, payload] = token.split('.');\n if (!payload) {\n throw new Error('Invalid Vertesia auth token');\n }\n const padded = payload\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n .padEnd(Math.ceil(payload.length / 4) * 4, '=');\n return JSON.parse(atob(padded));\n}\nexport function DevSessionProvider({ children, token }) {\n const session = useMemo(() => {\n const next = new UserSession();\n next.isLoading = false;\n try {\n next.authToken = decodeJwtPayload(token);\n next.client.withAuthCallback(() => Promise.resolve(`Bearer ${token}`));\n localStorage.setItem(LastSelectedAccountId_KEY, next.authToken.account.id);\n localStorage.setItem(`${LastSelectedProjectId_KEY}-${next.authToken.account.id}`, next.authToken.project?.id ?? '');\n }\n catch (error) {\n next.authError = error instanceof Error ? error : new Error(String(error));\n }\n return next.clone();\n }, [token]);\n return _jsx(UserSessionContext.Provider, { value: session, children: children });\n}\n//# sourceMappingURL=DevSessionProvider.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE } from '@vertesia/common';\nimport { Env } from '@vertesia/ui/env';\nimport { onAuthStateChanged } from 'firebase/auth';\nimport { useEffect, useRef, useState } from 'react';\nimport { AuthenticationServiceError, CredentialError, getComposableToken, NoAccessibleAccountError, RequestedScopeUnavailableError, RestrictedEnvironmentError, resolveAuthSelection, UserNotFoundError, } from './auth/composable';\nimport { redirectToCentralAuth, shouldRedirectToCentralAuth } from './auth/domainRouting';\nimport { getFirebaseAuth } from './auth/firebase';\nimport { useAuthState } from './auth/useAuthState';\nimport { UserSession, UserSessionContext } from './UserSession';\nfunction clearAuthHash() {\n const url = new URL(window.location.href);\n url.hash = '';\n window.history.replaceState(window.history.state, '', `${url.pathname}${url.search}`);\n}\nexport function sanitizeRejectedScopeUrl(currentUrl, error, clearHash = false) {\n const sanitizedUrl = new URL(currentUrl);\n const urlAccountId = sanitizedUrl.searchParams.get('a') ?? undefined;\n const urlProjectId = sanitizedUrl.searchParams.get('p') ?? undefined;\n if (urlAccountId && urlProjectId && urlAccountId === error.accountId && urlProjectId === error.projectId) {\n sanitizedUrl.searchParams.delete('a');\n sanitizedUrl.searchParams.delete('p');\n error.requestedScope = 'project';\n error.selectorSource = 'url';\n }\n else if (urlProjectId && urlProjectId === error.projectId) {\n sanitizedUrl.searchParams.delete('p');\n error.requestedScope = 'project';\n error.selectorSource = 'url';\n }\n else if (urlAccountId && urlAccountId === error.accountId) {\n sanitizedUrl.searchParams.delete('a');\n error.requestedScope = 'account';\n error.selectorSource = 'url';\n }\n else {\n error.requestedScope = error.projectId ? 'project' : 'account';\n error.selectorSource = 'persisted';\n }\n if (clearHash)\n sanitizedUrl.hash = '';\n error.recoveryUrl = sanitizedUrl.toString();\n return sanitizedUrl;\n}\nexport function UserSessionProvider({ children, loadOnboardingStatus = true }) {\n const hashParams = new URLSearchParams(location.hash.substring(1));\n const token = hashParams.get('token');\n const state = hashParams.get('state');\n const [session, setSession] = useState(new UserSession());\n const { verifyState, clearState } = useAuthState();\n const hasInitiatedAuthRef = useRef(false);\n const authFlowRef = useRef(undefined);\n const clearRejectedUrlScope = (error, clearHash = false) => {\n const sanitizedUrl = sanitizeRejectedScopeUrl(new URL(window.location.href), error, clearHash);\n window.history.replaceState(window.history.state, '', sanitizedUrl);\n Env.logger.warn('Sanitized an unavailable authentication scope', {\n vertesia: {\n account_id: error.accountId,\n project_id: error.projectId,\n error_code: REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE,\n selector_source: error.selectorSource,\n requested_scope: error.requestedScope,\n recovery_action: 'url_sanitized',\n },\n });\n };\n const surfaceAuthError = (error, clearHash = false) => {\n if (!(error instanceof RequestedScopeUnavailableError ||\n error instanceof NoAccessibleAccountError ||\n error instanceof CredentialError ||\n error instanceof AuthenticationServiceError ||\n error instanceof UserNotFoundError ||\n error instanceof RestrictedEnvironmentError)) {\n return false;\n }\n if (error instanceof RequestedScopeUnavailableError) {\n clearRejectedUrlScope(error, clearHash);\n }\n else if (clearHash) {\n clearAuthHash();\n }\n session.isLoading = false;\n session.authError = error;\n setSession(session.clone());\n return true;\n };\n authFlowRef.current = () => {\n // Make this effect idempotent - only run auth flow once\n if (hasInitiatedAuthRef.current) {\n Env.logger.debug('Skipping duplicate auth flow initiation');\n return;\n }\n hasInitiatedAuthRef.current = true;\n Env.logger.debug('Starting auth flow');\n const currentUrl = new URL(window.location.href);\n const { accountId: selectedAccount, projectId: selectedProject } = resolveAuthSelection(currentUrl);\n Env.logger.debug('Selected account and project', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n if (Env.isLocalDev && Env.devAuthToken) {\n session.setSession = setSession;\n getComposableToken(selectedAccount, selectedProject, Env.devAuthToken)\n .then((res) => {\n session.login(res.rawToken).then(() => setSession(session.clone()));\n })\n .catch((err) => {\n if (surfaceAuthError(err))\n return;\n console.error('Failed to initialize dev auth token', err);\n Env.logger.error('Failed to initialize dev auth token', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n error: err,\n },\n });\n session.isLoading = false;\n session.authError = err instanceof Error ? err : new Error(String(err));\n setSession(session.clone());\n });\n return;\n }\n if (token && state) {\n session.setSession = setSession;\n const validationError = verifyState(state);\n if (validationError) {\n console.error(`Auth: invalid state: ${validationError}`);\n Env.logger.error(`Invalid state: ${validationError}`, {\n vertesia: {\n state: state,\n },\n });\n redirectToCentralAuth({ accountId: selectedAccount, projectId: selectedProject });\n }\n else {\n clearState();\n }\n getComposableToken(selectedAccount, selectedProject, token, false, shouldRedirectToCentralAuth())\n .then((res) => {\n session.login(res.rawToken, { loadOnboardingStatus }).then(() => {\n setSession(session.clone());\n clearAuthHash();\n });\n })\n .catch((err) => {\n if (surfaceAuthError(err, true))\n return;\n console.error('Failed to fetch user token from studio, redirecting to central auth', err);\n Env.logger.error('Failed to fetch user token from studio, redirecting to central auth', {\n vertesia: {\n error: err,\n },\n });\n redirectToCentralAuth({ accountId: selectedAccount, projectId: selectedProject });\n });\n return;\n }\n let cancelled = false;\n let unsubscribe;\n const startFirebaseOrCentralAuth = () => {\n if (cancelled)\n return;\n // If the current host is not in the Firebase allowlist, central auth owns sign-in.\n if (!session.isLoggedIn()) {\n Env.logger.debug('Not logged in & no token/state', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n if (shouldRedirectToCentralAuth()) {\n Env.logger.debug('Redirecting to central auth with selection', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n redirectToCentralAuth({ accountId: selectedAccount, projectId: selectedProject });\n return; // Don't register onAuthStateChanged listener when redirecting\n }\n Env.logger.debug('Host is in Firebase auth allowlist', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n }\n unsubscribe = onAuthStateChanged(getFirebaseAuth(), async (firebaseUser) => {\n if (firebaseUser) {\n Env.logger.debug('Successful login with firebase', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n session.setSession = setSession;\n await getComposableToken(selectedAccount, selectedProject, undefined, false, shouldRedirectToCentralAuth())\n .then((res) => {\n session\n .login(res.rawToken, { loadOnboardingStatus })\n .then(() => setSession(session.clone()));\n })\n .catch((err) => {\n if (surfaceAuthError(err))\n return;\n console.error('Unexpected failure while fetching a user token', err);\n Env.logger.error('Unexpected failure while fetching a user token', {\n vertesia: { account_id: selectedAccount, project_id: selectedProject, error: err },\n });\n session.logout();\n session.isLoading = false;\n session.authError = err;\n setSession(session.clone());\n });\n }\n else {\n // anonymous user\n Env.logger.debug('Using anonymous user', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n session.client.withAuthCallback(undefined);\n session.logout();\n setSession(session.clone());\n }\n });\n };\n if (Env.authTokenProvider) {\n session.setSession = setSession;\n void Env.authTokenProvider()\n .then(async (injectedToken) => {\n if (!injectedToken) {\n startFirebaseOrCentralAuth();\n return;\n }\n const res = await getComposableToken(selectedAccount, selectedProject, injectedToken, false, shouldRedirectToCentralAuth());\n await session.login(res.rawToken, { loadOnboardingStatus });\n if (!cancelled)\n setSession(session.clone());\n })\n .catch((err) => {\n if (surfaceAuthError(err))\n return;\n console.warn('Auth: failed to initialize injected auth token', err);\n Env.logger.warn('Failed to initialize injected auth token', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n error: err,\n },\n });\n session.isLoading = false;\n session.authError = err instanceof Error ? err : new Error(String(err));\n setSession(session.clone());\n });\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n }\n startFirebaseOrCentralAuth();\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n };\n useEffect(() => {\n return authFlowRef.current?.();\n }, []);\n return _jsx(UserSessionContext.Provider, { value: session, children: children });\n}\n//# sourceMappingURL=UserSessionProvider.js.map","import { Env } from '@vertesia/ui/env';\nimport { logEvent } from 'firebase/analytics';\nimport { getFirebaseAnalytics } from './auth/firebase';\nexport function useUXTracking() {\n //identify user in monitoring and UX systems\n const tagUserSession = async (user) => {\n const signupData = window.localStorage.getItem('composableSignupData');\n if (!user) {\n console.error('No user found -- skipping tagging');\n return;\n }\n if (signupData) {\n window.localStorage.removeItem('composableSignupData');\n }\n };\n //send event to analytics and UX systems\n const trackEvent = (eventName, eventProperties) => {\n if (!Env.isProd) {\n console.debug('track event', eventName, eventProperties);\n }\n // GA via firebase — needs a complete Firebase config. A partial one (no apiKey, as in\n // central-auth deployments and local setups) would make the Installations SDK log an\n // async FirebaseError on every event, so analytics is a no-op there.\n if (!Env.firebase?.apiKey) {\n return;\n }\n logEvent(getFirebaseAnalytics(), eventName, { ...eventProperties, debug_mode: !Env.isProd });\n };\n return {\n tagUserSession,\n trackEvent,\n };\n}\n//# sourceMappingURL=useUXTracking.js.map"],"mappings":"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"}
1
+ {"version":3,"file":"vertesia-ui-session.js","names":["_jsx","_jsx"],"sources":["session/constants.js","session/auth/authState.js","session/auth/domainRouting.js","session/auth/firebase.js","session/auth/composable.js","session/auth/useAuthState.js","session/UserSession.js","session/auth/useCurrentTenant.js","session/DevSessionProvider.js","session/UserSessionProvider.js","session/useUXTracking.js"],"sourcesContent":["export const LastSelectedAccountId_KEY = 'composableai.lastSelectedAccountId';\nexport const LastSelectedProjectId_KEY = 'composableai.lastSelectedProjectId';\n//# sourceMappingURL=constants.js.map","/**\n * The `state` parameter binding a Central Auth round-trip to the tab that started it.\n *\n * Plain functions rather than a hook, because the round-trip is not always started from a\n * component: an expired session is renewed from the token layer, which has no React context to\n * call {@link useAuthState} from. The hook wraps these so both paths write the same record.\n */\nconst AUTH_STATE_KEY = 'auth_state';\nconst STATE_EXPIRY_KEY = 'auth_state_expiry';\nconst STATE_TTL = 5 * 60 * 1000; // 5 min\n/** Mint a state value and record it for {@link verifyAuthState}. */\nexport function generateAuthState() {\n const state = crypto.randomUUID();\n sessionStorage.setItem(AUTH_STATE_KEY, state);\n sessionStorage.setItem(STATE_EXPIRY_KEY, String(Date.now() + STATE_TTL));\n return state;\n}\n/** The reason `returnedState` is unacceptable, or undefined when it is good. */\nexport function verifyAuthState(returnedState) {\n if (!returnedState) {\n return 'Missing state';\n }\n const savedState = sessionStorage.getItem(AUTH_STATE_KEY);\n const expiryTime = parseInt(sessionStorage.getItem(STATE_EXPIRY_KEY) || '0', 10);\n if (savedState !== returnedState) {\n return `State mismatched (${savedState} !== ${returnedState})`;\n }\n if (Date.now() > expiryTime) {\n return 'State expired';\n }\n return undefined;\n}\nexport function clearAuthState() {\n sessionStorage.removeItem(AUTH_STATE_KEY);\n sessionStorage.removeItem(STATE_EXPIRY_KEY);\n}\n//# sourceMappingURL=authState.js.map","import { Env } from '@vertesia/ui/env';\nimport { generateAuthState } from './authState';\n/**\n * The broker this app sends users to for sign-in and logout.\n *\n * Hard-coded until now, which meant every consumer of this package reached the same deployment no\n * matter what was running. It is read from the environment so a single app can be pointed at a\n * different broker, and it keeps this default so an app that configures nothing is unaffected.\n */\nexport const DEFAULT_CENTRAL_AUTH_URL = 'https://internal-auth.vertesia.app/';\nexport function centralAuthUrl() {\n const configured = Env.endpoints.auth;\n return configured ? configured : DEFAULT_CENTRAL_AUTH_URL;\n}\nexport function shouldUseFirebaseAuth(_hostname) {\n return window.AUTH_MODE === 'firebase';\n}\nexport function shouldRedirectToCentralAuth(_hostname) {\n return !shouldUseFirebaseAuth();\n}\n/**\n * The page URL to return to after a central-auth round-trip (`redirect_uri`).\n *\n * Apps served under a deep gateway mount carry a `<base href=\"/tenants/<t>/apps/<app>/.../app/\">`\n * that the app-gateway injects at serve time. The in-app router rewrites the address bar relative\n * to the origin rather than that mount, so by the time the auth flow runs `window.location` can\n * read as the bare origin `/`. Building `redirect_uri` from that drops the app path, and the\n * post-login token bounces back to a URL that serves no app.\n *\n * Prefer the live location when it still sits under the mount (preserves the in-app deep route);\n * otherwise fall back to `document.baseURI` — the mount, which the router cannot clobber. With no\n * `<base>` element (the Studio UI), `document.baseURI` is the document URL and its `pathname` is a\n * prefix of the live location, so the live URL is used unchanged — no behavior change there.\n */\nexport function authReturnUrl() {\n const base = new URL(document.baseURI);\n const current = new URL(window.location.href);\n const target = current.pathname.startsWith(base.pathname) ? current : base;\n target.hash = '';\n return target;\n}\n/**\n * The app's mount root URL — the served `<base href>` (or the origin root for the Studio UI).\n *\n * Used for full-reload navigations that intentionally reset to the app root (logout, account /\n * project switch). Building these from a bare `/` drops a gateway-mounted app off its mount and\n * lands on a URL that serves no app; resolving against `document.baseURI` keeps the reload inside\n * the mount. For the Studio UI (no `<base>` element) `document.baseURI` is the origin root, so the\n * behavior is unchanged.\n */\nexport function mountRootUrl() {\n const url = new URL(document.baseURI);\n url.hash = '';\n url.search = '';\n return url;\n}\n/**\n * The broker URL a sign-in or renewal round-trip navigates to.\n *\n * Every parameter goes on through `searchParams`, never by concatenating a query string onto\n * `centralAuth`. The endpoint is configurable, so it may already carry its own query or a\n * fragment -- and appending `?sts=...` to one of those folds the parameter into the existing value\n * or hides it in the fragment, leaving Central Auth with no `sts` at all. `searchParams` also\n * percent-encodes the values, which the interpolated URLs did not.\n */\nexport function buildCentralAuthRedirectUrl(centralAuth, stsUrl, returnUrl, state, selection = {}) {\n const selectedReturnUrl = new URL(returnUrl);\n if (selection.projectId)\n selectedReturnUrl.searchParams.set('p', selection.projectId);\n if (selection.accountId)\n selectedReturnUrl.searchParams.set('a', selection.accountId);\n const url = new URL(centralAuth);\n url.searchParams.set('sts', stsUrl);\n url.searchParams.set('redirect_uri', selectedReturnUrl.toString());\n url.searchParams.set('state', state);\n return url;\n}\n/**\n * A short-lived marker saying \"a Central Auth round-trip is in flight for this browser\".\n *\n * It exists for the server that generates index.html. Preload hints are markup, so they start\n * fetching before any script runs: a page load that is going to bounce off to the broker cannot\n * avoid downloading the application by any decision made in the page itself. The server can, but\n * only if it can tell the outgoing leg from the return leg -- and the token comes back in the URL\n * *fragment*, which a server never sees. Hence a cookie: set here, immediately before navigating to\n * the broker, and cleared by the app as soon as it mounts.\n *\n * It carries no identity and grants nothing; it is a hint about which of two page loads this is.\n * The TTL bounds the cost of the case where the app never mounts (the visitor abandons the login\n * screen): after it lapses, cold loads are back to withholding the application preloads.\n */\nconst AUTH_ROUND_TRIP_COOKIE = 'vtsauth';\nconst AUTH_ROUND_TRIP_TTL_SECONDS = 300;\nfunction authRoundTripCookie(value, maxAgeSeconds) {\n // Secure only over https: setting it on http://localhost would make the browser drop the\n // cookie, and local development would silently lose the return-leg preloads.\n const secure = window.location.protocol === 'https:' ? '; Secure' : '';\n return `${AUTH_ROUND_TRIP_COOKIE}=${value}; Path=/; Max-Age=${maxAgeSeconds}; SameSite=Lax${secure}`;\n}\n/** Record that this browser is on its way to the broker. Called for you by {@link redirectToCentralAuth}. */\nexport function markCentralAuthRoundTripStarted() {\n // biome-ignore lint/suspicious/noDocumentCookie: CookieStore is async; this must land before location.replace()\n document.cookie = authRoundTripCookie('1', AUTH_ROUND_TRIP_TTL_SECONDS);\n}\n/**\n * Drop the marker. An app calls this once it is actually rendering, so that its *next* cold load is\n * recognized as an outgoing leg again and does not preload an application it is about to discard.\n */\nexport function clearCentralAuthRoundTripMarker() {\n // biome-ignore lint/suspicious/noDocumentCookie: pairs with the write above; see that comment.\n document.cookie = authRoundTripCookie('', 0);\n}\n/**\n * Start a Central Auth round-trip for the current page.\n *\n * The single place the broker URL is assembled, so the boot-time shortcut in an app's entry module\n * and {@link isCentralAuthRedirectPending}'s counterpart inside `UserSessionProvider` cannot drift\n * into producing different `redirect_uri` / `state` / `sts` values for the same page load.\n */\nexport function redirectToCentralAuth(selection = {}) {\n const url = buildCentralAuthRedirectUrl(centralAuthUrl(), Env.endpoints.sts ?? 'https://sts.vertesia.io', authReturnUrl(), generateAuthState(), selection);\n markCentralAuthRoundTripStarted();\n location.replace(url.toString());\n}\n/**\n * Whether this page load is already certain to end in a Central Auth round-trip, decidable before\n * any application module has run.\n *\n * A fresh `UserSession` holds no token and nothing persists one across loads, so on a cold load\n * `session.isLoggedIn()` in `UserSessionProvider` is always false. Every condition that can keep\n * that provider from redirecting is therefore knowable up front, and this predicate enumerates\n * them in the same order:\n *\n * - Firebase-allowlisted hosts sign in in place rather than at the broker;\n * - a host app that injects a token through `Env.authTokenProvider` may not need the broker\n * (and when its token turns out to be empty the provider still redirects — answering `false`\n * here just lets the normal flow decide, which is the safe direction);\n * - local development with a configured dev token skips auth entirely;\n * - a load carrying `#token=…&state=…` *is* the return leg and must be allowed to exchange it.\n *\n * When this returns true the app is going to navigate away, so an entry module can redirect\n * immediately instead of mounting a full React tree, initializing telemetry and warming route\n * chunks that the navigation is about to discard.\n */\nexport function isCentralAuthRedirectPending() {\n if (!shouldRedirectToCentralAuth())\n return false;\n if (Env.authTokenProvider)\n return false;\n if (Env.isLocalDev && Env.devAuthToken)\n return false;\n const hashParams = new URLSearchParams(window.location.hash.substring(1));\n return !(hashParams.get('token') && hashParams.get('state'));\n}\n//# sourceMappingURL=domainRouting.js.map","import { Env } from '@vertesia/ui/env';\nimport { getAnalytics } from 'firebase/analytics';\nimport { initializeApp } from 'firebase/app';\nimport { getAuth } from 'firebase/auth';\n// Use lazy initialization to avoid accessing Env before it's initialized\nlet _firebaseApp = null;\nlet _analytics = null;\nlet _firebaseAuth = null;\n// Getters that lazily initialize Firebase components when first accessed\nexport function getFirebaseApp() {\n if (!_firebaseApp) {\n try {\n if (!Env.firebase) {\n throw new Error('Firebase configuration is not available in the environment');\n }\n _firebaseApp = initializeApp(Env.firebase);\n }\n catch (error) {\n console.error('Failed to initialize Firebase app:', error);\n throw new Error('Firebase initialization failed - environment may not be properly initialized', {\n cause: error,\n });\n }\n }\n return _firebaseApp;\n}\nexport function getFirebaseAnalytics() {\n if (!_analytics) {\n _analytics = getAnalytics(getFirebaseApp());\n }\n return _analytics;\n}\nexport function getFirebaseAuth() {\n if (!_firebaseAuth) {\n _firebaseAuth = getAuth(getFirebaseApp());\n _firebaseAuth.tenantId = Env.firebase?.tenantId ?? null;\n }\n return _firebaseAuth;\n}\nexport async function setFirebaseTenant(tenantEmail) {\n if (Env.firebase?.tenantId) {\n getFirebaseAuth().tenantId = Env.firebase.tenantId;\n return {\n firebaseTenantId: Env.firebase.tenantId,\n provider: Env.firebase.providerType ?? 'oidc',\n name: '',\n label: '',\n };\n }\n if (!tenantEmail) {\n console.log('No tenant name or email specified, skipping tenant setup');\n return;\n }\n if (!Env.firebase) {\n console.log('Firebase configuration is not available in the environment');\n return;\n }\n try {\n if (tenantEmail)\n console.log(`Resolving tenant ID from email: ${tenantEmail}`);\n // Add retry logic with exponential backoff\n let retries = 3;\n let retryDelay = 250; // Start with 250ms delay\n while (retries > 0) {\n try {\n // Call the API endpoint to resolve the tenant ID\n const response = await fetch('/api/resolve-tenant', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n tenantEmail: tenantEmail,\n }),\n // Add timeout to prevent hanging requests\n signal: AbortSignal.timeout(5000),\n });\n // Check for network errors\n if (!response) {\n throw new Error('No response received from tenant API');\n }\n // Handle HTTP error responses\n if (!response.ok) {\n // Try to parse the error response\n try {\n const errorData = await response.json();\n console.error('Failed to resolve tenant ID:', errorData.error);\n }\n catch {\n console.error(`Failed to resolve tenant ID: HTTP ${response.status}`);\n }\n // If the error is 404 Not Found, no need to retry\n if (response.status === 404) {\n console.warn(`Tenant not found for ${tenantEmail}`);\n return;\n }\n throw new Error(`HTTP error ${response.status}`);\n }\n // Successfully got a response, parse it\n const data = (await response.json());\n if (data?.firebaseTenantId) {\n const auth = getFirebaseAuth();\n auth.tenantId = data.firebaseTenantId;\n Env.firebase.providerType = data.provider ?? 'oidc';\n console.log(`Tenant ID set to ${auth.tenantId}`);\n return data;\n }\n else {\n console.error(`Invalid response format, missing tenantId for ${tenantEmail}`);\n return; // No need to retry for invalid response format\n }\n }\n catch (fetchError) {\n // Only retry for network-related errors\n if (retries > 1) {\n console.warn(`Tenant resolution failed, retrying in ${retryDelay}ms...`, fetchError);\n await new Promise((resolve) => setTimeout(resolve, retryDelay));\n retryDelay *= 2; // Exponential backoff\n retries--;\n }\n else {\n throw fetchError; // Last retry failed, propagate error\n }\n }\n }\n }\n catch (error) {\n // Final error handler\n console.error('Error setting Firebase tenant:', error instanceof Error ? error.message : 'Unknown error');\n // Continue without tenant ID - authentication will work without multi-tenancy\n // but the user will access the default tenant\n }\n}\nexport async function getFirebaseAuthToken(refresh) {\n const auth = getFirebaseAuth();\n const user = auth.currentUser;\n if (user) {\n return user\n .getIdToken(refresh)\n .then((token) => {\n Env.logger.debug('Got Firebase token', {\n vertesia: {\n user_email: user.email,\n user_name: user.displayName,\n user_id: user.uid,\n refresh: refresh,\n },\n });\n return token;\n })\n .catch((err) => {\n Env.logger.error('Failed to get Firebase token', {\n vertesia: {\n user_email: user.email,\n user_name: user.displayName,\n user_id: user.uid,\n refresh: refresh,\n error: err,\n },\n });\n console.error('Failed to get access token', err);\n return null;\n });\n }\n else {\n Env.logger.warn('No user found');\n return Promise.resolve(null);\n }\n}\n//# sourceMappingURL=firebase.js.map","/**\n * Handle client caching and refresh of auth token\n */\nimport { NO_ACCESSIBLE_ACCOUNT_ERROR_CODE, REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE, RESTRICTED_ENVIRONMENT_ERROR_CODE, } from '@vertesia/common';\nimport { Env } from '@vertesia/ui/env';\nimport { jwtDecode } from 'jwt-decode';\nimport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY } from '../constants';\nimport { generateAuthState } from './authState';\nimport { authReturnUrl, buildCentralAuthRedirectUrl, centralAuthUrl, shouldRedirectToCentralAuth, } from './domainRouting';\nimport { getFirebaseAuth, getFirebaseAuthToken } from './firebase';\nlet AUTH_TOKEN_RAW;\nlet AUTH_TOKEN;\n/**\n * Whether this tab ever held a token.\n *\n * Separates \"the session expired\" from \"there is no session yet\". Only the first is renewed from\n * here; the first load is UserSessionProvider's, and it has the account/project selection that\n * this layer does not.\n */\nlet HAD_SESSION = false;\n/** Session-storage stamp of the last renewal redirect, so the cooldown survives the navigation. */\nconst RENEWAL_ATTEMPT_KEY = 'vt.centralAuthRenewalAt';\nconst RENEWAL_COOLDOWN_MS = 60_000;\n/**\n * Renew an expired Central Auth session by bouncing through the broker.\n *\n * A Central Auth session holds exactly one credential -- the STS JWT, in memory -- and STS refuses\n * it once it has expired. There is no Firebase user in this browser to mint a replacement from and\n * no refresh token, so the only way back is the broker, which still has the user's own session and\n * returns a fresh identity token without prompting. That is precisely what a manual reload does\n * today; doing it here turns a page that dead-ends on \"Cannot acquire a composable token\" into a\n * redirect the user usually does not notice.\n *\n * The cooldown is the important part. If the broker returns a credential STS also rejects, an\n * unguarded redirect bounces the browser between the two forever -- and a redirect loop is a worse\n * failure than the error page it replaced, because it never comes to rest anywhere the user can\n * read. One attempt per minute, recorded in sessionStorage because the module state does not\n * survive the navigation this function starts.\n *\n * Returns whether a redirect was started.\n */\nfunction renewExpiredCentralAuthSession() {\n if (!HAD_SESSION || !shouldRedirectToCentralAuth()) {\n return false;\n }\n let lastAttempt = 0;\n try {\n lastAttempt = Number(sessionStorage.getItem(RENEWAL_ATTEMPT_KEY) ?? '0');\n }\n catch {\n // sessionStorage unavailable -- treated as \"no previous attempt\", so the redirect is\n // allowed once and the failure surfaces normally if it does not stick.\n }\n if (Number.isFinite(lastAttempt) && lastAttempt > 0 && Date.now() - lastAttempt < RENEWAL_COOLDOWN_MS) {\n Env.logger.warn('Central Auth session renewal already attempted; surfacing the failure instead', {\n vertesia: { last_attempt_ms_ago: Date.now() - lastAttempt },\n });\n return false;\n }\n try {\n sessionStorage.setItem(RENEWAL_ATTEMPT_KEY, String(Date.now()));\n }\n catch {\n // Without the stamp the cooldown cannot be enforced across the navigation. Still worth\n // redirecting once: the alternative is a page the user can only fix by reloading manually.\n }\n Env.logger.info('Renewing an expired Central Auth session through the broker');\n const url = buildCentralAuthRedirectUrl(centralAuthUrl(), Env.endpoints.sts ?? 'https://sts.vertesia.io', authReturnUrl(), generateAuthState());\n location.replace(url.toString());\n return true;\n}\nfunction clearRejectedPersistedScope(accountId, projectId) {\n if (!accountId)\n return;\n const projectKey = `${LastSelectedProjectId_KEY}-${accountId}`;\n if (projectId) {\n const persistedProjectMatches = localStorage.getItem(projectKey) === projectId;\n if (persistedProjectMatches) {\n localStorage.removeItem(projectKey);\n }\n if (persistedProjectMatches && localStorage.getItem(LastSelectedAccountId_KEY) === accountId) {\n localStorage.removeItem(LastSelectedAccountId_KEY);\n }\n return;\n }\n if (localStorage.getItem(LastSelectedAccountId_KEY) === accountId) {\n localStorage.removeItem(LastSelectedAccountId_KEY);\n localStorage.removeItem(projectKey);\n }\n}\nfunction identityFromAcceptedToken(token) {\n try {\n const payload = jwtDecode(token);\n if (typeof payload.email !== 'string' || !payload.email)\n return undefined;\n return {\n email: payload.email,\n name: typeof payload.name === 'string' && payload.name ? payload.name : undefined,\n };\n }\n catch {\n return undefined;\n }\n}\nexport function resolveAuthSelection(currentUrl) {\n // A URL selection owns the whole pair: never attach an unrelated configured account/project.\n const hasUrlScope = currentUrl.searchParams.has('a') || currentUrl.searchParams.has('p');\n const defaults = hasUrlScope ? undefined : Env.defaultAuthSelection;\n const urlAccount = currentUrl.searchParams.get('a') ?? defaults?.accountId;\n const urlProject = currentUrl.searchParams.get('p') ?? defaults?.projectId;\n const accountId = urlAccount ??\n (urlProject === undefined ? (localStorage.getItem(LastSelectedAccountId_KEY) ?? undefined) : undefined);\n const projectId = urlProject ?? localStorage.getItem(`${LastSelectedProjectId_KEY}-${accountId}`) ?? undefined;\n return { accountId, projectId };\n}\nfunction normalizeIssuer(value) {\n return value?.replace(/\\/+$/, '');\n}\nfunction decodeToken(token) {\n return jwtDecode(token);\n}\nfunction isVertesiaIssuedToken(token) {\n if (!token)\n return false;\n try {\n const decoded = decodeToken(token);\n return normalizeIssuer(decoded.iss) === normalizeIssuer(Env.endpoints.sts);\n }\n catch {\n return false;\n }\n}\nfunction canUseVertesiaTokenDirectly(token, accountId, projectId) {\n const decoded = decodeToken(token);\n if (!decoded.exp || decoded.exp <= Date.now() / 1000 + 300) {\n return false;\n }\n const hasAuthorizationClaims = Boolean(decoded.permissions?.length ||\n decoded.account_roles?.length ||\n decoded.project_roles?.length ||\n decoded.apps?.length);\n if (!hasAuthorizationClaims) {\n return false;\n }\n if (accountId && decoded.account?.id !== accountId) {\n return false;\n }\n if (projectId && decoded.project?.id !== projectId) {\n return false;\n }\n return true;\n}\nexport async function fetchComposableToken(getIdToken, accountId, projectId, ttl, retryCount = 0) {\n Env.logger.debug('Getting/refreshing composable token', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n retry_count: retryCount,\n },\n });\n const idToken = await getIdToken(); //get from firebase\n if (!idToken) {\n throw new Error('No id token found');\n }\n // Use STS endpoint - either configured or default to sts.vertesia.io\n const stsEndpoint = Env.endpoints.sts;\n Env.logger.debug('Using STS for token generation', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n sts_url: stsEndpoint,\n },\n });\n try {\n // Call STS to generate a user token\n const stsUrl = new URL(`${stsEndpoint}/token/issue`);\n const requestBody = {\n type: 'user',\n account_id: accountId,\n project_id: projectId,\n expires_at: ttl ? Math.floor(Date.now() / 1000) + ttl : undefined,\n };\n const stsRes = await fetch(stsUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${idToken}`, // Firebase token for authentication\n },\n body: JSON.stringify(requestBody),\n }).catch((error) => {\n console.error('Failed to call STS endpoint', error);\n Env.logger.error('Failed to call STS endpoint', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n error: error,\n },\n });\n throw new AuthenticationServiceError('The authentication service could not be reached', stsEndpoint, {\n cause: error,\n });\n });\n if (idToken && stsRes?.status === 404) {\n // User not found in token-server - call ensure-user endpoint\n Env.logger.debug('404: User not found - calling ensure-user endpoint', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes?.status,\n },\n });\n const ensureResponse = await fetch(`${Env.endpoints.studio}/auth/ensure-user`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${idToken}`,\n 'Content-Type': 'application/json',\n },\n });\n if (ensureResponse.status === 412) {\n // No invite - trigger signup\n Env.logger.debug('412: No invite found - signup required', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n },\n });\n const idTokenDecoded = jwtDecode(idToken);\n if (!idTokenDecoded?.email) {\n Env.logger.error('No email found in id token');\n throw new Error('No email found in id token');\n }\n throw new UserNotFoundError('User not found - signup required', idTokenDecoded.email);\n }\n if (ensureResponse.status === 403) {\n // SigninScreen keys the invite-required view off this message.\n Env.logger.warn('403: Customer-domain user requires an invite to join', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n },\n });\n throw new Error('Customer-domain user requires an invite to join');\n }\n if (!ensureResponse.ok) {\n console.error('Failed to ensure user exists', ensureResponse.status);\n Env.logger.error('Failed to ensure user exists', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: ensureResponse.status,\n },\n });\n throw new Error('Failed to ensure user exists');\n }\n // User created/exists - retry token generation\n Env.logger.debug('User ensured - retrying token generation', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n },\n });\n return fetchComposableToken(getIdToken, accountId, projectId, ttl, retryCount);\n }\n if (idToken && stsRes?.status === 412) {\n Env.logger.info(\"412: auth succeeded but user doesn't exist - signup required\", {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes?.status,\n },\n });\n const idTokenDecoded = jwtDecode(idToken);\n if (!idTokenDecoded?.email) {\n Env.logger.error('No email found in id token');\n throw new Error('No email found in id token');\n }\n Env.logger.info('User not found', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n email: idTokenDecoded.email,\n },\n });\n throw new UserNotFoundError('User not found', idTokenDecoded.email);\n }\n if (stsRes.status === 401) {\n Env.logger.warn('STS rejected the identity credential', {\n vertesia: { account_id: accountId, project_id: projectId, status: stsRes.status },\n });\n throw new CredentialError('Your sign-in credentials could not be verified', stsEndpoint, {\n status: stsRes.status,\n });\n }\n if (stsRes.status === 403) {\n const body = (await stsRes.json().catch(() => undefined));\n const identity = identityFromAcceptedToken(idToken);\n if (body?.errorCode === RESTRICTED_ENVIRONMENT_ERROR_CODE) {\n Env.logger.warn('403: User lacks early-access for this restricted environment', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n },\n });\n throw new RestrictedEnvironmentError(body.message ?? \"You don't have sufficient permission to visit unstable environments\");\n }\n if (body?.errorCode === NO_ACCESSIBLE_ACCOUNT_ERROR_CODE) {\n Env.logger.warn('STS identity has no accessible account', {\n vertesia: { error_code: body.errorCode, status: stsRes.status },\n });\n throw new NoAccessibleAccountError(stsEndpoint, body.message, identity);\n }\n // During rolling deployment an older STS may return an uncoded user-token 403.\n // Treat it as a requested-scope rejection; all calls in this function issue user tokens.\n if (!body?.errorCode || body.errorCode === REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE) {\n Env.logger.warn('STS rejected the requested account or project', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes.status,\n error_code: body?.errorCode ?? REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE,\n },\n });\n throw new RequestedScopeUnavailableError(stsEndpoint, accountId, projectId, body?.errorCode ? body.message : undefined, identity);\n }\n Env.logger.error('STS returned an unrecognized forbidden response', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes.status,\n error_code: body.errorCode,\n },\n });\n throw new AuthenticationServiceError('The authentication service rejected the request', stsEndpoint, {\n status: stsRes.status,\n errorCode: body.errorCode,\n cause: body,\n });\n }\n if (!stsRes.ok) {\n const errorText = await stsRes.text();\n console.error('STS token generation failed:', stsRes.status, errorText);\n Env.logger.error('STS token generation failed', {\n vertesia: {\n status: stsRes.status,\n error: errorText,\n account_id: accountId,\n project_id: projectId,\n },\n });\n throw new AuthenticationServiceError('The authentication service could not complete sign-in', stsEndpoint, {\n status: stsRes.status,\n cause: errorText,\n });\n }\n const { token } = await stsRes.json();\n Env.logger.debug('Successfully got token from STS');\n return token;\n }\n catch (error) {\n if (error instanceof UserNotFoundError ||\n error instanceof STSError ||\n error instanceof RestrictedEnvironmentError) {\n // Re-throw typed auth errors to be handled separately in the caller\n throw error;\n }\n if (error instanceof Error && error.message === 'Customer-domain user requires an invite to join')\n throw error;\n console.error('Failed to get composable token from STS', error);\n Env.logger.error('Failed to get composable token from STS', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n error: error,\n },\n });\n throw new AuthenticationServiceError('The authentication service could not complete sign-in', stsEndpoint, {\n cause: error,\n });\n }\n}\n/**\n *\n * @param accountId\n * @param projectId\n * @param ttl time to live for the token in seconds\n * @returns\n */\nexport async function fetchComposableTokenFromFirebaseToken(accountId, projectId, ttl) {\n return fetchComposableToken(getFirebaseAuthToken, accountId, projectId, ttl);\n}\n/**\n * Mint a scoped Vertesia token from an existing Vertesia JWT. STS accepts STS-issued\n * tokens on /token/issue, so this works for sessions established via Central Auth where\n * the browser has no Firebase user.\n */\nexport async function fetchComposableTokenFromVertesiaToken(vertesiaToken, accountId, projectId, ttl) {\n return fetchComposableToken(() => Promise.resolve(vertesiaToken), accountId, projectId, ttl);\n}\n/** Returns the cached Vertesia raw JWT, if any. Does not refresh. */\nexport function getCurrentVertesiaToken() {\n return AUTH_TOKEN_RAW;\n}\nexport async function getComposableToken(accountId, projectId, initToken, forceRefresh = false, useInternalAuth = false) {\n const selectedAccount = accountId ??\n (projectId === undefined ? (localStorage.getItem(LastSelectedAccountId_KEY) ?? undefined) : undefined);\n const selectedProject = projectId ?? localStorage.getItem(`${LastSelectedProjectId_KEY}-${selectedAccount}`) ?? undefined;\n const devAuthToken = Env.isLocalDev ? Env.devAuthToken : undefined;\n const suppliedToken = devAuthToken ?? initToken ?? AUTH_TOKEN_RAW;\n const cachedTokenMatchesScope = (!selectedAccount || AUTH_TOKEN?.account?.id === selectedAccount) &&\n (!selectedProject || AUTH_TOKEN?.project?.id === selectedProject);\n // Token is still valid for more than 5 minutes and belongs to the requested scope.\n if (!forceRefresh &&\n AUTH_TOKEN_RAW &&\n AUTH_TOKEN &&\n cachedTokenMatchesScope &&\n AUTH_TOKEN.exp > Date.now() / 1000 + 300) {\n HAD_SESSION = true;\n return { rawToken: AUTH_TOKEN_RAW, token: AUTH_TOKEN, error: false };\n }\n if (!forceRefresh &&\n isVertesiaIssuedToken(suppliedToken) &&\n canUseVertesiaTokenDirectly(suppliedToken, selectedAccount, selectedProject)) {\n AUTH_TOKEN_RAW = suppliedToken;\n AUTH_TOKEN = decodeToken(AUTH_TOKEN_RAW);\n if (!AUTH_TOKEN.exp) {\n throw new Error('Invalid composable token');\n }\n HAD_SESSION = true;\n return { rawToken: AUTH_TOKEN_RAW, token: AUTH_TOKEN, error: false };\n }\n try {\n //token is close to expire, refresh it\n if (!devAuthToken && !useInternalAuth && getFirebaseAuth().currentUser) {\n //we have a firebase user, get the token from there\n AUTH_TOKEN_RAW = await fetchComposableTokenFromFirebaseToken(selectedAccount, selectedProject);\n }\n else if (!devAuthToken) {\n // Embedded apps can reacquire a fresh credential from their host after their cached token expires.\n const refreshCredential = (await Env.authTokenProvider?.()) ?? initToken ?? AUTH_TOKEN_RAW;\n // `forceRefresh` has to defeat this shortcut, not just the cache above it. A caller\n // asking for a forced refresh wants claims recomputed -- `refreshAuthToken()` exists so\n // a stale `apps` claim can be re-read after an ACE change, and STS recomputes it on\n // every issuance. Adopting the credential we already hold satisfies the check and\n // returns the very token whose claims were suspect, silently making the call a no-op\n // for exactly the sessions that have no other credential to fall back on.\n if (!forceRefresh &&\n refreshCredential &&\n isVertesiaIssuedToken(refreshCredential) &&\n canUseVertesiaTokenDirectly(refreshCredential, selectedAccount, selectedProject)) {\n AUTH_TOKEN_RAW = refreshCredential;\n }\n else if (refreshCredential) {\n AUTH_TOKEN_RAW = await fetchComposableToken(() => Promise.resolve(refreshCredential), selectedAccount, selectedProject);\n }\n else {\n AUTH_TOKEN_RAW = undefined;\n }\n }\n else if (devAuthToken) {\n AUTH_TOKEN_RAW = devAuthToken;\n }\n }\n catch (error) {\n if (error instanceof RequestedScopeUnavailableError ||\n error instanceof NoAccessibleAccountError ||\n error instanceof CredentialError) {\n AUTH_TOKEN_RAW = undefined;\n AUTH_TOKEN = undefined;\n if (error instanceof RequestedScopeUnavailableError) {\n clearRejectedPersistedScope(selectedAccount, selectedProject);\n }\n }\n // An expired Central Auth session presents as a rejected credential: the JWT we sent STS to\n // renew from is the one that timed out. The broker can still vouch for the user. Only this\n // error -- a scope or account failure is about who the user is, and the broker would hand\n // back the same identity and the same refusal.\n if (error instanceof CredentialError) {\n renewExpiredCentralAuthSession();\n }\n throw error;\n }\n if (!AUTH_TOKEN_RAW) {\n Env.logger.error('Cannot acquire a composable token', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n // Reached once the credential has already been discarded -- typically the call after the\n // one that saw STS reject it. Same remedy, and the cooldown keeps this from redirecting a\n // second time for the same expiry.\n renewExpiredCentralAuthSession();\n throw new Error('Cannot acquire a composable token');\n }\n AUTH_TOKEN = decodeToken(AUTH_TOKEN_RAW);\n if (!AUTH_TOKEN?.exp || !AUTH_TOKEN_RAW) {\n console.error('Invalid composable token', AUTH_TOKEN);\n Env.logger.error('Invalid composable token', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n throw new Error('Invalid composable token');\n }\n HAD_SESSION = true;\n return { rawToken: AUTH_TOKEN_RAW, token: AUTH_TOKEN, error: false };\n}\nexport class UserNotFoundError extends Error {\n email;\n constructor(message, email) {\n super(message);\n this.name = 'UserNotFoundError';\n this.email = email;\n }\n}\nexport class STSError extends Error {\n stsURL;\n status;\n errorCode;\n constructor(message, stsURL, options) {\n super(message, options);\n this.name = 'STSError';\n this.stsURL = stsURL;\n this.status = options?.status;\n this.errorCode = options?.errorCode;\n }\n}\nexport class RequestedScopeUnavailableError extends STSError {\n accountId;\n projectId;\n identity;\n requestedScope;\n selectorSource;\n recoveryUrl;\n constructor(stsURL, accountId, projectId, responseMessage, identity) {\n super(responseMessage ?? 'The requested account or project is not available.', stsURL, {\n status: 403,\n errorCode: REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE,\n });\n this.accountId = accountId;\n this.projectId = projectId;\n this.identity = identity;\n this.name = 'RequestedScopeUnavailableError';\n this.requestedScope = projectId ? 'project' : 'account';\n }\n}\n/** @deprecated Use RequestedScopeUnavailableError. */\nexport const TokenAuthorizationError = RequestedScopeUnavailableError;\nexport class NoAccessibleAccountError extends STSError {\n identity;\n constructor(stsURL, responseMessage, identity) {\n super(responseMessage ?? 'No accessible account is available for this user.', stsURL, {\n status: 403,\n errorCode: NO_ACCESSIBLE_ACCOUNT_ERROR_CODE,\n });\n this.identity = identity;\n this.name = 'NoAccessibleAccountError';\n }\n}\nexport class CredentialError extends STSError {\n constructor(message, stsURL, options) {\n super(message, stsURL, options);\n this.name = 'CredentialError';\n }\n}\nexport class AuthenticationServiceError extends STSError {\n constructor(message, stsURL, options) {\n super(message, stsURL, options);\n this.name = 'AuthenticationServiceError';\n }\n}\nexport function isRequestedScopeUnavailableError(error) {\n return error instanceof RequestedScopeUnavailableError;\n}\nexport function isNoAccessibleAccountError(error) {\n return error instanceof NoAccessibleAccountError;\n}\nexport function isCredentialError(error) {\n return error instanceof CredentialError;\n}\nexport function isAuthenticationServiceError(error) {\n return error instanceof AuthenticationServiceError;\n}\n/**\n * Thrown when the STS rejects sign-in because the current environment (preview/preprod) is\n * restricted to early-access users and this user lacks the `early-access` annotation. The\n * sign-in screen renders a dedicated \"restricted environment\" step for this error.\n * See docs/restrict-access-to-non-production-envs.md.\n */\nexport class RestrictedEnvironmentError extends Error {\n constructor(message) {\n super(message);\n this.name = 'RestrictedEnvironmentError';\n }\n}\n//# sourceMappingURL=composable.js.map","/**\n * This hook is used to generate and verify state for OAuth2 authorization requests.\n *\n * The implementation lives in ./authState so the token layer can start the same round-trip when it\n * renews an expired Central Auth session, where no component is available to call a hook.\n */\nimport { useCallback } from 'react';\nimport { clearAuthState, generateAuthState, verifyAuthState } from './authState';\nexport function useAuthState() {\n const generateState = useCallback(() => generateAuthState(), []);\n const verifyState = useCallback((returnedState) => verifyAuthState(returnedState), []);\n const clearState = useCallback(() => clearAuthState(), []);\n return { generateState, verifyState, clearState };\n}\n//# sourceMappingURL=useAuthState.js.map","import { VertesiaClient } from '@vertesia/client';\nimport { Env } from '@vertesia/ui/env';\nimport { jwtDecode } from 'jwt-decode';\nimport { createContext, useContext } from 'react';\nimport { getComposableToken } from './auth/composable';\nimport { authReturnUrl, centralAuthUrl, mountRootUrl, shouldRedirectToCentralAuth } from './auth/domainRouting';\nimport { getFirebaseAuth } from './auth/firebase';\nimport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY } from './constants';\nexport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY };\nclass UserSession {\n isLoading = true;\n client;\n authError;\n authToken;\n setSession;\n lastSelectedAccount;\n lastSelectedProject;\n onboardingComplete;\n constructor(client, setSession) {\n if (client) {\n this.client = client;\n }\n else {\n this.client = new VertesiaClient({\n serverUrl: Env.endpoints.studio,\n storeUrl: Env.endpoints.zeno,\n tokenServerUrl: Env.endpoints.sts,\n });\n }\n if (setSession) {\n this.setSession = setSession;\n }\n this.logout = this.logout.bind(this);\n this.signOut = this.signOut.bind(this);\n }\n get store() {\n return this.client.store;\n }\n get user() {\n //compatibility\n return this.authToken;\n }\n get account() {\n //compatibility\n return this.authToken?.account;\n }\n get project() {\n return this.authToken?.project;\n }\n get accounts() {\n //compatibility\n return this.authToken?.accounts;\n }\n get authCallback() {\n return this.rawAuthToken.then((token) => `Bearer ${token}`);\n }\n get rawAuthToken() {\n return getComposableToken().then((res) => {\n const token = res?.rawToken;\n if (!token) {\n throw new Error('No token available');\n }\n this.authToken = jwtDecode(token);\n return token;\n });\n }\n /**\n * Force a fresh Vertesia JWT from STS, bypassing the in-memory cache.\n * Use this when the current token's claims (e.g. `apps`) are suspected\n * stale — STS recomputes the `apps` claim from ACEs on every issuance.\n */\n async refreshAuthToken() {\n const res = await getComposableToken(undefined, undefined, undefined, true);\n const token = res?.rawToken;\n if (!token) {\n throw new Error('No token available');\n }\n this.authToken = jwtDecode(token);\n this.setSession?.(this.clone());\n return this.authToken;\n }\n signOut() {\n //compatibility\n this.logout();\n }\n getAccount() {\n return this.authToken?.account;\n }\n async login(token, options = {}) {\n this.authError = undefined;\n this.isLoading = false;\n this.client.withAuthCallback(() => this.authCallback);\n this.authToken = jwtDecode(token);\n console.log(`Logging in as ${this.authToken?.name} with account ${this.authToken?.account.name} (${this.authToken?.account.id}, and project ${this.authToken?.project?.name} (${this.authToken?.project?.id})`);\n //store selected account in local storage\n localStorage.setItem(LastSelectedAccountId_KEY, this.authToken.account.id);\n localStorage.setItem(`${LastSelectedProjectId_KEY}-${this.authToken.account.id}`, this.authToken.project?.id ?? '');\n // notify the host app of the login\n Env.onLogin?.(this.authToken);\n if (options.loadOnboardingStatus ?? true) {\n await this.fetchOnboardingStatus();\n }\n return Promise.resolve();\n }\n isLoggedIn() {\n return !!this.authToken;\n }\n logout() {\n console.log('Logging out');\n if (shouldRedirectToCentralAuth()) {\n // Redirect to central auth for logout\n // Central auth will handle Firebase logout\n console.log('Using central auth logout');\n this.authError = undefined;\n this.isLoading = false;\n this.authToken = undefined;\n this.setSession = undefined;\n this.client.withAuthCallback(undefined);\n const logoutUrl = new URL(centralAuthUrl());\n const currentUrl = authReturnUrl();\n logoutUrl.pathname = '/logout';\n logoutUrl.searchParams.set('redirect_uri', currentUrl.toString());\n location.replace(logoutUrl.toString());\n }\n else {\n // Use Firebase logout directly\n console.log('Using Firebase logout');\n const wasLoggedIn = !!this.authToken;\n if (this.authToken) {\n void getFirebaseAuth().signOut();\n }\n this.authError = undefined;\n this.isLoading = false;\n this.authToken = undefined;\n this.setSession = undefined;\n this.client.withAuthCallback(undefined);\n // Navigate to the app root to avoid React rendering errors when\n // unmounting deeply nested route components during logout.\n // Use the mount root (not bare '/') so a gateway-mounted app stays\n // on its mount. Only navigate if user was actually logged in to\n // avoid an infinite reload loop on fresh/incognito sessions.\n if (wasLoggedIn) {\n location.replace(mountRootUrl().toString());\n }\n }\n }\n async switchAccount(targetAccountId) {\n localStorage.setItem(LastSelectedAccountId_KEY, targetAccountId);\n if (this) {\n if (this.account && this.project) {\n localStorage.setItem(`${LastSelectedProjectId_KEY}-${this.account.id}`, this.project.id);\n }\n else if (this.account) {\n localStorage.removeItem(`${LastSelectedProjectId_KEY}-${this.account.id}`);\n }\n }\n const url = mountRootUrl();\n url.searchParams.set('a', targetAccountId);\n window.location.replace(url.toString());\n }\n async switchProject(targetProjectId) {\n if (this.account) {\n localStorage.setItem(`${LastSelectedProjectId_KEY}-${this.account.id}`, targetProjectId);\n }\n const url = mountRootUrl();\n if (this.account?.id) {\n url.searchParams.set('a', this.account.id);\n }\n url.searchParams.set('p', targetProjectId);\n window.location.replace(url.toString());\n }\n async fetchAccounts() {\n return this.client.accounts\n .list()\n .then((accounts) => {\n if (!this.authToken) {\n throw new Error('No token available');\n }\n this.authToken.accounts = accounts;\n this.setSession?.(this.clone());\n })\n .catch((err) => {\n console.error('Failed to fetch accounts', err);\n throw err;\n });\n }\n async fetchProjects(accountId) {\n return this.client.projects\n .list([accountId])\n .then((projects) => {\n if (!this.authToken) {\n throw new Error('No token available');\n }\n this.authToken = {\n ...this.authToken,\n accounts: this.authToken.accounts?.map((account) => {\n if (account.id === accountId) {\n return {\n ...account,\n projects: projects.filter((project) => project.account === accountId),\n };\n }\n return account;\n }),\n };\n this.setSession?.(this.clone());\n })\n .catch((err) => {\n console.error('Failed to fetch projects', err);\n throw err;\n });\n }\n async fetchOnboardingStatus() {\n if (!this.project?.id) {\n return false;\n }\n if (this.onboardingComplete) {\n console.log('Onboarding already completed');\n return false;\n }\n const previousStatus = this.onboardingComplete;\n try {\n const onboarding = await this.client.account.onboardingProgress();\n this.onboardingComplete = Object.values(onboarding).every((value) => value === true);\n if (previousStatus !== this.onboardingComplete) {\n this.setSession?.(this.clone());\n }\n return previousStatus === false && this.onboardingComplete;\n }\n catch (error) {\n console.error('Error fetching onboarding status:', error);\n this.onboardingComplete = false;\n if (previousStatus !== this.onboardingComplete) {\n this.setSession?.(this.clone());\n }\n }\n return false;\n }\n clone() {\n const session = new UserSession(this.client);\n session.isLoading = this.isLoading;\n session.authError = this.authError;\n session.authToken = this.authToken;\n session.setSession = this.setSession;\n session.lastSelectedAccount = this.lastSelectedAccount;\n session.switchAccount = this.switchAccount;\n session.onboardingComplete = this.onboardingComplete;\n return session;\n }\n}\nconst UserSessionContext = createContext(undefined);\nexport function useUserSession() {\n const session = useContext(UserSessionContext);\n if (!session) {\n throw new Error('useUserSession must be used within a UserSessionProvider');\n }\n return session;\n}\nexport { UserSession, UserSessionContext };\n//# sourceMappingURL=UserSession.js.map","import { i18nInstance, NAMESPACE } from '@vertesia/ui/i18n';\nimport { useEffect, useState } from 'react';\nimport { useUserSession } from '../UserSession';\nexport function useCurrentTenant() {\n const { user } = useUserSession();\n const [currentTenant, setCurrentTenant] = useState(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState(null);\n useEffect(() => {\n const loadCurrentTenant = async () => {\n if (!user?.email) {\n setCurrentTenant(null);\n setIsLoading(false);\n return;\n }\n try {\n const response = await fetch('/api/resolve-tenant', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n tenantEmail: user.email,\n }),\n });\n if (response.ok) {\n const tenantData = await response.json();\n if (tenantData) {\n // Convert the resolved tenant data to our TenantConfig format\n setCurrentTenant({\n tenantKey: tenantData.name || 'unknown',\n name: tenantData.label || tenantData.name || 'Unknown',\n domain: tenantData.domain || [],\n firebaseTenantId: tenantData.firebaseTenantId,\n provider: tenantData.provider,\n logo: tenantData.logo,\n });\n }\n else {\n setCurrentTenant(null);\n }\n }\n else {\n setCurrentTenant(null);\n }\n }\n catch (error) {\n console.error('Error loading current tenant:', error);\n setError(i18nInstance.getFixedT(null, NAMESPACE)('errors.failedToLoadTenantConfig'));\n setCurrentTenant(null);\n }\n finally {\n setIsLoading(false);\n }\n };\n void loadCurrentTenant();\n }, [user?.email]);\n return {\n currentTenant,\n isLoading,\n error,\n };\n}\n//# sourceMappingURL=useCurrentTenant.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { useMemo } from 'react';\nimport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY } from './constants';\nimport { UserSession, UserSessionContext } from './UserSession';\nfunction decodeJwtPayload(token) {\n const [, payload] = token.split('.');\n if (!payload) {\n throw new Error('Invalid Vertesia auth token');\n }\n const padded = payload\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n .padEnd(Math.ceil(payload.length / 4) * 4, '=');\n return JSON.parse(atob(padded));\n}\nexport function DevSessionProvider({ children, token }) {\n const session = useMemo(() => {\n const next = new UserSession();\n next.isLoading = false;\n try {\n next.authToken = decodeJwtPayload(token);\n next.client.withAuthCallback(() => Promise.resolve(`Bearer ${token}`));\n localStorage.setItem(LastSelectedAccountId_KEY, next.authToken.account.id);\n localStorage.setItem(`${LastSelectedProjectId_KEY}-${next.authToken.account.id}`, next.authToken.project?.id ?? '');\n }\n catch (error) {\n next.authError = error instanceof Error ? error : new Error(String(error));\n }\n return next.clone();\n }, [token]);\n return _jsx(UserSessionContext.Provider, { value: session, children: children });\n}\n//# sourceMappingURL=DevSessionProvider.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE } from '@vertesia/common';\nimport { Env } from '@vertesia/ui/env';\nimport { onAuthStateChanged } from 'firebase/auth';\nimport { useEffect, useRef, useState } from 'react';\nimport { AuthenticationServiceError, CredentialError, getComposableToken, NoAccessibleAccountError, RequestedScopeUnavailableError, RestrictedEnvironmentError, resolveAuthSelection, UserNotFoundError, } from './auth/composable';\nimport { buildCentralAuthRedirectUrl, redirectToCentralAuth, shouldRedirectToCentralAuth } from './auth/domainRouting';\nimport { getFirebaseAuth } from './auth/firebase';\nimport { useAuthState } from './auth/useAuthState';\nimport { UserSession, UserSessionContext } from './UserSession';\nfunction clearAuthHash() {\n const url = new URL(window.location.href);\n url.hash = '';\n window.history.replaceState(window.history.state, '', `${url.pathname}${url.search}`);\n}\n// Re-exported from its new home in ./auth/domainRouting, which the token layer can import without\n// a cycle -- it renews an expired Central Auth session through the same URL.\nexport { buildCentralAuthRedirectUrl };\nexport function sanitizeRejectedScopeUrl(currentUrl, error, clearHash = false) {\n const sanitizedUrl = new URL(currentUrl);\n const urlAccountId = sanitizedUrl.searchParams.get('a') ?? undefined;\n const urlProjectId = sanitizedUrl.searchParams.get('p') ?? undefined;\n if (urlAccountId && urlProjectId && urlAccountId === error.accountId && urlProjectId === error.projectId) {\n sanitizedUrl.searchParams.delete('a');\n sanitizedUrl.searchParams.delete('p');\n error.requestedScope = 'project';\n error.selectorSource = 'url';\n }\n else if (urlProjectId && urlProjectId === error.projectId) {\n sanitizedUrl.searchParams.delete('p');\n error.requestedScope = 'project';\n error.selectorSource = 'url';\n }\n else if (urlAccountId && urlAccountId === error.accountId) {\n sanitizedUrl.searchParams.delete('a');\n error.requestedScope = 'account';\n error.selectorSource = 'url';\n }\n else {\n error.requestedScope = error.projectId ? 'project' : 'account';\n error.selectorSource = 'persisted';\n }\n if (clearHash)\n sanitizedUrl.hash = '';\n error.recoveryUrl = sanitizedUrl.toString();\n return sanitizedUrl;\n}\nexport function UserSessionProvider({ children, loadOnboardingStatus = true }) {\n const hashParams = new URLSearchParams(location.hash.substring(1));\n const token = hashParams.get('token');\n const state = hashParams.get('state');\n const [session, setSession] = useState(new UserSession());\n const { verifyState, clearState } = useAuthState();\n const hasInitiatedAuthRef = useRef(false);\n const authFlowRef = useRef(undefined);\n const clearRejectedUrlScope = (error, clearHash = false) => {\n const sanitizedUrl = sanitizeRejectedScopeUrl(new URL(window.location.href), error, clearHash);\n window.history.replaceState(window.history.state, '', sanitizedUrl);\n Env.logger.warn('Sanitized an unavailable authentication scope', {\n vertesia: {\n account_id: error.accountId,\n project_id: error.projectId,\n error_code: REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE,\n selector_source: error.selectorSource,\n requested_scope: error.requestedScope,\n recovery_action: 'url_sanitized',\n },\n });\n };\n const surfaceAuthError = (error, clearHash = false) => {\n if (!(error instanceof RequestedScopeUnavailableError ||\n error instanceof NoAccessibleAccountError ||\n error instanceof CredentialError ||\n error instanceof AuthenticationServiceError ||\n error instanceof UserNotFoundError ||\n error instanceof RestrictedEnvironmentError)) {\n return false;\n }\n if (error instanceof RequestedScopeUnavailableError) {\n clearRejectedUrlScope(error, clearHash);\n }\n else if (clearHash) {\n clearAuthHash();\n }\n session.isLoading = false;\n session.authError = error;\n setSession(session.clone());\n return true;\n };\n authFlowRef.current = () => {\n // Make this effect idempotent - only run auth flow once\n if (hasInitiatedAuthRef.current) {\n Env.logger.debug('Skipping duplicate auth flow initiation');\n return;\n }\n hasInitiatedAuthRef.current = true;\n Env.logger.debug('Starting auth flow');\n const currentUrl = new URL(window.location.href);\n const { accountId: selectedAccount, projectId: selectedProject } = resolveAuthSelection(currentUrl);\n Env.logger.debug('Selected account and project', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n if (Env.isLocalDev && Env.devAuthToken) {\n session.setSession = setSession;\n getComposableToken(selectedAccount, selectedProject, Env.devAuthToken)\n .then((res) => {\n return session.login(res.rawToken).then(() => setSession(session.clone()));\n })\n .catch((err) => {\n if (surfaceAuthError(err))\n return;\n console.error('Failed to initialize dev auth token', err);\n Env.logger.error('Failed to initialize dev auth token', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n error: err,\n },\n });\n session.isLoading = false;\n session.authError = err instanceof Error ? err : new Error(String(err));\n setSession(session.clone());\n });\n return;\n }\n if (token && state) {\n session.setSession = setSession;\n const validationError = verifyState(state);\n if (validationError) {\n console.error(`Auth: invalid state: ${validationError}`);\n Env.logger.error(`Invalid state: ${validationError}`, {\n vertesia: {\n state: state,\n },\n });\n redirectToCentralAuth({ accountId: selectedAccount, projectId: selectedProject });\n }\n else {\n clearState();\n }\n getComposableToken(selectedAccount, selectedProject, token, false, shouldRedirectToCentralAuth())\n .then((res) => {\n return session.login(res.rawToken, { loadOnboardingStatus }).then(() => {\n setSession(session.clone());\n clearAuthHash();\n });\n })\n .catch((err) => {\n if (surfaceAuthError(err, true))\n return;\n console.error('Failed to fetch user token from studio, redirecting to central auth', err);\n Env.logger.error('Failed to fetch user token from studio, redirecting to central auth', {\n vertesia: {\n error: err,\n },\n });\n redirectToCentralAuth({ accountId: selectedAccount, projectId: selectedProject });\n });\n return;\n }\n let cancelled = false;\n let unsubscribe;\n const startFirebaseOrCentralAuth = () => {\n if (cancelled)\n return;\n // If the current host is not in the Firebase allowlist, central auth owns sign-in.\n if (!session.isLoggedIn()) {\n Env.logger.debug('Not logged in & no token/state', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n if (shouldRedirectToCentralAuth()) {\n Env.logger.debug('Redirecting to central auth with selection', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n redirectToCentralAuth({ accountId: selectedAccount, projectId: selectedProject });\n return; // Don't register onAuthStateChanged listener when redirecting\n }\n Env.logger.debug('Host is in Firebase auth allowlist', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n }\n unsubscribe = onAuthStateChanged(getFirebaseAuth(), async (firebaseUser) => {\n if (firebaseUser) {\n Env.logger.debug('Successful login with firebase', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n session.setSession = setSession;\n await getComposableToken(selectedAccount, selectedProject, undefined, false, shouldRedirectToCentralAuth())\n .then((res) => {\n return session\n .login(res.rawToken, { loadOnboardingStatus })\n .then(() => setSession(session.clone()));\n })\n .catch((err) => {\n if (surfaceAuthError(err))\n return;\n console.error('Unexpected failure while fetching a user token', err);\n Env.logger.error('Unexpected failure while fetching a user token', {\n vertesia: { account_id: selectedAccount, project_id: selectedProject, error: err },\n });\n session.logout();\n session.isLoading = false;\n session.authError = err;\n setSession(session.clone());\n });\n }\n else {\n // anonymous user\n Env.logger.debug('Using anonymous user', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n session.client.withAuthCallback(undefined);\n session.logout();\n setSession(session.clone());\n }\n });\n };\n if (Env.authTokenProvider) {\n session.setSession = setSession;\n void Env.authTokenProvider()\n .then(async (injectedToken) => {\n if (cancelled)\n return;\n if (!injectedToken) {\n startFirebaseOrCentralAuth();\n return;\n }\n const res = await getComposableToken(selectedAccount, selectedProject, injectedToken, false, shouldRedirectToCentralAuth());\n await session.login(res.rawToken, { loadOnboardingStatus });\n if (!cancelled)\n setSession(session.clone());\n })\n .catch((err) => {\n if (cancelled)\n return;\n if (surfaceAuthError(err))\n return;\n console.warn('Auth: failed to initialize injected auth token', err);\n Env.logger.warn('Failed to initialize injected auth token', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n error: err,\n },\n });\n session.isLoading = false;\n session.authError = err instanceof Error ? err : new Error(String(err));\n setSession(session.clone());\n });\n return () => {\n cancelled = true;\n hasInitiatedAuthRef.current = false;\n unsubscribe?.();\n };\n }\n startFirebaseOrCentralAuth();\n return () => {\n cancelled = true;\n hasInitiatedAuthRef.current = false;\n unsubscribe?.();\n };\n };\n useEffect(() => {\n return authFlowRef.current?.();\n }, []);\n return _jsx(UserSessionContext.Provider, { value: session, children: children });\n}\n//# sourceMappingURL=UserSessionProvider.js.map","import { Env } from '@vertesia/ui/env';\nimport { logEvent } from 'firebase/analytics';\nimport { getFirebaseAnalytics } from './auth/firebase';\nexport function useUXTracking() {\n //identify user in monitoring and UX systems\n const tagUserSession = async (user) => {\n const signupData = window.localStorage.getItem('composableSignupData');\n if (!user) {\n console.error('No user found -- skipping tagging');\n return;\n }\n if (signupData) {\n window.localStorage.removeItem('composableSignupData');\n }\n };\n //send event to analytics and UX systems\n const trackEvent = (eventName, eventProperties) => {\n if (!Env.isProd) {\n console.debug('track event', eventName, eventProperties);\n }\n // GA via firebase — needs a complete Firebase config. A partial one (no apiKey, as in\n // central-auth deployments and local setups) would make the Installations SDK log an\n // async FirebaseError on every event, so analytics is a no-op there.\n if (!Env.firebase?.apiKey) {\n return;\n }\n logEvent(getFirebaseAnalytics(), eventName, { ...eventProperties, debug_mode: !Env.isProd });\n };\n return {\n tagUserSession,\n trackEvent,\n };\n}\n//# sourceMappingURL=useUXTracking.js.map"],"mappings":"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"}