@robosystems/core 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +252 -0
  3. package/actions/entity-actions.d.ts +8 -0
  4. package/actions/entity-actions.js +16 -0
  5. package/actions/graph-actions.d.ts +3 -0
  6. package/actions/graph-actions.js +13 -0
  7. package/auth-components/AppSwitcher.d.ts +6 -0
  8. package/auth-components/AppSwitcher.js +28 -0
  9. package/auth-components/AuthGuard.d.ts +7 -0
  10. package/auth-components/AuthGuard.js +31 -0
  11. package/auth-components/AuthProvider.d.ts +9 -0
  12. package/auth-components/AuthProvider.js +565 -0
  13. package/auth-components/SessionWarningDialog.d.ts +1 -0
  14. package/auth-components/SessionWarningDialog.js +51 -0
  15. package/auth-components/SignInForm.d.ts +20 -0
  16. package/auth-components/SignInForm.js +139 -0
  17. package/auth-components/SignUpForm.d.ts +15 -0
  18. package/auth-components/SignUpForm.js +167 -0
  19. package/auth-components/TurnstileWidget.d.ts +99 -0
  20. package/auth-components/TurnstileWidget.js +224 -0
  21. package/auth-components/index.d.ts +6 -0
  22. package/auth-components/index.js +6 -0
  23. package/auth-core/cleanup.d.ts +44 -0
  24. package/auth-core/cleanup.js +157 -0
  25. package/auth-core/client.d.ts +121 -0
  26. package/auth-core/client.js +767 -0
  27. package/auth-core/config.d.ts +19 -0
  28. package/auth-core/config.js +56 -0
  29. package/auth-core/hooks.d.ts +19 -0
  30. package/auth-core/hooks.js +63 -0
  31. package/auth-core/index.d.ts +6 -0
  32. package/auth-core/index.js +6 -0
  33. package/auth-core/sso.d.ts +42 -0
  34. package/auth-core/sso.js +281 -0
  35. package/auth-core/token-storage.d.ts +58 -0
  36. package/auth-core/token-storage.js +177 -0
  37. package/auth-core/types.d.ts +145 -0
  38. package/auth-core/types.js +1 -0
  39. package/components/EntitySelector.d.ts +44 -0
  40. package/components/EntitySelector.js +64 -0
  41. package/components/EntitySelectorCore.d.ts +45 -0
  42. package/components/EntitySelectorCore.js +67 -0
  43. package/components/GraphSelectorCore.d.ts +21 -0
  44. package/components/GraphSelectorCore.js +39 -0
  45. package/components/PageLayout.d.ts +8 -0
  46. package/components/PageLayout.js +5 -0
  47. package/components/RepositoryGuard.d.ts +68 -0
  48. package/components/RepositoryGuard.js +91 -0
  49. package/components/console/ConsoleContent.d.ts +4 -0
  50. package/components/console/ConsoleContent.js +612 -0
  51. package/components/console/ConsoleMarkdown.d.ts +11 -0
  52. package/components/console/ConsoleMarkdown.js +49 -0
  53. package/components/console/ProgressiveText.d.ts +6 -0
  54. package/components/console/ProgressiveText.js +21 -0
  55. package/components/console/graphAwareConfig.d.ts +70 -0
  56. package/components/console/graphAwareConfig.js +411 -0
  57. package/components/console/index.d.ts +6 -0
  58. package/components/console/index.js +4 -0
  59. package/components/console/types.d.ts +84 -0
  60. package/components/console/types.js +1 -0
  61. package/components/graph-filters.d.ts +75 -0
  62. package/components/graph-filters.js +111 -0
  63. package/components/index.d.ts +10 -0
  64. package/components/index.js +9 -0
  65. package/components/repositories/ActiveSubscriptions.d.ts +26 -0
  66. package/components/repositories/ActiveSubscriptions.js +103 -0
  67. package/components/repositories/BrowseRepositories.d.ts +11 -0
  68. package/components/repositories/BrowseRepositories.js +184 -0
  69. package/components/repositories/index.d.ts +2 -0
  70. package/components/repositories/index.js +2 -0
  71. package/components/search/SearchContent.d.ts +4 -0
  72. package/components/search/SearchContent.js +168 -0
  73. package/components/search/index.d.ts +2 -0
  74. package/components/search/index.js +1 -0
  75. package/components/search/types.d.ts +15 -0
  76. package/components/search/types.js +1 -0
  77. package/contexts/entity-context.d.ts +28 -0
  78. package/contexts/entity-context.js +89 -0
  79. package/contexts/graph-context.d.ts +27 -0
  80. package/contexts/graph-context.js +128 -0
  81. package/contexts/index.d.ts +5 -0
  82. package/contexts/index.js +5 -0
  83. package/contexts/org-context.d.ts +25 -0
  84. package/contexts/org-context.js +64 -0
  85. package/contexts/service-offerings-context.d.ts +45 -0
  86. package/contexts/service-offerings-context.js +98 -0
  87. package/contexts/sidebar-context.d.ts +18 -0
  88. package/contexts/sidebar-context.js +44 -0
  89. package/hooks/index.d.ts +6 -0
  90. package/hooks/index.js +6 -0
  91. package/hooks/use-api-error.d.ts +3 -0
  92. package/hooks/use-api-error.js +24 -0
  93. package/hooks/use-media-query.d.ts +1 -0
  94. package/hooks/use-media-query.js +21 -0
  95. package/hooks/use-streaming-query.d.ts +22 -0
  96. package/hooks/use-streaming-query.js +142 -0
  97. package/hooks/use-toast.d.ts +37 -0
  98. package/hooks/use-toast.js +117 -0
  99. package/hooks/use-user-limits.d.ts +12 -0
  100. package/hooks/use-user-limits.js +73 -0
  101. package/hooks/use-user.d.ts +11 -0
  102. package/hooks/use-user.js +39 -0
  103. package/index.d.ts +29 -0
  104. package/index.js +77 -0
  105. package/lib/entity-cookie.d.ts +10 -0
  106. package/lib/entity-cookie.js +38 -0
  107. package/lib/graph-cookie.d.ts +11 -0
  108. package/lib/graph-cookie.js +62 -0
  109. package/lib/graph-tiers.d.ts +41 -0
  110. package/lib/graph-tiers.js +107 -0
  111. package/lib/index.d.ts +3 -0
  112. package/lib/index.js +3 -0
  113. package/lib/sidebar-cookie.d.ts +11 -0
  114. package/lib/sidebar-cookie.js +42 -0
  115. package/library/colors.d.ts +4 -0
  116. package/library/colors.js +41 -0
  117. package/library/components/ClassificationPicker.d.ts +8 -0
  118. package/library/components/ClassificationPicker.js +55 -0
  119. package/library/components/ElementBrowser.d.ts +11 -0
  120. package/library/components/ElementBrowser.js +106 -0
  121. package/library/components/ElementDetail.d.ts +7 -0
  122. package/library/components/ElementDetail.js +97 -0
  123. package/library/components/LibraryHierarchy.d.ts +13 -0
  124. package/library/components/LibraryHierarchy.js +380 -0
  125. package/library/components/TaxonomySidebar.d.ts +6 -0
  126. package/library/components/TaxonomySidebar.js +45 -0
  127. package/library/index.d.ts +8 -0
  128. package/library/index.js +7 -0
  129. package/package.json +37 -0
  130. package/research/CoverageCard.d.ts +12 -0
  131. package/research/CoverageCard.js +15 -0
  132. package/research/CoverageGrid.d.ts +6 -0
  133. package/research/CoverageGrid.js +9 -0
  134. package/research/CoverageHistory.d.ts +5 -0
  135. package/research/CoverageHistory.js +7 -0
  136. package/research/ResearchArticle.d.ts +15 -0
  137. package/research/ResearchArticle.js +26 -0
  138. package/research/catalog.d.ts +14 -0
  139. package/research/catalog.js +42 -0
  140. package/research/index.d.ts +6 -0
  141. package/research/index.js +9 -0
  142. package/research/types.d.ts +38 -0
  143. package/research/types.js +4 -0
  144. package/task-monitoring/hooks.d.ts +26 -0
  145. package/task-monitoring/hooks.js +126 -0
  146. package/task-monitoring/index.d.ts +5 -0
  147. package/task-monitoring/index.js +7 -0
  148. package/task-monitoring/operationErrors.d.ts +82 -0
  149. package/task-monitoring/operationErrors.js +251 -0
  150. package/task-monitoring/operationHooks.d.ts +117 -0
  151. package/task-monitoring/operationHooks.js +536 -0
  152. package/task-monitoring/operationMonitor.d.ts +78 -0
  153. package/task-monitoring/operationMonitor.js +200 -0
  154. package/task-monitoring/operationTypes.d.ts +236 -0
  155. package/task-monitoring/operationTypes.js +60 -0
  156. package/task-monitoring/taskMonitor.d.ts +14 -0
  157. package/task-monitoring/taskMonitor.js +132 -0
  158. package/task-monitoring/types.d.ts +31 -0
  159. package/task-monitoring/types.js +1 -0
  160. package/theme/flowbite-theme.d.ts +758 -0
  161. package/theme/flowbite-theme.js +765 -0
  162. package/theme/index.d.ts +1 -0
  163. package/theme/index.js +1 -0
  164. package/types/entity.d.ts +19 -0
  165. package/types/index.d.ts +2 -0
  166. package/types/index.js +1 -0
  167. package/types/user.d.ts +6 -0
  168. package/ui-components/ConfirmModal.d.ts +41 -0
  169. package/ui-components/ConfirmModal.js +19 -0
  170. package/ui-components/EmptyState.d.ts +22 -0
  171. package/ui-components/EmptyState.js +10 -0
  172. package/ui-components/LoadingState.d.ts +27 -0
  173. package/ui-components/LoadingState.js +12 -0
  174. package/ui-components/Logo.d.ts +28 -0
  175. package/ui-components/Logo.js +95 -0
  176. package/ui-components/Spinner.d.ts +37 -0
  177. package/ui-components/Spinner.js +48 -0
  178. package/ui-components/StatCard.d.ts +21 -0
  179. package/ui-components/StatCard.js +10 -0
  180. package/ui-components/api-keys/ApiKeyDisplay.d.ts +13 -0
  181. package/ui-components/api-keys/ApiKeyDisplay.js +7 -0
  182. package/ui-components/api-keys/ApiKeyTable.d.ts +9 -0
  183. package/ui-components/api-keys/ApiKeyTable.js +17 -0
  184. package/ui-components/api-keys/ApiKeysCard.d.ts +6 -0
  185. package/ui-components/api-keys/ApiKeysCard.js +97 -0
  186. package/ui-components/api-keys/CreateApiKeyModal.d.ts +11 -0
  187. package/ui-components/api-keys/CreateApiKeyModal.js +47 -0
  188. package/ui-components/api-keys/SecureApiKeyField.d.ts +13 -0
  189. package/ui-components/api-keys/SecureApiKeyField.js +64 -0
  190. package/ui-components/api-keys/index.d.ts +5 -0
  191. package/ui-components/api-keys/index.js +5 -0
  192. package/ui-components/chat/ChatHeader.d.ts +12 -0
  193. package/ui-components/chat/ChatHeader.js +5 -0
  194. package/ui-components/chat/ChatInputArea.d.ts +16 -0
  195. package/ui-components/chat/ChatInputArea.js +9 -0
  196. package/ui-components/chat/ChatMessage.d.ts +7 -0
  197. package/ui-components/chat/ChatMessage.js +24 -0
  198. package/ui-components/chat/DeepResearchToggle.d.ts +8 -0
  199. package/ui-components/chat/DeepResearchToggle.js +13 -0
  200. package/ui-components/chat/index.d.ts +9 -0
  201. package/ui-components/chat/index.js +8 -0
  202. package/ui-components/chat/types.d.ts +23 -0
  203. package/ui-components/chat/types.js +51 -0
  204. package/ui-components/forms/PasswordRequirements.d.ts +10 -0
  205. package/ui-components/forms/PasswordRequirements.js +15 -0
  206. package/ui-components/forms/SettingsCard.d.ts +11 -0
  207. package/ui-components/forms/SettingsCard.js +5 -0
  208. package/ui-components/forms/SettingsFormField.d.ts +18 -0
  209. package/ui-components/forms/SettingsFormField.js +17 -0
  210. package/ui-components/forms/StatusAlert.d.ts +8 -0
  211. package/ui-components/forms/StatusAlert.js +7 -0
  212. package/ui-components/forms/index.d.ts +4 -0
  213. package/ui-components/forms/index.js +4 -0
  214. package/ui-components/index.d.ts +13 -0
  215. package/ui-components/index.js +18 -0
  216. package/ui-components/layout/CoreNavbar.d.ts +14 -0
  217. package/ui-components/layout/CoreNavbar.js +53 -0
  218. package/ui-components/layout/CoreSidebar.d.ts +28 -0
  219. package/ui-components/layout/CoreSidebar.js +62 -0
  220. package/ui-components/layout/LandingFooter.d.ts +28 -0
  221. package/ui-components/layout/LandingFooter.js +34 -0
  222. package/ui-components/layout/PageContainer.d.ts +8 -0
  223. package/ui-components/layout/PageContainer.js +17 -0
  224. package/ui-components/layout/PageHeader.d.ts +23 -0
  225. package/ui-components/layout/PageHeader.js +12 -0
  226. package/ui-components/layout/SettingsContainer.d.ts +10 -0
  227. package/ui-components/layout/SettingsContainer.js +9 -0
  228. package/ui-components/layout/ThemeToggle.d.ts +4 -0
  229. package/ui-components/layout/ThemeToggle.js +6 -0
  230. package/ui-components/layout/index.d.ts +7 -0
  231. package/ui-components/layout/index.js +7 -0
  232. package/ui-components/settings/GeneralInformationCard.d.ts +12 -0
  233. package/ui-components/settings/GeneralInformationCard.js +72 -0
  234. package/ui-components/settings/PasswordInformationCard.d.ts +10 -0
  235. package/ui-components/settings/PasswordInformationCard.js +167 -0
  236. package/ui-components/settings/SettingsPageHeader.d.ts +7 -0
  237. package/ui-components/settings/SettingsPageHeader.js +6 -0
  238. package/ui-components/settings/index.d.ts +3 -0
  239. package/ui-components/settings/index.js +3 -0
  240. package/ui-components/support/SupportModal.d.ts +15 -0
  241. package/ui-components/support/SupportModal.js +74 -0
  242. package/ui-components/support/index.d.ts +2 -0
  243. package/ui-components/support/index.js +1 -0
  244. package/ui-components/types/index.d.ts +39 -0
  245. package/ui-components/types/index.js +1 -0
  246. package/utils/index.d.ts +31 -0
  247. package/utils/index.js +38 -0
  248. package/utils/turnstile-config.d.ts +11 -0
  249. package/utils/turnstile-config.js +19 -0
