@vertesia/ui 1.5.0-dev.20260804.124748Z → 1.5.0-dev.20260807.073259Z

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 (299) hide show
  1. package/lib/core/components/ComboBox.d.ts.map +1 -1
  2. package/lib/core/components/ComboBox.js +4 -4
  3. package/lib/core/components/ComboBox.js.map +1 -1
  4. package/lib/core/components/FileUpload.js.map +1 -1
  5. package/lib/core/components/MenuList.d.ts +2 -1
  6. package/lib/core/components/MenuList.d.ts.map +1 -1
  7. package/lib/core/components/TagsInput.js +1 -1
  8. package/lib/core/components/popup/PopupController.d.ts.map +1 -1
  9. package/lib/core/components/popup/PopupController.js.map +1 -1
  10. package/lib/core/components/popup/position.js.map +1 -1
  11. package/lib/core/components/popup/utils.js.map +1 -1
  12. package/lib/core/components/shadcn/LanguageSwitcher.js.map +1 -1
  13. package/lib/core/components/shadcn/MessageBox.js.map +1 -1
  14. package/lib/core/components/shadcn/Panel.js +1 -1
  15. package/lib/core/components/shadcn/badge.d.ts +2 -2
  16. package/lib/core/components/shadcn/button.d.ts +2 -2
  17. package/lib/core/components/shadcn/button.js +1 -1
  18. package/lib/core/components/shadcn/command.d.ts +4 -4
  19. package/lib/core/components/shadcn/command.d.ts.map +1 -1
  20. package/lib/core/components/shadcn/filters/DynamicLabel.js +1 -1
  21. package/lib/core/components/shadcn/filters/comboBox/DateCombobox.d.ts.map +1 -1
  22. package/lib/core/components/shadcn/filters/comboBox/DateCombobox.js +1 -1
  23. package/lib/core/components/shadcn/filters/comboBox/SelectCombobox.d.ts.map +1 -1
  24. package/lib/core/components/shadcn/filters/comboBox/SelectCombobox.js +1 -1
  25. package/lib/core/components/shadcn/filters/comboBox/StringListCombobox.d.ts.map +1 -1
  26. package/lib/core/components/shadcn/filters/comboBox/TextCombobox.d.ts.map +1 -1
  27. package/lib/core/components/shadcn/filters/filter/dateFilter.js +1 -1
  28. package/lib/core/components/shadcn/filters/filterBar.d.ts.map +1 -1
  29. package/lib/core/components/shadcn/filters/filters.js.map +1 -1
  30. package/lib/core/components/shadcn/input.d.ts +2 -2
  31. package/lib/core/components/shadcn/modal/dialog.d.ts.map +1 -1
  32. package/lib/core/components/shadcn/popover.d.ts +1 -1
  33. package/lib/core/components/shadcn/popover.d.ts.map +1 -1
  34. package/lib/core/components/shadcn/popover.js.map +1 -1
  35. package/lib/core/components/shadcn/radioGroup.d.ts.map +1 -1
  36. package/lib/core/components/shadcn/selectBox.js +1 -1
  37. package/lib/core/components/shadcn/tabs.d.ts.map +1 -1
  38. package/lib/core/components/shadcn/tabs.js +1 -1
  39. package/lib/core/components/shadcn/text.d.ts +2 -2
  40. package/lib/core/components/shadcn/theme/ThemeSwitcher.js.map +1 -1
  41. package/lib/core/components/toast/ToastProvider.js +1 -1
  42. package/lib/core/hooks/CompositeState.d.ts +1 -1
  43. package/lib/core/hooks/CompositeState.d.ts.map +1 -1
  44. package/lib/core/hooks/CompositeState.js.map +1 -1
  45. package/lib/core/hooks/SharedState.d.ts.map +1 -1
  46. package/lib/core/hooks/useFetch.d.ts.map +1 -1
  47. package/lib/core/hooks/useFetch.js +2 -1
  48. package/lib/core/hooks/useFetch.js.map +1 -1
  49. package/lib/core/hooks/useFlag.d.ts.map +1 -1
  50. package/lib/core/hooks/useIntersectionObserver.js.map +1 -1
  51. package/lib/core/hooks/useSafeLayoutEffect.d.ts +2 -2
  52. package/lib/core/hooks/useSafeLayoutEffect.d.ts.map +1 -1
  53. package/lib/core/hooks/useScrollableSearch.js.map +1 -1
  54. package/lib/core/hooks/useSharedValue.d.ts.map +1 -1
  55. package/lib/core/hooks/useSharedValue.js.map +1 -1
  56. package/lib/core/utils/a11y.d.ts.map +1 -1
  57. package/lib/env/index.d.ts.map +1 -1
  58. package/lib/env/index.js.map +1 -1
  59. package/lib/features/agent/PayloadBuilder.d.ts +1 -1
  60. package/lib/features/agent/PayloadBuilder.d.ts.map +1 -1
  61. package/lib/features/agent/PayloadBuilder.js.map +1 -1
  62. package/lib/features/agent/chat/ArtifactsTab.js +1 -1
  63. package/lib/features/agent/chat/AskUserWidget.js +1 -1
  64. package/lib/features/agent/chat/DocumentEditingActionCard.js +1 -1
  65. package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  66. package/lib/features/agent/chat/ModernAgentConversation.js +26 -3
  67. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  68. package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.js +1 -1
  69. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.d.ts.map +1 -1
  70. package/lib/features/agent/chat/ModernAgentOutput/AgentMarkdownAnchor.js.map +1 -1
  71. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  72. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +1 -1
  73. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts +7 -0
  74. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts.map +1 -1
  75. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js +25 -0
  76. package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js.map +1 -1
  77. package/lib/features/agent/chat/ModernAgentOutput/BatchProgressPanel.d.ts.map +1 -1
  78. package/lib/features/agent/chat/ModernAgentOutput/InlineSlidingPlanPanel.d.ts.map +1 -1
  79. package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
  80. package/lib/features/agent/chat/ModernAgentOutput/ResourceChip.js +1 -1
  81. package/lib/features/agent/chat/ModernAgentOutput/SlideInPanel.js +1 -1
  82. package/lib/features/agent/chat/ModernAgentOutput/StreamingMessage.d.ts.map +1 -1
  83. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.d.ts.map +1 -1
  84. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js +8 -0
  85. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js.map +1 -1
  86. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.d.ts.map +1 -1
  87. package/lib/features/agent/chat/ModernAgentOutput/ToolCallGroup.js +1 -1
  88. package/lib/features/agent/chat/ModernAgentOutput/WorkstreamTabs.js +1 -1
  89. package/lib/features/agent/chat/ModernAgentOutput/utils.d.ts.map +1 -1
  90. package/lib/features/agent/chat/SlidingThinkingIndicator.js +1 -1
  91. package/lib/features/agent/chat/VegaLiteChart.js +1 -1
  92. package/lib/features/agent/chat/VegaLiteChart.js.map +1 -1
  93. package/lib/features/agent/chat/hooks/useAgentStream.d.ts.map +1 -1
  94. package/lib/features/agent/chat/hooks/useAgentStream.js +26 -0
  95. package/lib/features/agent/chat/hooks/useAgentStream.js.map +1 -1
  96. package/lib/features/agent/chat/hooks/useFileProcessing.d.ts +8 -1
  97. package/lib/features/agent/chat/hooks/useFileProcessing.d.ts.map +1 -1
  98. package/lib/features/agent/chat/hooks/useFileProcessing.js +31 -2
  99. package/lib/features/agent/chat/hooks/useFileProcessing.js.map +1 -1
  100. package/lib/features/agent/chat/useArtifactUrlCache.js.map +1 -1
  101. package/lib/features/agent/chat/workstreams.d.ts +1 -1
  102. package/lib/features/facets/AgentRunnerFacetsNav.d.ts.map +1 -1
  103. package/lib/features/facets/CollectionsFacetsNav.d.ts.map +1 -1
  104. package/lib/features/facets/DocumentsFacetsNav.d.ts.map +1 -1
  105. package/lib/features/facets/InteractionsFacetsNav.d.ts.map +1 -1
  106. package/lib/features/facets/PromptsFacetsNav.d.ts.map +1 -1
  107. package/lib/features/facets/RunsFacetsNav.d.ts.map +1 -1
  108. package/lib/features/facets/WorkflowExecutionsFacetsNav.d.ts.map +1 -1
  109. package/lib/features/magic-pdf/GroundedExtractionView.js +1 -1
  110. package/lib/features/magic-pdf/MagicPdfProvider.d.ts.map +1 -1
  111. package/lib/features/magic-pdf/MagicPdfProvider.js.map +1 -1
  112. package/lib/features/oauth/McpConnectionsButton.js +1 -1
  113. package/lib/features/oauth/OAuthProviderConnectButton.js +1 -1
  114. package/lib/features/oauth/RemoteMcpConnectionButton.js +1 -1
  115. package/lib/features/pdf-viewer/PdfPageRenderer.js +1 -1
  116. package/lib/features/pdf-viewer/PdfPageSlider.js +1 -1
  117. package/lib/features/permissions/UserPermissionsProvider.d.ts.map +1 -1
  118. package/lib/features/permissions/UserPermissionsProvider.js.map +1 -1
  119. package/lib/features/store/collections/CollectionsTable.js +2 -2
  120. package/lib/features/store/collections/CollectionsTable.js.map +1 -1
  121. package/lib/features/store/collections/CreateCollection.js +1 -1
  122. package/lib/features/store/collections/EditCollectionView.js +1 -1
  123. package/lib/features/store/objects/DocumentQuickFilter.js +1 -1
  124. package/lib/features/store/objects/DocumentSelectionProvider.d.ts.map +1 -1
  125. package/lib/features/store/objects/DocumentSelectionProvider.js.map +1 -1
  126. package/lib/features/store/objects/components/ContentDispositionButton.d.ts +2 -1
  127. package/lib/features/store/objects/components/ContentDispositionButton.d.ts.map +1 -1
  128. package/lib/features/store/objects/components/ContentOverview.js +1 -1
  129. package/lib/features/store/objects/components/DocumentEditingPanel.js +1 -1
  130. package/lib/features/store/objects/components/PropertiesEditorModal.js +1 -1
  131. package/lib/features/store/objects/components/TextEditorPanel.js +1 -1
  132. package/lib/features/store/objects/components/documentEditingTarget.d.ts.map +1 -1
  133. package/lib/features/store/objects/components/useContentPanelHooks.d.ts +2 -2
  134. package/lib/features/store/objects/components/useContentPanelHooks.d.ts.map +1 -1
  135. package/lib/features/store/objects/layout/DocumentTableColumn.d.ts +1 -1
  136. package/lib/features/store/objects/layout/DocumentTableColumn.d.ts.map +1 -1
  137. package/lib/features/store/objects/layout/DocumentTableColumn.js.map +1 -1
  138. package/lib/features/store/objects/layout/documentLayout.js +1 -1
  139. package/lib/features/store/objects/layout/renderers.d.ts.map +1 -1
  140. package/lib/features/store/objects/search/DocumentSearchContext.d.ts +25 -25
  141. package/lib/features/store/objects/search/DocumentSearchContext.d.ts.map +1 -1
  142. package/lib/features/store/objects/search/DocumentSearchContext.js.map +1 -1
  143. package/lib/features/store/objects/selection/ObjectsActionContextClass.d.ts.map +1 -1
  144. package/lib/features/store/objects/selection/ObjectsActionContextClass.js.map +1 -1
  145. package/lib/features/store/objects/selection/SelectionActions.js +1 -1
  146. package/lib/features/store/objects/selection/actions/ConfirmAction.js +1 -1
  147. package/lib/features/store/objects/selection/actions/DeleteObjectsAction.js +1 -1
  148. package/lib/features/store/objects/selection/actions/StartWorkflowButton.js +1 -1
  149. package/lib/features/store/objects/upload/DocumentUploadModal.js +1 -1
  150. package/lib/features/store/objects/upload/useSmartFileUploadProcessing.d.ts.map +1 -1
  151. package/lib/features/store/objects/upload/useUploadHandler.d.ts.map +1 -1
  152. package/lib/features/store/types/ContentObjectTypesSearch.js +1 -1
  153. package/lib/features/store/types/IntakePolicyEditor.js +1 -1
  154. package/lib/features/store/types/TypeRegistry.d.ts +240 -180
  155. package/lib/features/store/types/TypeRegistry.d.ts.map +1 -1
  156. package/lib/features/store/types/TypeRegistry.js.map +1 -1
  157. package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts +239 -179
  158. package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts.map +1 -1
  159. package/lib/features/store/types/search/ObjectTypeSearchContext.js.map +1 -1
  160. package/lib/features/user/UserInfo.d.ts +14 -14
  161. package/lib/features/user/UserInfo.js +1 -1
  162. package/lib/features/user/UserInfo.js.map +1 -1
  163. package/lib/features/utils/mimeType.d.ts.map +1 -1
  164. package/lib/features/utils/rendition.js.map +1 -1
  165. package/lib/features/utils/text.js.map +1 -1
  166. package/lib/i18n/locales/ar.json +25 -0
  167. package/lib/i18n/locales/de.json +25 -0
  168. package/lib/i18n/locales/en.json +25 -0
  169. package/lib/i18n/locales/es.json +25 -0
  170. package/lib/i18n/locales/fr.json +25 -0
  171. package/lib/i18n/locales/it.json +25 -0
  172. package/lib/i18n/locales/ja.json +25 -0
  173. package/lib/i18n/locales/ko.json +25 -0
  174. package/lib/i18n/locales/pt.json +25 -0
  175. package/lib/i18n/locales/ru.json +25 -0
  176. package/lib/i18n/locales/tr.json +25 -0
  177. package/lib/i18n/locales/zh-TW.json +25 -0
  178. package/lib/i18n/locales/zh.json +25 -0
  179. package/lib/i18n/rtl.d.ts +1 -1
  180. package/lib/i18n/rtl.d.ts.map +1 -1
  181. package/lib/layout/FullHeightLayout.d.ts +7 -6
  182. package/lib/layout/FullHeightLayout.d.ts.map +1 -1
  183. package/lib/layout/Sidebar.d.ts +1 -1
  184. package/lib/layout/Sidebar.js +1 -1
  185. package/lib/rich-text/EditorToolbar.js +1 -1
  186. package/lib/router/HistoryNavigator.d.ts +2 -2
  187. package/lib/router/HistoryNavigator.d.ts.map +1 -1
  188. package/lib/router/HistoryNavigator.js.map +1 -1
  189. package/lib/router/PathMatcher.d.ts.map +1 -1
  190. package/lib/router/PathMatcher.js.map +1 -1
  191. package/lib/router/PathWithParams.d.ts.map +1 -1
  192. package/lib/router/Redirect.js.map +1 -1
  193. package/lib/router/Route404.js +1 -1
  194. package/lib/router/Router.d.ts.map +1 -1
  195. package/lib/router/Router.js.map +1 -1
  196. package/lib/session/UserSession.d.ts +1 -1
  197. package/lib/session/UserSession.d.ts.map +1 -1
  198. package/lib/session/UserSession.js.map +1 -1
  199. package/lib/session/UserSessionProvider.d.ts +2 -0
  200. package/lib/session/UserSessionProvider.d.ts.map +1 -1
  201. package/lib/session/UserSessionProvider.js +81 -53
  202. package/lib/session/UserSessionProvider.js.map +1 -1
  203. package/lib/session/auth/composable.d.ts +49 -1
  204. package/lib/session/auth/composable.d.ts.map +1 -1
  205. package/lib/session/auth/composable.js +179 -53
  206. package/lib/session/auth/composable.js.map +1 -1
  207. package/lib/shell/VertesiaShell.js +1 -1
  208. package/lib/shell/apps/AppInstallationProvider.d.ts +58 -50
  209. package/lib/shell/apps/AppInstallationProvider.d.ts.map +1 -1
  210. package/lib/shell/apps/AppProjectSelector.js +1 -1
  211. package/lib/shell/login/SignInEmailStep.js +1 -1
  212. package/lib/shell/login/SignInPrimitives.d.ts +21 -20
  213. package/lib/shell/login/SignInPrimitives.d.ts.map +1 -1
  214. package/lib/shell/login/SignInPrimitives.js.map +1 -1
  215. package/lib/shell/login/SignInRecoveryStep.d.ts +22 -0
  216. package/lib/shell/login/SignInRecoveryStep.d.ts.map +1 -0
  217. package/lib/shell/login/SignInRecoveryStep.js +59 -0
  218. package/lib/shell/login/SignInRecoveryStep.js.map +1 -0
  219. package/lib/shell/login/SigninScreen.d.ts.map +1 -1
  220. package/lib/shell/login/SigninScreen.js +84 -11
  221. package/lib/shell/login/SigninScreen.js.map +1 -1
  222. package/lib/shell/login/SignupForm.js +1 -1
  223. package/lib/shell/login/TerminalLogin.js +1 -1
  224. package/lib/shell/login/UserSessionMenu.js +1 -1
  225. package/lib/vertesia-ui-core.js.map +1 -1
  226. package/lib/vertesia-ui-features.js +26 -26
  227. package/lib/vertesia-ui-features.js.map +1 -1
  228. package/lib/vertesia-ui-i18n.js +13 -13
  229. package/lib/vertesia-ui-layout.js.map +1 -1
  230. package/lib/vertesia-ui-rich-text.js.map +1 -1
  231. package/lib/vertesia-ui-router.js.map +1 -1
  232. package/lib/vertesia-ui-session.js +1 -1
  233. package/lib/vertesia-ui-session.js.map +1 -1
  234. package/lib/vertesia-ui-shell.js +1 -1
  235. package/lib/vertesia-ui-shell.js.map +1 -1
  236. package/lib/vertesia-ui-widgets.js.map +1 -1
  237. package/lib/widgets/form/FormContext.d.ts.map +1 -1
  238. package/lib/widgets/form/FormContext.js.map +1 -1
  239. package/lib/widgets/form/ManagedObject.d.ts +2 -2
  240. package/lib/widgets/form/ManagedObject.d.ts.map +1 -1
  241. package/lib/widgets/form/ManagedObject.js.map +1 -1
  242. package/lib/widgets/form/schema.d.ts.map +1 -1
  243. package/lib/widgets/form/schema.js.map +1 -1
  244. package/lib/widgets/json-view/JSONCode.js +1 -1
  245. package/lib/widgets/json-view/JSONTable.js +1 -1
  246. package/lib/widgets/markdown/ArtifactContentRenderer.js.map +1 -1
  247. package/lib/widgets/markdown/CodeBlockPlaceholder.d.ts +1 -1
  248. package/lib/widgets/markdown/CodeBlockPlaceholder.d.ts.map +1 -1
  249. package/lib/widgets/markdown/CodeBlockPlaceholder.js +1 -1
  250. package/lib/widgets/markdown/CodeBlockRendering.d.ts.map +1 -1
  251. package/lib/widgets/markdown/CollaborativeMarkdownRenderer.js +1 -1
  252. package/lib/widgets/markdown/remarkDirectiveHandler.d.ts.map +1 -1
  253. package/lib/widgets/markdown/textDiff.js.map +1 -1
  254. package/lib/widgets/monacoEditor/MonacoEditor.d.ts.map +1 -1
  255. package/lib/widgets/monacoEditor/MonacoEditor.js.map +1 -1
  256. package/lib/widgets/schema-editor/ManagedSchema.d.ts.map +1 -1
  257. package/lib/widgets/schema-editor/editor/PropertyEditor.js +1 -1
  258. package/lib/widgets/schema-editor/json-schema4-utils.js.map +1 -1
  259. package/lib/widgets/xml-viewer/components/Elements/index.js +1 -1
  260. package/lib/widgets/xml-viewer/components/Tag/index.js +1 -1
  261. package/lib/widgets/xml-viewer/constants/index.d.ts.map +1 -1
  262. package/lib/widgets/xml-viewer/helpers/index.d.ts +1 -1
  263. package/lib/widgets/xml-viewer/hooks/useCollapsible.d.ts +1 -1
  264. package/lib/widgets/xml-viewer/hooks/useCollapsible.d.ts.map +1 -1
  265. package/package.json +11 -11
  266. package/src/core/hooks/useFetch.ts +2 -1
  267. package/src/features/agent/chat/ModernAgentConversation.test.tsx +13 -0
  268. package/src/features/agent/chat/ModernAgentConversation.tsx +24 -3
  269. package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.test.ts +38 -0
  270. package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.tsx +26 -0
  271. package/src/features/agent/chat/ModernAgentOutput/SummaryConversation.ts +9 -0
  272. package/src/features/agent/chat/ModernAgentOutput/utils.test.ts +21 -0
  273. package/src/features/agent/chat/hooks/useAgentStream.test.tsx +116 -1
  274. package/src/features/agent/chat/hooks/useAgentStream.ts +26 -0
  275. package/src/features/agent/chat/hooks/useFileProcessing.test.tsx +87 -0
  276. package/src/features/agent/chat/hooks/useFileProcessing.ts +32 -1
  277. package/src/features/store/collections/CollectionsTable.tsx +1 -1
  278. package/src/i18n/locales/ar.json +25 -0
  279. package/src/i18n/locales/de.json +25 -0
  280. package/src/i18n/locales/en.json +25 -0
  281. package/src/i18n/locales/es.json +25 -0
  282. package/src/i18n/locales/fr.json +25 -0
  283. package/src/i18n/locales/it.json +25 -0
  284. package/src/i18n/locales/ja.json +25 -0
  285. package/src/i18n/locales/ko.json +25 -0
  286. package/src/i18n/locales/pt.json +25 -0
  287. package/src/i18n/locales/ru.json +25 -0
  288. package/src/i18n/locales/tr.json +25 -0
  289. package/src/i18n/locales/zh-TW.json +25 -0
  290. package/src/i18n/locales/zh.json +25 -0
  291. package/src/session/UserSessionProvider.test.ts +69 -0
  292. package/src/session/UserSessionProvider.tsx +96 -60
  293. package/src/session/auth/composable.scope.test.ts +88 -0
  294. package/src/session/auth/composable.test.ts +205 -0
  295. package/src/session/auth/composable.ts +227 -58
  296. package/src/shell/login/SignInPrimitives.tsx +1 -0
  297. package/src/shell/login/SignInRecoveryStep.test.tsx +112 -0
  298. package/src/shell/login/SignInRecoveryStep.tsx +177 -0
  299. package/src/shell/login/SigninScreen.tsx +145 -27