@@ -0,0 +1,19 @@
1
+ import type { AppConfig, AppName } from './types';
2
+ export declare const CURRENT_APP: AppName;
3
+ /**
4
+ * Each app's fixed brand hex. Cross-app safe: unlike the Tailwind `primary`
5
+ * tokens (which only ever resolve to the *currently-running* app's palette),
6
+ * these literals correctly represent any app from inside any other app — used
7
+ * by the colored AnimatedLogo and the AppSwitcher so each product's mark shows
8
+ * in its own brand color.
9
+ */
10
+ export declare const BRAND_COLORS: Record<AppName, string>;
11
+ /**
12
+ * Per-app brand gradient (CSS) for the LogoBadge chip — a white mark on a
13
+ * brand-gradient rounded square. Cross-app safe literals (NOT tokens), so each
14
+ * product's chip renders its own gradient from inside any app (e.g. the
15
+ * AppSwitcher). Mirrors each app's landing hero gradient.
16
+ */
17
+ export declare const BRAND_GRADIENTS: Record<AppName, string>;
18
+ export declare const APP_CONFIGS: Record<string, AppConfig>;
19
+ export declare const getAppConfig: (appName: string) => AppConfig;
@@ -0,0 +1,56 @@
1
+ export const CURRENT_APP = process.env.NEXT_PUBLIC_APP_NAME || 'robosystems';
2
+ /**
3
+ * Each app's fixed brand hex. Cross-app safe: unlike the Tailwind `primary`
4
+ * tokens (which only ever resolve to the *currently-running* app's palette),
5
+ * these literals correctly represent any app from inside any other app — used
6
+ * by the colored AnimatedLogo and the AppSwitcher so each product's mark shows
7
+ * in its own brand color.
8
+ */
9
+ export const BRAND_COLORS = {
10
+ robosystems: '#3B7AF5', // blue
11
+ roboledger: '#8B5CF6', // violet
12
+ roboinvestor: '#10B981', // emerald
13
+ };
14
+ /**
15
+ * Per-app brand gradient (CSS) for the LogoBadge chip — a white mark on a
16
+ * brand-gradient rounded square. Cross-app safe literals (NOT tokens), so each
17
+ * product's chip renders its own gradient from inside any app (e.g. the
18
+ * AppSwitcher). Mirrors each app's landing hero gradient.
19
+ */
20
+ export const BRAND_GRADIENTS = {
21
+ robosystems: 'linear-gradient(135deg, #06B6D4, #3B7AF5 55%, #6366F1)', // cyan → blue → indigo
22
+ roboledger: 'linear-gradient(135deg, #8B5CF6, #A855F7 55%, #D946EF)', // violet → purple → fuchsia
23
+ roboinvestor: 'linear-gradient(135deg, #10B981, #14B8A6 55%, #06B6D4)', // emerald → teal → cyan
24
+ };
25
+ export const APP_CONFIGS = {
26
+ roboinvestor: {
27
+ name: 'roboinvestor',
28
+ displayName: 'RoboInvestor',
29
+ url: process.env.NEXT_PUBLIC_ROBOINVESTOR_APP_URL || 'https://roboinvestor.ai',
30
+ description: 'Portfolio Management Agent',
31
+ initials: 'RI',
32
+ colorClass: 'bg-emerald-600 dark:bg-emerald-500',
33
+ brandColor: BRAND_COLORS.roboinvestor,
34
+ },
35
+ roboledger: {
36
+ name: 'roboledger',
37
+ displayName: 'RoboLedger',
38
+ url: process.env.NEXT_PUBLIC_ROBOLEDGER_APP_URL || 'https://roboledger.ai',
39
+ description: 'Accounting & Reporting Agent',
40
+ initials: 'RL',
41
+ colorClass: 'bg-violet-600 dark:bg-violet-500',
42
+ brandColor: BRAND_COLORS.roboledger,
43
+ },
44
+ robosystems: {
45
+ name: 'robosystems',
46
+ displayName: 'RoboSystems',
47
+ url: process.env.NEXT_PUBLIC_ROBOSYSTEMS_APP_URL || 'https://robosystems.ai',
48
+ description: 'Financial Intelligence Platform',
49
+ initials: 'RS',
50
+ colorClass: 'bg-blue-600 dark:bg-blue-500',
51
+ brandColor: BRAND_COLORS.robosystems,
52
+ },
53
+ };
54
+ export const getAppConfig = (appName) => {
55
+ return APP_CONFIGS[appName] || APP_CONFIGS.robosystems;
56
+ };
@@ -0,0 +1,19 @@
1
+ import type { AuthUser } from './types';
2
+ /**
3
+ * Creates user-specific operations that can be used with auth context.
4
+ * This is a factory function that takes the auth client instance.
5
+ */
6
+ export declare function useUserHook(authClient: any, setUser: (user: AuthUser | null) => void): {
7
+ refreshUser: () => Promise<AuthUser | null>;
8
+ updateProfile: (data: {
9
+ name?: string;
10
+ email?: string;
11
+ }) => Promise<AuthUser>;
12
+ };
13
+ /**
14
+ * Hook to handle token expiry errors globally
15
+ * Automatically redirects to login on token expiry
16
+ */
17
+ export declare function useTokenExpiryHandler(logout: (reason?: string) => Promise<void>): {
18
+ handleTokenExpiry: (error: unknown) => Promise<boolean>;
19
+ };
@@ -0,0 +1,63 @@
1
+ 'use client';
2
+ import { useCallback, useEffect } from 'react';
3
+ import { TokenExpiredError } from './client';
4
+ /**
5
+ * Creates user-specific operations that can be used with auth context.
6
+ * This is a factory function that takes the auth client instance.
7
+ */
8
+ export function useUserHook(authClient, setUser) {
9
+ const refreshUser = useCallback(async () => {
10
+ try {
11
+ const user = await authClient.getCurrentUser();
12
+ setUser(user);
13
+ return user;
14
+ }
15
+ catch (error) {
16
+ setUser(null);
17
+ return null;
18
+ }
19
+ }, [authClient, setUser]);
20
+ const updateProfile = useCallback(async (data) => {
21
+ // This would use SDK updateUser method
22
+ // For now, refresh user data after update
23
+ const user = await refreshUser();
24
+ if (!user) {
25
+ throw new Error('User not found after update');
26
+ }
27
+ return user;
28
+ }, [refreshUser]);
29
+ return {
30
+ refreshUser,
31
+ updateProfile,
32
+ };
33
+ }
34
+ /**
35
+ * Hook to handle token expiry errors globally
36
+ * Automatically redirects to login on token expiry
37
+ */
38
+ export function useTokenExpiryHandler(logout) {
39
+ const handleTokenExpiry = useCallback(async (error) => {
40
+ if (error instanceof TokenExpiredError) {
41
+ // Token expired, redirect to login
42
+ await logout('session_expired');
43
+ return true; // Error was handled
44
+ }
45
+ return false; // Error was not handled
46
+ }, [logout]);
47
+ // Set up global error handler for unhandled promise rejections
48
+ useEffect(() => {
49
+ const handleUnhandledRejection = (event) => {
50
+ if (event.reason instanceof TokenExpiredError) {
51
+ event.preventDefault(); // Prevent default error logging
52
+ handleTokenExpiry(event.reason);
53
+ }
54
+ };
55
+ if (typeof window !== 'undefined') {
56
+ window.addEventListener('unhandledrejection', handleUnhandledRejection);
57
+ return () => {
58
+ window.removeEventListener('unhandledrejection', handleUnhandledRejection);
59
+ };
60
+ }
61
+ }, [handleTokenExpiry]);
62
+ return { handleTokenExpiry };
63
+ }
@@ -0,0 +1,6 @@
1
+ export * from './cleanup';
2
+ export * from './client';
3
+ export * from './config';
4
+ export * from './hooks';
5
+ export * from './sso';
6
+ export * from './types';
@@ -0,0 +1,6 @@
1
+ export * from './cleanup';
2
+ export * from './client';
3
+ export * from './config';
4
+ export * from './hooks';
5
+ export * from './sso';
6
+ export * from './types';
@@ -0,0 +1,42 @@
1
+ import type { AppConfig, AuthUser } from './types';
2
+ export declare class SSOManager {
3
+ private authClient;
4
+ constructor(apiUrl: string);
5
+ /**
6
+ * Check if user is already authenticated via SSO
7
+ */
8
+ checkSSOAuthentication(): Promise<AuthUser | null>;
9
+ /**
10
+ * Generate SSO token for cross-app authentication
11
+ */
12
+ generateSSOToken(): Promise<import("./types").SSOTokenResponse>;
13
+ /**
14
+ * Get SSO redirect URL using secure POST-based flow
15
+ */
16
+ getSSORedirectUrl(targetApp: string, returnUrl?: string): Promise<string>;
17
+ /**
18
+ * Handle SSO login using secure POST-based flow
19
+ */
20
+ handleSSOLogin(): Promise<AuthUser | null>;
21
+ /**
22
+ * Get available apps for the authenticated user
23
+ */
24
+ getAvailableApps(): AppConfig[];
25
+ /**
26
+ * Sanitize error objects for safe logging in development
27
+ */
28
+ private sanitizeErrorForLogging;
29
+ /**
30
+ * Navigate to another app with secure SSO
31
+ */
32
+ navigateToApp(targetApp: string, returnUrl?: string): Promise<void>;
33
+ }
34
+ /**
35
+ * Hook for SSO functionality
36
+ */
37
+ export declare function useSSO(apiUrl: string): {
38
+ checkSSOAuthentication: () => Promise<AuthUser>;
39
+ handleSSOLogin: () => Promise<AuthUser>;
40
+ navigateToApp: (targetApp: string, returnUrl?: string) => Promise<void>;
41
+ getAvailableApps: () => AppConfig[];
42
+ };
@@ -0,0 +1,281 @@
1
+ import { useMemo } from 'react';
2
+ import { RoboSystemsAuthClient } from './client';
3
+ import { APP_CONFIGS } from './config';
4
+ // Configuration constants
5
+ const AUTH_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
6
+ const NAVIGATION_DELAY_MS = 100; // 100ms delay for navigation
7
+ // Debug logging helper
8
+ const debugLog = (message, error) => {
9
+ if (process.env.NODE_ENV === 'development') {
10
+ console.debug(`[SSO] ${message}`, error);
11
+ }
12
+ };
13
+ export class SSOManager {
14
+ constructor(apiUrl) {
15
+ this.authClient = new RoboSystemsAuthClient(apiUrl);
16
+ }
17
+ /**
18
+ * Check if user is already authenticated via SSO
19
+ */
20
+ async checkSSOAuthentication() {
21
+ try {
22
+ return await this.authClient.checkAuthentication();
23
+ }
24
+ catch (_a) {
25
+ return null;
26
+ }
27
+ }
28
+ /**
29
+ * Generate SSO token for cross-app authentication
30
+ */
31
+ async generateSSOToken() {
32
+ return await this.authClient.generateSSOToken();
33
+ }
34
+ /**
35
+ * Get SSO redirect URL using secure POST-based flow
36
+ */
37
+ async getSSORedirectUrl(targetApp, returnUrl) {
38
+ const ssoData = await this.generateSSOToken();
39
+ const appConfig = APP_CONFIGS[targetApp];
40
+ if (!appConfig) {
41
+ throw new Error(`Unknown app: ${targetApp}`);
42
+ }
43
+ // Step 1: Exchange SSO token for secure session ID
44
+ const exchangeResult = await this.authClient.ssoExchange(ssoData.token, targetApp);
45
+ // Step 2: Create URL that will trigger the POST-based flow
46
+ const url = new URL('/login', appConfig.url);
47
+ // Pass session ID via URL parameters for cross-domain compatibility
48
+ url.searchParams.set('session_id', exchangeResult.session_id);
49
+ if (returnUrl) {
50
+ url.searchParams.set('returnUrl', returnUrl);
51
+ }
52
+ // Store only non-sensitive metadata in sessionStorage as backup for same-domain cases
53
+ // Note: We avoid storing the session_id in sessionStorage for security
54
+ try {
55
+ sessionStorage.setItem('sso_target_app', targetApp);
56
+ if (returnUrl) {
57
+ sessionStorage.setItem('sso_return_url', returnUrl);
58
+ }
59
+ }
60
+ catch (error) {
61
+ // Storage error - continue without backup storage
62
+ debugLog('sessionStorage write failed during SSO URL generation', error);
63
+ }
64
+ return url.toString();
65
+ }
66
+ /**
67
+ * Handle SSO login using secure POST-based flow
68
+ */
69
+ async handleSSOLogin() {
70
+ // Check for session ID from URL parameters (cross-domain compatible)
71
+ const urlParams = new URLSearchParams(window.location.search);
72
+ const sessionId = urlParams.get('session_id');
73
+ const returnUrl = urlParams.get('returnUrl');
74
+ // Check sessionStorage for return URL fallback (same-domain case)
75
+ // Note: We don't store session_id in sessionStorage for security
76
+ let sessionStorageReturn = null;
77
+ try {
78
+ sessionStorageReturn = sessionStorage.getItem('sso_return_url');
79
+ }
80
+ catch (error) {
81
+ // Storage access error - continue without fallback
82
+ debugLog('sessionStorage read failed during SSO login', error);
83
+ }
84
+ const finalSessionId = sessionId; // Only use URL parameter for session ID
85
+ const finalReturnUrl = returnUrl || sessionStorageReturn;
86
+ if (!finalSessionId) {
87
+ return null;
88
+ }
89
+ try {
90
+ // Complete SSO authentication using session ID
91
+ const authResponse = await this.authClient.ssoComplete(finalSessionId);
92
+ // Clean up both URL parameters and session storage
93
+ if (sessionId) {
94
+ // Clean up URL parameters
95
+ const newUrl = new URL(window.location.href);
96
+ newUrl.searchParams.delete('session_id');
97
+ newUrl.searchParams.delete('returnUrl');
98
+ window.history.replaceState({}, '', newUrl.toString());
99
+ }
100
+ // Clean up session storage
101
+ try {
102
+ sessionStorage.removeItem('sso_target_app');
103
+ sessionStorage.removeItem('sso_return_url');
104
+ }
105
+ catch (error) {
106
+ // Storage access error - continue silently
107
+ debugLog('sessionStorage cleanup failed during SSO success', error);
108
+ }
109
+ // Handle return URL if provided
110
+ if (finalReturnUrl && finalReturnUrl !== window.location.pathname) {
111
+ // Delay navigation to allow authentication to complete
112
+ setTimeout(() => {
113
+ window.location.href = finalReturnUrl;
114
+ }, NAVIGATION_DELAY_MS);
115
+ }
116
+ return authResponse.user;
117
+ }
118
+ catch (error) {
119
+ // Clean up both URL parameters and session storage on error
120
+ if (sessionId) {
121
+ const newUrl = new URL(window.location.href);
122
+ newUrl.searchParams.delete('session_id');
123
+ newUrl.searchParams.delete('returnUrl');
124
+ window.history.replaceState({}, '', newUrl.toString());
125
+ }
126
+ try {
127
+ sessionStorage.removeItem('sso_target_app');
128
+ sessionStorage.removeItem('sso_return_url');
129
+ }
130
+ catch (error) {
131
+ // Storage access error - continue silently
132
+ debugLog('sessionStorage cleanup failed during SSO error', error);
133
+ }
134
+ // Log errors for monitoring in production (sanitized)
135
+ if (process.env.NODE_ENV === 'development') {
136
+ console.error('SSO login failed:', this.sanitizeErrorForLogging(error));
137
+ }
138
+ else {
139
+ // Production error monitoring - log sanitized errors for observability
140
+ console.error('SSO authentication failed:', {
141
+ timestamp: new Date().toISOString(),
142
+ error: this.sanitizeErrorForLogging(error),
143
+ user_agent: (navigator === null || navigator === void 0 ? void 0 : navigator.userAgent) || 'unknown',
144
+ });
145
+ }
146
+ return null;
147
+ }
148
+ }
149
+ /**
150
+ * Get available apps for the authenticated user
151
+ */
152
+ getAvailableApps() {
153
+ return Object.values(APP_CONFIGS);
154
+ }
155
+ /**
156
+ * Sanitize error objects for safe logging in development
157
+ */
158
+ sanitizeErrorForLogging(error) {
159
+ var _a;
160
+ if (error instanceof Error) {
161
+ return {
162
+ name: error.name,
163
+ message: error.message,
164
+ // Exclude potentially sensitive stack traces in some cases
165
+ stack: ((_a = error.stack) === null || _a === void 0 ? void 0 : _a.split('\n').slice(0, 3).join('\n')) + '...',
166
+ };
167
+ }
168
+ if (typeof error === 'object' && error !== null) {
169
+ // Remove any fields that might contain sensitive data
170
+ const sanitized = Object.assign({}, error);
171
+ delete sanitized.token;
172
+ delete sanitized.session_id;
173
+ delete sanitized.password;
174
+ delete sanitized.key;
175
+ return sanitized;
176
+ }
177
+ return String(error);
178
+ }
179
+ /**
180
+ * Navigate to another app with secure SSO
181
+ */
182
+ async navigateToApp(targetApp, returnUrl) {
183
+ const appConfig = APP_CONFIGS[targetApp];
184
+ if (!appConfig) {
185
+ throw new Error(`Unknown app: ${targetApp}`);
186
+ }
187
+ // Check local auth cache first to avoid unnecessary SSO token generation
188
+ if (typeof window !== 'undefined') {
189
+ let cachedUser = null;
190
+ try {
191
+ cachedUser = sessionStorage.getItem('auth_user_cache');
192
+ }
193
+ catch (error) {
194
+ // Storage access error - proceed with SSO flow
195
+ debugLog('sessionStorage read failed during navigation cache check', error);
196
+ }
197
+ if (cachedUser) {
198
+ try {
199
+ const userData = JSON.parse(cachedUser);
200
+ const cacheAge = Date.now() - (userData.cached_at || 0);
201
+ // REMOVED: Cache bypass logic that was skipping SSO flow
202
+ // Always use proper SSO flow for cross-app authentication
203
+ debugLog('Proceeding with full SSO flow for secure cross-app auth');
204
+ }
205
+ catch (error) {
206
+ // Invalid cache, proceed with SSO flow
207
+ debugLog('Invalid cache found, proceeding with SSO flow', error);
208
+ try {
209
+ sessionStorage.removeItem('auth_user_cache');
210
+ }
211
+ catch (removeError) {
212
+ // Storage error - continue silently
213
+ }
214
+ }
215
+ }
216
+ }
217
+ try {
218
+ // Fallback to full SSO flow for stale/missing cache
219
+ const ssoData = await this.generateSSOToken();
220
+ // Step 2: Exchange token for secure session ID
221
+ const exchangeResult = await this.authClient.ssoExchange(ssoData.token, targetApp);
222
+ // Step 3: Navigate to target app with secure session handoff
223
+ const targetUrl = new URL('/login', appConfig.url);
224
+ // Pass session ID via URL parameters for cross-domain compatibility
225
+ targetUrl.searchParams.set('session_id', exchangeResult.session_id);
226
+ if (returnUrl) {
227
+ targetUrl.searchParams.set('returnUrl', returnUrl);
228
+ }
229
+ // Store only non-sensitive metadata in sessionStorage as backup for same-domain cases
230
+ // Note: We avoid storing the session_id in sessionStorage for security
231
+ if (typeof window !== 'undefined') {
232
+ try {
233
+ sessionStorage.setItem('sso_target_app', targetApp);
234
+ if (returnUrl) {
235
+ sessionStorage.setItem('sso_return_url', returnUrl);
236
+ }
237
+ }
238
+ catch (error) {
239
+ // Storage error - continue without backup storage
240
+ debugLog('sessionStorage write failed during SSO navigation', error);
241
+ }
242
+ }
243
+ // Navigate to target app
244
+ window.location.href = targetUrl.toString();
245
+ }
246
+ catch (error) {
247
+ if (process.env.NODE_ENV === 'development') {
248
+ console.error('Failed to navigate to app:', error);
249
+ }
250
+ debugLog('SSO navigation failed, attempting fallback', error);
251
+ // Fallback to direct navigation
252
+ try {
253
+ const targetUrl = new URL(appConfig.url);
254
+ if (returnUrl) {
255
+ targetUrl.searchParams.set('returnUrl', returnUrl);
256
+ }
257
+ window.location.href = targetUrl.toString();
258
+ }
259
+ catch (locationError) {
260
+ if (process.env.NODE_ENV === 'development') {
261
+ console.error('Fallback navigation failed:', locationError);
262
+ }
263
+ debugLog('All navigation methods failed', locationError);
264
+ // Final fallback using window.open
265
+ window.open(appConfig.url, '_self');
266
+ }
267
+ }
268
+ }
269
+ }
270
+ /**
271
+ * Hook for SSO functionality
272
+ */
273
+ export function useSSO(apiUrl) {
274
+ const ssoManager = useMemo(() => new SSOManager(apiUrl), [apiUrl]);
275
+ return useMemo(() => ({
276
+ checkSSOAuthentication: () => ssoManager.checkSSOAuthentication(),
277
+ handleSSOLogin: () => ssoManager.handleSSOLogin(),
278
+ navigateToApp: (targetApp, returnUrl) => ssoManager.navigateToApp(targetApp, returnUrl),
279
+ getAvailableApps: () => ssoManager.getAvailableApps(),
280
+ }), [ssoManager]);
281
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * JWT Token Storage Management
3
+ *
4
+ * Provides secure storage for JWT tokens using localStorage
5
+ * for cross-domain authentication support and persistent sessions.
6
+ */
7
+ export interface TokenData {
8
+ token: string;
9
+ expiresAt: number;
10
+ refreshThreshold?: number;
11
+ }
12
+ export type TokenStatus = 'valid' | 'warning' | 'expired';
13
+ /**
14
+ * Store JWT token in localStorage
15
+ * @param token - The JWT token from login response
16
+ * @param expiresIn - Optional expiry time in seconds (default 30 minutes)
17
+ * @param refreshThreshold - Optional refresh threshold in seconds (default 5 minutes)
18
+ */
19
+ export declare function storeToken(token: string, expiresIn?: number, refreshThreshold?: number): void;
20
+ /**
21
+ * Retrieve JWT token from localStorage
22
+ * @returns The stored token or null if not found/expired
23
+ */
24
+ export declare function getToken(): string | null;
25
+ /**
26
+ * Clear JWT token from storage
27
+ */
28
+ export declare function clearToken(): void;
29
+ /**
30
+ * Get current token status
31
+ * @returns 'valid' if token is fresh, 'warning' if approaching expiry, 'expired' if expired
32
+ */
33
+ export declare function getTokenStatus(): TokenStatus;
34
+ /**
35
+ * Get time until token expiry in milliseconds
36
+ * @returns Time until expiry in ms, or 0 if expired
37
+ */
38
+ export declare function getTimeUntilExpiry(): number;
39
+ /**
40
+ * Check if a valid token exists
41
+ * @returns True if a non-expired token exists
42
+ */
43
+ export declare function hasValidToken(): boolean;
44
+ /**
45
+ * Get the Authorization header value for API requests
46
+ * @returns Bearer token header or null
47
+ */
48
+ export declare function getAuthHeader(): string | null;
49
+ /**
50
+ * Extract token from login response and store it
51
+ * @param response - The auth response from login/refresh
52
+ */
53
+ export declare function handleAuthResponse(response: any): void;
54
+ /**
55
+ * Get valid token (backend handles refresh via grace period)
56
+ * @returns Valid token or null if expired
57
+ */
58
+ export declare function getValidToken(): Promise<string | null>;