@@ -0,0 +1,88 @@
1
+ // @vitest-environment jsdom
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ const logger = { info: vi.fn(), warn: vi.fn(), error: vi.fn() };
5
+
6
+ vi.mock('@vertesia/ui/env', () => ({
7
+ Env: {
8
+ endpoints: { sts: 'https://sts.test', studio: 'https://studio.test' },
9
+ logger,
10
+ },
11
+ }));
12
+
13
+ vi.mock('./firebase', () => ({
14
+ getFirebaseAuth: () => ({ currentUser: null }),
15
+ getFirebaseAuthToken: vi.fn(),
16
+ }));
17
+
18
+ function unsignedJwt(payload: Record<string, unknown>): string {
19
+ const encode = (value: string) => btoa(value).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
20
+ return `${encode(JSON.stringify({ alg: 'none', typ: 'JWT' }))}.${encode(JSON.stringify(payload))}.signature`;
21
+ }
22
+
23
+ describe('getComposableToken scope cache', () => {
24
+ beforeEach(() => {
25
+ vi.restoreAllMocks();
26
+ vi.resetModules();
27
+ localStorage.clear();
28
+ });
29
+
30
+ it('does not reuse a valid token from another project', async () => {
31
+ const accountId = 'account-1';
32
+ const projectA = 'project-a';
33
+ const projectB = 'project-b';
34
+ const expiry = Math.floor(Date.now() / 1000) + 3600;
35
+ const token = (project: string) =>
36
+ unsignedJwt({ exp: expiry, account: { id: accountId }, project: { id: project } });
37
+ const response = (project: string) => new Response(JSON.stringify({ token: token(project) }), { status: 200 });
38
+ const fetchMock = vi
39
+ .spyOn(globalThis, 'fetch')
40
+ .mockResolvedValueOnce(response(projectA))
41
+ .mockResolvedValueOnce(response(projectB));
42
+
43
+ const { getComposableToken } = await import('./composable');
44
+ const first = await getComposableToken(accountId, projectA, 'central-auth-credential', true);
45
+ const second = await getComposableToken(accountId, projectB, 'central-auth-credential');
46
+
47
+ expect(first.token.project?.id).toBe(projectA);
48
+ expect(second.token.project?.id).toBe(projectB);
49
+ expect(fetchMock).toHaveBeenCalledTimes(2);
50
+ });
51
+
52
+ it('reuses a valid project token for an account-scoped request in the same account', async () => {
53
+ const accountId = 'account-1';
54
+ const projectId = 'project-a';
55
+ const expiry = Math.floor(Date.now() / 1000) + 3600;
56
+ const token = unsignedJwt({ exp: expiry, account: { id: accountId }, project: { id: projectId } });
57
+ const fetchMock = vi
58
+ .spyOn(globalThis, 'fetch')
59
+ .mockResolvedValueOnce(new Response(JSON.stringify({ token }), { status: 200 }));
60
+
61
+ const { getComposableToken } = await import('./composable');
62
+ await getComposableToken(accountId, projectId, 'central-auth-credential', true);
63
+ const accountScoped = await getComposableToken(accountId);
64
+
65
+ expect(accountScoped.token.project?.id).toBe(projectId);
66
+ expect(fetchMock).toHaveBeenCalledTimes(1);
67
+ });
68
+
69
+ it('reuses a valid token for a global request', async () => {
70
+ const expiry = Math.floor(Date.now() / 1000) + 3600;
71
+ const token = unsignedJwt({
72
+ exp: expiry,
73
+ account: { id: 'account-1' },
74
+ project: { id: 'project-a' },
75
+ });
76
+ const fetchMock = vi
77
+ .spyOn(globalThis, 'fetch')
78
+ .mockResolvedValueOnce(new Response(JSON.stringify({ token }), { status: 200 }));
79
+
80
+ const { getComposableToken } = await import('./composable');
81
+ await getComposableToken('account-1', 'project-a', 'central-auth-credential', true);
82
+ const global = await getComposableToken();
83
+
84
+ expect(global.token.account.id).toBe('account-1');
85
+ expect(global.token.project?.id).toBe('project-a');
86
+ expect(fetchMock).toHaveBeenCalledTimes(1);
87
+ });
88
+ });
@@ -30,6 +30,7 @@ describe('getComposableToken', () => {
30
30
 
31
31
  afterEach(() => {
32
32
  vi.unstubAllGlobals();
33
+ vi.clearAllMocks();
33
34
  });
34
35
 
35
36
  it('uses an authorization-bearing STS-issued Vertesia token directly instead of exchanging it', async () => {
@@ -108,4 +109,208 @@ describe('getComposableToken', () => {
108
109
  expect(fetchMock).toHaveBeenCalledOnce();
109
110
  expect(fetchMock.mock.calls[0]?.[0]?.toString()).toBe('https://sts.dev1.vertesia.io/token/issue');
110
111
  });
112
+
113
+ it('does not relax a rejected account and project scope', async () => {
114
+ localStorage.setItem('composableai.lastSelectedAccountId', 'account-a');
115
+ localStorage.setItem('composableai.lastSelectedProjectId-account-a', 'project-a');
116
+ const fetchMock = vi.fn().mockResolvedValue(
117
+ new Response('Project does not belong to account', {
118
+ status: 403,
119
+ }),
120
+ );
121
+ vi.stubGlobal('fetch', fetchMock);
122
+
123
+ const { fetchComposableToken } = await importComposableAuth();
124
+ const getIdToken = vi.fn(async () => 'identity-token');
125
+ await expect(fetchComposableToken(getIdToken, 'account-a', 'project-a')).rejects.toMatchObject({
126
+ name: 'RequestedScopeUnavailableError',
127
+ accountId: 'account-a',
128
+ projectId: 'project-a',
129
+ });
130
+
131
+ expect(fetchMock).toHaveBeenCalledTimes(1);
132
+ expect(localStorage.getItem('composableai.lastSelectedAccountId')).toBe('account-a');
133
+ expect(localStorage.getItem('composableai.lastSelectedProjectId-account-a')).toBe('project-a');
134
+ });
135
+
136
+ it('clears a rejected persisted account/project pair without retrying', async () => {
137
+ localStorage.setItem('composableai.lastSelectedAccountId', 'account-a');
138
+ localStorage.setItem('composableai.lastSelectedProjectId-account-a', 'project-a');
139
+ const fetchMock = vi
140
+ .fn()
141
+ .mockResolvedValue(new Response('Project does not belong to account', { status: 403 }));
142
+ vi.stubGlobal('fetch', fetchMock);
143
+
144
+ const { getComposableToken } = await importComposableAuth();
145
+ await expect(getComposableToken('account-a', 'project-a', 'identity-token', true, true)).rejects.toBeInstanceOf(
146
+ Error,
147
+ );
148
+
149
+ expect(fetchMock).toHaveBeenCalledTimes(1);
150
+ expect(localStorage.getItem('composableai.lastSelectedAccountId')).toBeNull();
151
+ expect(localStorage.getItem('composableai.lastSelectedProjectId-account-a')).toBeNull();
152
+ });
153
+
154
+ it('clears a rejected persisted account-only scope without retrying', async () => {
155
+ localStorage.setItem('composableai.lastSelectedAccountId', 'account-a');
156
+ const fetchMock = vi.fn().mockResolvedValue(new Response('Account access denied', { status: 403 }));
157
+ vi.stubGlobal('fetch', fetchMock);
158
+
159
+ const { getComposableToken } = await importComposableAuth();
160
+ await expect(getComposableToken('account-a', undefined, 'identity-token', true, true)).rejects.toBeInstanceOf(
161
+ Error,
162
+ );
163
+
164
+ expect(fetchMock).toHaveBeenCalledTimes(1);
165
+ expect(localStorage.getItem('composableai.lastSelectedAccountId')).toBeNull();
166
+ });
167
+
168
+ it('does not clear an unrelated persisted selection', async () => {
169
+ localStorage.setItem('composableai.lastSelectedAccountId', 'account-b');
170
+ localStorage.setItem('composableai.lastSelectedProjectId-account-b', 'project-b');
171
+ const fetchMock = vi.fn().mockResolvedValue(new Response('Project access denied', { status: 403 }));
172
+ vi.stubGlobal('fetch', fetchMock);
173
+
174
+ const { getComposableToken } = await importComposableAuth();
175
+ await expect(getComposableToken('account-a', 'project-a', 'identity-token', true, true)).rejects.toBeInstanceOf(
176
+ Error,
177
+ );
178
+
179
+ expect(fetchMock).toHaveBeenCalledTimes(1);
180
+ expect(localStorage.getItem('composableai.lastSelectedAccountId')).toBe('account-b');
181
+ expect(localStorage.getItem('composableai.lastSelectedProjectId-account-b')).toBe('project-b');
182
+ });
183
+
184
+ it('preserves an unrelated persisted project within the rejected URL account', async () => {
185
+ localStorage.setItem('composableai.lastSelectedAccountId', 'account-a');
186
+ localStorage.setItem('composableai.lastSelectedProjectId-account-a', 'project-b');
187
+ vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response('Project access denied', { status: 403 })));
188
+
189
+ const { getComposableToken } = await importComposableAuth();
190
+ await expect(getComposableToken('account-a', 'project-a', 'identity-token', true, true)).rejects.toBeInstanceOf(
191
+ Error,
192
+ );
193
+
194
+ expect(localStorage.getItem('composableai.lastSelectedAccountId')).toBe('account-a');
195
+ expect(localStorage.getItem('composableai.lastSelectedProjectId-account-a')).toBe('project-b');
196
+ });
197
+
198
+ it('classifies coded scope failures and keeps the deprecated type alias compatible', async () => {
199
+ vi.stubGlobal(
200
+ 'fetch',
201
+ vi.fn().mockResolvedValue(
202
+ Response.json(
203
+ {
204
+ error: 'Forbidden',
205
+ message: 'safe message',
206
+ errorCode: 'requested_scope_unavailable',
207
+ },
208
+ { status: 403 },
209
+ ),
210
+ ),
211
+ );
212
+
213
+ const { fetchComposableToken, RequestedScopeUnavailableError, TokenAuthorizationError } =
214
+ await importComposableAuth();
215
+ const identityToken = makeJwt({ email: 'leon@example.com', name: 'Leon Ruggiero' });
216
+ const error = await fetchComposableToken(async () => identityToken, 'account-a', 'project-a').catch(
217
+ (caught) => caught,
218
+ );
219
+
220
+ expect(error).toBeInstanceOf(RequestedScopeUnavailableError);
221
+ expect(error).toBeInstanceOf(TokenAuthorizationError);
222
+ expect(error).toMatchObject({
223
+ accountId: 'account-a',
224
+ errorCode: 'requested_scope_unavailable',
225
+ identity: { email: 'leon@example.com', name: 'Leon Ruggiero' },
226
+ message: 'safe message',
227
+ projectId: 'project-a',
228
+ status: 403,
229
+ });
230
+ });
231
+
232
+ it('classifies users with no accessible account separately', async () => {
233
+ vi.stubGlobal(
234
+ 'fetch',
235
+ vi
236
+ .fn()
237
+ .mockResolvedValue(
238
+ Response.json(
239
+ { error: 'Forbidden', message: 'safe message', errorCode: 'no_accessible_account' },
240
+ { status: 403 },
241
+ ),
242
+ ),
243
+ );
244
+
245
+ const { fetchComposableToken, NoAccessibleAccountError, isNoAccessibleAccountError } =
246
+ await importComposableAuth();
247
+ const identityToken = makeJwt({ email: 'leon@example.com' });
248
+ const error = await fetchComposableToken(async () => identityToken).catch((caught) => caught);
249
+
250
+ expect(error).toBeInstanceOf(NoAccessibleAccountError);
251
+ expect(isNoAccessibleAccountError(error)).toBe(true);
252
+ expect(error).toMatchObject({
253
+ errorCode: 'no_accessible_account',
254
+ identity: { email: 'leon@example.com' },
255
+ message: 'safe message',
256
+ status: 403,
257
+ });
258
+ });
259
+
260
+ it.each([
261
+ { status: 401, errorName: 'CredentialError' },
262
+ { status: 500, errorName: 'AuthenticationServiceError' },
263
+ ])(
264
+ 'classifies STS status $status as $errorName without exposing its response body',
265
+ async ({ status, errorName }) => {
266
+ vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response('sensitive internal detail', { status })));
267
+
268
+ const { fetchComposableToken } = await importComposableAuth();
269
+ const error = await fetchComposableToken(async () => 'identity-token').catch((caught) => caught);
270
+
271
+ expect(error).toMatchObject({ name: errorName });
272
+ expect(error.message).not.toContain('sensitive internal detail');
273
+ },
274
+ );
275
+ });
276
+
277
+ describe('resolveAuthSelection', () => {
278
+ beforeEach(() => {
279
+ localStorage.clear();
280
+ localStorage.setItem('composableai.lastSelectedAccountId', 'stored-account');
281
+ localStorage.setItem('composableai.lastSelectedProjectId-stored-account', 'stored-project');
282
+ localStorage.setItem('composableai.lastSelectedProjectId-url-account', 'account-project');
283
+ });
284
+
285
+ it('does not combine a URL project with a stored account', async () => {
286
+ const { resolveAuthSelection } = await importComposableAuth();
287
+ expect(resolveAuthSelection(new URL('https://app.example.test/?p=url-project'))).toEqual({
288
+ accountId: undefined,
289
+ projectId: 'url-project',
290
+ });
291
+ });
292
+
293
+ it('uses an account and project supplied together by the URL', async () => {
294
+ const { resolveAuthSelection } = await importComposableAuth();
295
+ expect(resolveAuthSelection(new URL('https://app.example.test/?a=url-account&p=url-project'))).toEqual({
296
+ accountId: 'url-account',
297
+ projectId: 'url-project',
298
+ });
299
+ });
300
+
301
+ it('preserves cached project restoration for an account-only URL', async () => {
302
+ const { resolveAuthSelection } = await importComposableAuth();
303
+ expect(resolveAuthSelection(new URL('https://app.example.test/?a=url-account'))).toEqual({
304
+ accountId: 'url-account',
305
+ projectId: 'account-project',
306
+ });
307
+ });
308
+
309
+ it('restores the complete persisted selection when the URL has no scope', async () => {
310
+ const { resolveAuthSelection } = await importComposableAuth();
311
+ expect(resolveAuthSelection(new URL('https://app.example.test/'))).toEqual({
312
+ accountId: 'stored-account',
313
+ projectId: 'stored-project',
314
+ });
315
+ });
111
316
  });