@rebasepro/app 0.0.1-canary.4829d6e

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 (268) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/dist/auth/api.d.ts +66 -0
  4. package/dist/auth/index.d.ts +13 -0
  5. package/dist/auth/types.d.ts +127 -0
  6. package/dist/auth/useRebaseAuthController.d.ts +13 -0
  7. package/dist/components/AIIcon.d.ts +17 -0
  8. package/dist/components/AuthSimulationMenu.d.ts +0 -0
  9. package/dist/components/ConfirmationDialog.d.ts +9 -0
  10. package/dist/components/Debug/UIReferenceView.d.ts +13 -0
  11. package/dist/components/Debug/UIStyleGuide.d.ts +2 -0
  12. package/dist/components/Debug/collection-views/CardViewDemo.d.ts +2 -0
  13. package/dist/components/Debug/collection-views/CollectionTableDemo.d.ts +2 -0
  14. package/dist/components/Debug/collection-views/KanbanBoardDemo.d.ts +2 -0
  15. package/dist/components/Debug/collection-views/index.d.ts +3 -0
  16. package/dist/components/Debug/collection-views/sample_data.d.ts +23 -0
  17. package/dist/components/Debug/crm-dashboard/CalendarWidget.d.ts +22 -0
  18. package/dist/components/Debug/crm-dashboard/CrmDashboardDemo.d.ts +2 -0
  19. package/dist/components/Debug/crm-dashboard/DashboardMetrics.d.ts +15 -0
  20. package/dist/components/Debug/crm-dashboard/PipelineOverview.d.ts +34 -0
  21. package/dist/components/Debug/crm-dashboard/RecentActivity.d.ts +19 -0
  22. package/dist/components/Debug/crm-dashboard/TaskFilters.d.ts +30 -0
  23. package/dist/components/Debug/crm-dashboard/TaskTable.d.ts +50 -0
  24. package/dist/components/Debug/crm-dashboard/TasksView.d.ts +41 -0
  25. package/dist/components/ErrorTooltip.d.ts +3 -0
  26. package/dist/components/ErrorView.d.ts +21 -0
  27. package/dist/components/LanguageToggle.d.ts +2 -0
  28. package/dist/components/LoginView/LoginView.d.ts +113 -0
  29. package/dist/components/LoginView/index.d.ts +2 -0
  30. package/dist/components/NotFoundPage.d.ts +2 -0
  31. package/dist/components/RebaseAuth.d.ts +10 -0
  32. package/dist/components/RebaseLogo.d.ts +7 -0
  33. package/dist/components/SchemaDriftBanner.d.ts +27 -0
  34. package/dist/components/UnsavedChangesDialog.d.ts +9 -0
  35. package/dist/components/UserDisplay.d.ts +7 -0
  36. package/dist/components/UserSelectPopover.d.ts +63 -0
  37. package/dist/components/UserSettingsView.d.ts +2 -0
  38. package/dist/components/common/index.d.ts +6 -0
  39. package/dist/components/common/table_height.d.ts +5 -0
  40. package/dist/components/common/types.d.ts +66 -0
  41. package/dist/components/common/useColumnsIds.d.ts +9 -0
  42. package/dist/components/common/useDataTableController.d.ts +45 -0
  43. package/dist/components/common/useDebouncedData.d.ts +9 -0
  44. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  45. package/dist/components/index.d.ts +18 -0
  46. package/dist/contexts/AdminModeController.d.ts +4 -0
  47. package/dist/contexts/AnalyticsContext.d.ts +3 -0
  48. package/dist/contexts/AuthControllerContext.d.ts +3 -0
  49. package/dist/contexts/CollectionScopeContext.d.ts +50 -0
  50. package/dist/contexts/ComponentOverrideContext.d.ts +42 -0
  51. package/dist/contexts/CustomizationControllerContext.d.ts +3 -0
  52. package/dist/contexts/DataDriverContext.d.ts +3 -0
  53. package/dist/contexts/DataSourcesContext.d.ts +31 -0
  54. package/dist/contexts/DatabaseAdminContext.d.ts +3 -0
  55. package/dist/contexts/DialogsProvider.d.ts +4 -0
  56. package/dist/contexts/EffectiveRoleController.d.ts +4 -0
  57. package/dist/contexts/ModeController.d.ts +4 -0
  58. package/dist/contexts/RebaseClientInstanceContext.d.ts +6 -0
  59. package/dist/contexts/RebaseDataContext.d.ts +3 -0
  60. package/dist/contexts/SnackbarProvider.d.ts +2 -0
  61. package/dist/contexts/StorageSourceContext.d.ts +3 -0
  62. package/dist/contexts/StorageSourcesContext.d.ts +25 -0
  63. package/dist/contexts/UserConfigurationPersistenceContext.d.ts +3 -0
  64. package/dist/contexts/index.d.ts +17 -0
  65. package/dist/core/PluginLifecycleManager.d.ts +17 -0
  66. package/dist/core/PluginProviderStack.d.ts +21 -0
  67. package/dist/core/Rebase.d.ts +15 -0
  68. package/dist/core/RebaseProps.d.ts +291 -0
  69. package/dist/core/RebaseRouter.d.ts +4 -0
  70. package/dist/core/RebaseRoutes.d.ts +17 -0
  71. package/dist/core/index.d.ts +4 -0
  72. package/dist/hooks/ApiConfigContext.d.ts +24 -0
  73. package/dist/hooks/data/delete.d.ts +33 -0
  74. package/dist/hooks/data/save.d.ts +40 -0
  75. package/dist/hooks/data/useCollection.d.ts +62 -0
  76. package/dist/hooks/data/useData.d.ts +13 -0
  77. package/dist/hooks/data/useDataOrder.d.ts +12 -0
  78. package/dist/hooks/data/useFetch.d.ts +43 -0
  79. package/dist/hooks/data/useRelationSelector.d.ts +52 -0
  80. package/dist/hooks/index.d.ts +36 -0
  81. package/dist/hooks/useAdminModeController.d.ts +19 -0
  82. package/dist/hooks/useAnalyticsController.d.ts +5 -0
  83. package/dist/hooks/useAuthController.d.ts +11 -0
  84. package/dist/hooks/useAuthSubscription.d.ts +2 -0
  85. package/dist/hooks/useBackendStorageSource.d.ts +30 -0
  86. package/dist/hooks/useBridgeRegistration.d.ts +18 -0
  87. package/dist/hooks/useBrowserTitleAndIcon.d.ts +6 -0
  88. package/dist/hooks/useBuildAdminModeController.d.ts +6 -0
  89. package/dist/hooks/useBuildEffectiveRoleController.d.ts +8 -0
  90. package/dist/hooks/useBuildLocalConfigurationPersistence.d.ts +2 -0
  91. package/dist/hooks/useBuildModeController.d.ts +6 -0
  92. package/dist/hooks/useClipboard.d.ts +57 -0
  93. package/dist/hooks/useCollapsedGroups.d.ts +27 -0
  94. package/dist/hooks/useComponentOverride.d.ts +32 -0
  95. package/dist/hooks/useCustomizationController.d.ts +11 -0
  96. package/dist/hooks/useDialogsController.d.ts +11 -0
  97. package/dist/hooks/useEffectiveRoleController.d.ts +7 -0
  98. package/dist/hooks/useLargeLayout.d.ts +1 -0
  99. package/dist/hooks/useModeController.d.ts +19 -0
  100. package/dist/hooks/usePermissions.d.ts +12 -0
  101. package/dist/hooks/useRebaseClient.d.ts +5 -0
  102. package/dist/hooks/useRebaseContext.d.ts +11 -0
  103. package/dist/hooks/useRebaseRegistry.d.ts +34 -0
  104. package/dist/hooks/useResolvedComponent.d.ts +47 -0
  105. package/dist/hooks/useSlot.d.ts +18 -0
  106. package/dist/hooks/useSnackbarController.d.ts +20 -0
  107. package/dist/hooks/useStorageSource.d.ts +6 -0
  108. package/dist/hooks/useStudioBridge.d.ts +79 -0
  109. package/dist/hooks/useTranslation.d.ts +17 -0
  110. package/dist/hooks/useUnsavedChangesDialog.d.ts +12 -0
  111. package/dist/hooks/useUserConfigurationPersistence.d.ts +8 -0
  112. package/dist/i18n/RebaseI18nProvider.d.ts +33 -0
  113. package/dist/index.d.ts +16 -0
  114. package/dist/index.es.js +16843 -0
  115. package/dist/index.es.js.map +1 -0
  116. package/dist/index.umd.js +17021 -0
  117. package/dist/index.umd.js.map +1 -0
  118. package/dist/internal/common.d.ts +14 -0
  119. package/dist/internal/useRestoreScroll.d.ts +15 -0
  120. package/dist/locales/de.d.ts +2 -0
  121. package/dist/locales/en.d.ts +10 -0
  122. package/dist/locales/es.d.ts +10 -0
  123. package/dist/locales/fr.d.ts +2 -0
  124. package/dist/locales/hi.d.ts +2 -0
  125. package/dist/locales/it.d.ts +2 -0
  126. package/dist/locales/pt.d.ts +7 -0
  127. package/dist/util/constants.d.ts +1 -0
  128. package/dist/util/createFormexStub.d.ts +2 -0
  129. package/dist/util/entity_cache.d.ts +22 -0
  130. package/dist/util/enums.d.ts +5 -0
  131. package/dist/util/icon_list.d.ts +5 -0
  132. package/dist/util/icons.d.ts +20 -0
  133. package/dist/util/index.d.ts +8 -0
  134. package/dist/util/previews.d.ts +4 -0
  135. package/dist/util/useStorageUploadController.d.ts +44 -0
  136. package/dist/vitePlugin.d.ts +60 -0
  137. package/dist/vitePlugin.js +113 -0
  138. package/package.json +145 -0
  139. package/src/auth/api.ts +148 -0
  140. package/src/auth/index.ts +25 -0
  141. package/src/auth/types.ts +115 -0
  142. package/src/auth/useRebaseAuthController.ts +328 -0
  143. package/src/components/AIIcon.tsx +47 -0
  144. package/src/components/AuthSimulationMenu.tsx +0 -0
  145. package/src/components/ConfirmationDialog.tsx +48 -0
  146. package/src/components/Debug/UIReferenceView.tsx +958 -0
  147. package/src/components/Debug/UIStyleGuide.tsx +156 -0
  148. package/src/components/Debug/collection-views/CardViewDemo.tsx +85 -0
  149. package/src/components/Debug/collection-views/CollectionTableDemo.tsx +107 -0
  150. package/src/components/Debug/collection-views/KanbanBoardDemo.tsx +90 -0
  151. package/src/components/Debug/collection-views/index.ts +3 -0
  152. package/src/components/Debug/collection-views/sample_data.ts +42 -0
  153. package/src/components/Debug/crm-dashboard/CalendarWidget.tsx +389 -0
  154. package/src/components/Debug/crm-dashboard/CrmDashboardDemo.tsx +1102 -0
  155. package/src/components/Debug/crm-dashboard/DashboardMetrics.tsx +133 -0
  156. package/src/components/Debug/crm-dashboard/PipelineOverview.tsx +290 -0
  157. package/src/components/Debug/crm-dashboard/RecentActivity.tsx +215 -0
  158. package/src/components/Debug/crm-dashboard/TaskFilters.tsx +184 -0
  159. package/src/components/Debug/crm-dashboard/TaskTable.tsx +427 -0
  160. package/src/components/Debug/crm-dashboard/TasksView.tsx +234 -0
  161. package/src/components/ErrorTooltip.tsx +12 -0
  162. package/src/components/ErrorView.tsx +94 -0
  163. package/src/components/LanguageToggle.tsx +59 -0
  164. package/src/components/LoginView/LoginView.tsx +932 -0
  165. package/src/components/LoginView/index.ts +2 -0
  166. package/src/components/NotFoundPage.tsx +27 -0
  167. package/src/components/RebaseAuth.tsx +27 -0
  168. package/src/components/RebaseLogo.tsx +29 -0
  169. package/src/components/SchemaDriftBanner.tsx +102 -0
  170. package/src/components/UnsavedChangesDialog.tsx +46 -0
  171. package/src/components/UserDisplay.tsx +54 -0
  172. package/src/components/UserSelectPopover.tsx +399 -0
  173. package/src/components/UserSettingsView.tsx +318 -0
  174. package/src/components/common/index.ts +6 -0
  175. package/src/components/common/table_height.tsx +21 -0
  176. package/src/components/common/types.tsx +66 -0
  177. package/src/components/common/useColumnsIds.tsx +222 -0
  178. package/src/components/common/useDataTableController.tsx +514 -0
  179. package/src/components/common/useDebouncedData.ts +49 -0
  180. package/src/components/common/useScrollRestoration.tsx +68 -0
  181. package/src/components/index.tsx +26 -0
  182. package/src/contexts/AdminModeController.tsx +11 -0
  183. package/src/contexts/AnalyticsContext.tsx +4 -0
  184. package/src/contexts/AuthControllerContext.tsx +4 -0
  185. package/src/contexts/CollectionScopeContext.tsx +67 -0
  186. package/src/contexts/ComponentOverrideContext.tsx +81 -0
  187. package/src/contexts/CustomizationControllerContext.tsx +4 -0
  188. package/src/contexts/DataDriverContext.tsx +4 -0
  189. package/src/contexts/DataSourcesContext.tsx +36 -0
  190. package/src/contexts/DatabaseAdminContext.tsx +4 -0
  191. package/src/contexts/DialogsProvider.tsx +56 -0
  192. package/src/contexts/EffectiveRoleController.tsx +12 -0
  193. package/src/contexts/ModeController.tsx +11 -0
  194. package/src/contexts/RebaseClientInstanceContext.tsx +7 -0
  195. package/src/contexts/RebaseDataContext.tsx +4 -0
  196. package/src/contexts/SnackbarProvider.tsx +15 -0
  197. package/src/contexts/StorageSourceContext.tsx +4 -0
  198. package/src/contexts/StorageSourcesContext.tsx +30 -0
  199. package/src/contexts/UserConfigurationPersistenceContext.tsx +4 -0
  200. package/src/contexts/index.ts +17 -0
  201. package/src/core/PluginLifecycleManager.tsx +95 -0
  202. package/src/core/PluginProviderStack.tsx +40 -0
  203. package/src/core/Rebase.tsx +439 -0
  204. package/src/core/RebaseProps.tsx +320 -0
  205. package/src/core/RebaseRouter.tsx +17 -0
  206. package/src/core/RebaseRoutes.tsx +24 -0
  207. package/src/core/index.tsx +5 -0
  208. package/src/hooks/ApiConfigContext.tsx +41 -0
  209. package/src/hooks/data/delete.ts +55 -0
  210. package/src/hooks/data/save.ts +77 -0
  211. package/src/hooks/data/useCollection.tsx +176 -0
  212. package/src/hooks/data/useData.tsx +18 -0
  213. package/src/hooks/data/useDataOrder.ts +26 -0
  214. package/src/hooks/data/useFetch.tsx +140 -0
  215. package/src/hooks/data/useRelationSelector.tsx +234 -0
  216. package/src/hooks/index.tsx +45 -0
  217. package/src/hooks/useAdminModeController.tsx +23 -0
  218. package/src/hooks/useAnalyticsController.tsx +8 -0
  219. package/src/hooks/useAuthController.tsx +14 -0
  220. package/src/hooks/useAuthSubscription.ts +87 -0
  221. package/src/hooks/useBackendStorageSource.ts +288 -0
  222. package/src/hooks/useBridgeRegistration.tsx +32 -0
  223. package/src/hooks/useBrowserTitleAndIcon.tsx +23 -0
  224. package/src/hooks/useBuildAdminModeController.tsx +24 -0
  225. package/src/hooks/useBuildEffectiveRoleController.tsx +31 -0
  226. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +75 -0
  227. package/src/hooks/useBuildModeController.tsx +71 -0
  228. package/src/hooks/useClipboard.tsx +158 -0
  229. package/src/hooks/useCollapsedGroups.ts +116 -0
  230. package/src/hooks/useComponentOverride.tsx +59 -0
  231. package/src/hooks/useCustomizationController.tsx +14 -0
  232. package/src/hooks/useDialogsController.tsx +15 -0
  233. package/src/hooks/useEffectiveRoleController.tsx +10 -0
  234. package/src/hooks/useLargeLayout.tsx +65 -0
  235. package/src/hooks/useModeController.tsx +23 -0
  236. package/src/hooks/usePermissions.ts +44 -0
  237. package/src/hooks/useRebaseClient.tsx +10 -0
  238. package/src/hooks/useRebaseContext.tsx +83 -0
  239. package/src/hooks/useRebaseRegistry.tsx +91 -0
  240. package/src/hooks/useResolvedComponent.tsx +159 -0
  241. package/src/hooks/useSlot.tsx +64 -0
  242. package/src/hooks/useSnackbarController.tsx +52 -0
  243. package/src/hooks/useStorageSource.tsx +11 -0
  244. package/src/hooks/useStudioBridge.tsx +209 -0
  245. package/src/hooks/useTranslation.ts +33 -0
  246. package/src/hooks/useUnsavedChangesDialog.tsx +64 -0
  247. package/src/hooks/useUserConfigurationPersistence.tsx +11 -0
  248. package/src/i18n/RebaseI18nProvider.tsx +160 -0
  249. package/src/index.ts +25 -0
  250. package/src/internal/common.tsx +16 -0
  251. package/src/internal/useRestoreScroll.tsx +69 -0
  252. package/src/locales/de.ts +902 -0
  253. package/src/locales/en.ts +980 -0
  254. package/src/locales/es.ts +922 -0
  255. package/src/locales/fr.ts +901 -0
  256. package/src/locales/hi.ts +901 -0
  257. package/src/locales/it.ts +901 -0
  258. package/src/locales/pt.ts +909 -0
  259. package/src/util/constants.ts +7 -0
  260. package/src/util/createFormexStub.tsx +66 -0
  261. package/src/util/entity_cache.ts +239 -0
  262. package/src/util/enums.ts +32 -0
  263. package/src/util/icon_list.ts +22 -0
  264. package/src/util/icons.tsx +149 -0
  265. package/src/util/index.ts +12 -0
  266. package/src/util/previews.ts +105 -0
  267. package/src/util/useStorageUploadController.tsx +350 -0
  268. package/src/vitePlugin.ts +196 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Auth-config fetch helper for the Rebase React frontend.
3
+ *
4
+ * The former per-endpoint auth client (login, register, refresh, OAuth,
5
+ * sessions, …) has been removed: `useRebaseAuthController` now delegates to
6
+ * the headless SDK's `client.auth` (from `@rebasepro/client`), which is the
7
+ * single source of truth for the auth session. The only piece that remains
8
+ * here is the unauthenticated `/api/auth/config` probe used to detect
9
+ * bootstrap mode and enabled providers before a session exists.
10
+ */
11
+
12
+ import { RebaseApiError } from "@rebasepro/types";
13
+
14
+ /**
15
+ * @deprecated Use {@link RebaseApiError} (from `@rebasepro/types`, re-exported by
16
+ * `@rebasepro/client`). Kept as an alias so `instanceof` / imports keep working;
17
+ * errors thrown here now carry `.code` and `.status` on the unified type.
18
+ */
19
+ export const AuthApiError = RebaseApiError;
20
+
21
+ async function handleResponse<T>(response: Response): Promise<T> {
22
+ let data: Record<string, unknown>;
23
+ try {
24
+ data = await response.json();
25
+ } catch (parseError) {
26
+ // Response wasn't JSON - could be network error or server issue
27
+ throw new RebaseApiError(
28
+ `Server returned non-JSON response (status: ${response.status})`,
29
+ { status: response.status, code: "PARSE_ERROR" }
30
+ );
31
+ }
32
+
33
+ if (!response.ok) {
34
+ throw new RebaseApiError(
35
+ (data as Record<string, Record<string, string>>).error?.message || "Request failed",
36
+ {
37
+ status: response.status,
38
+ code: (data as Record<string, Record<string, string>>).error?.code || "UNKNOWN_ERROR"
39
+ }
40
+ );
41
+ }
42
+
43
+ return data as T;
44
+ }
45
+
46
+ /**
47
+ * Wrapper for fetch that catches generic network failures (like server down)
48
+ * and translates them to a {@link RebaseApiError} with code `NETWORK_ERROR`.
49
+ */
50
+ async function fetchWithHandling(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
51
+ try {
52
+ return await fetch(input, init);
53
+ } catch (error: unknown) {
54
+ if (error instanceof TypeError && error.message.includes("Failed to fetch")) {
55
+ throw new RebaseApiError(
56
+ "Failed to connect to the backend server. The backend might be down or failed to initialize (e.g., database connection timeout).",
57
+ { code: "NETWORK_ERROR", cause: error }
58
+ );
59
+ }
60
+ throw new RebaseApiError(
61
+ "Network error: " + (error instanceof Error ? error.message : String(error)),
62
+ { code: "NETWORK_ERROR", cause: error }
63
+ );
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Auth config response from the backend
69
+ */
70
+ export interface AuthConfigResponse {
71
+ /** True when there are no users in the system and first user setup is needed */
72
+ needsSetup: boolean;
73
+ /** Whether new user registration is enabled */
74
+ registrationEnabled: boolean;
75
+ /** Whether email service is configured */
76
+ emailServiceEnabled?: boolean;
77
+ /** Whether the self-service password reset flow (email link) is supported */
78
+ passwordReset?: boolean;
79
+ /**
80
+ * Whether an admin can reset another user's password, i.e. the adapter
81
+ * mounts `POST /admin/users/:userId/reset-password`. Absent on backends
82
+ * predating this field, which the frontend treats as supported.
83
+ */
84
+ adminPasswordReset?: boolean;
85
+ /** Whether email verification is supported */
86
+ emailVerification?: boolean;
87
+ /** Complete list of enabled OAuth provider IDs (e.g. ["google", "github", "discord"]) */
88
+ enabledProviders: string[];
89
+ }
90
+
91
+ /**
92
+ * Cache container for `fetchAuthConfig` — holds both the inflight promise
93
+ * (to deduplicate concurrent calls) and the cached result.
94
+ */
95
+ export interface AuthConfigCache {
96
+ cached: AuthConfigResponse | null;
97
+ inflight: Promise<AuthConfigResponse> | null;
98
+ }
99
+
100
+ /**
101
+ * Create a fresh `AuthConfigCache` instance.
102
+ * Callers own the cache object and pass it into `fetchAuthConfig` / `clearAuthConfigCache`.
103
+ */
104
+ export function createAuthConfigCache(): AuthConfigCache {
105
+ return { cached: null, inflight: null };
106
+ }
107
+
108
+ /**
109
+ * Fetch auth configuration / status from the backend
110
+ * This is an unauthenticated endpoint used to detect bootstrap mode.
111
+ *
112
+ * Results are cached for the session lifetime.
113
+ * Concurrent calls are deduplicated: only one network request is made
114
+ * and all callers share the same promise.
115
+ */
116
+ export async function fetchAuthConfig(apiUrl: string, cache: AuthConfigCache): Promise<AuthConfigResponse> {
117
+ if (cache.cached) {
118
+ return cache.cached;
119
+ }
120
+
121
+ if (cache.inflight) {
122
+ return cache.inflight;
123
+ }
124
+
125
+ cache.inflight = (async () => {
126
+ const response = await fetchWithHandling(`${apiUrl}/api/auth/config`, {
127
+ method: "GET",
128
+ headers: { "Content-Type": "application/json" }
129
+ });
130
+ return handleResponse<AuthConfigResponse>(response);
131
+ })();
132
+
133
+ try {
134
+ const result = await cache.inflight;
135
+ cache.cached = result;
136
+ return result;
137
+ } finally {
138
+ cache.inflight = null;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Clear the cached auth config (e.g. on logout or for testing).
144
+ */
145
+ export function clearAuthConfigCache(cache: AuthConfigCache): void {
146
+ cache.cached = null;
147
+ cache.inflight = null;
148
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @rebasepro/app
3
+ *
4
+ * React authentication controller for Rebase frontends.
5
+ * Provides the `useRebaseAuthController` hook and API utilities
6
+ * for communicating with a Rebase backend's JWT auth endpoints.
7
+ *
8
+ * For the generic LoginView and RebaseAuth components, see @rebasepro/app.
9
+ */
10
+
11
+ // Types
12
+ export type {
13
+ RebaseAuthController,
14
+ RebaseAuthControllerProps,
15
+ AuthTokens,
16
+ DeviceSession,
17
+ UserInfo,
18
+ AuthResponse,
19
+ RefreshResponse
20
+ } from "./types";
21
+
22
+ export { useRebaseAuthController } from "./useRebaseAuthController";
23
+ // API utilities
24
+ export { fetchAuthConfig, clearAuthConfigCache, createAuthConfigCache, AuthApiError } from "./api";
25
+ export type { AuthConfigResponse, AuthConfigCache } from "./api";
@@ -0,0 +1,115 @@
1
+ import { AuthController, User, AuthTokens, DeviceSession, RebaseSession, AuthChangeEvent } from "@rebasepro/types";
2
+ import type { AuthConfigResponse } from "./api";
3
+
4
+ /** @deprecated Use `User` from `@rebasepro/types` instead. */
5
+ export type UserInfo = User;
6
+ /** @deprecated Use `DeviceSession` from `@rebasepro/types` instead. */
7
+ export type Session = DeviceSession;
8
+ // Re-export canonical types for backward compatibility
9
+ export type { AuthTokens, DeviceSession };
10
+
11
+ /**
12
+ * Auth controller that extends the base AuthController
13
+ * with additional methods for email/password and Google login
14
+ */
15
+ export type RebaseAuthController = AuthController & {
16
+ /** Login with Google — accepts an ID token, access token, or authorization code payload */
17
+ googleLogin: (payload: { idToken: string } | { accessToken: string } | { code: string; redirectUri: string }) => Promise<void>;
18
+ /** Generic OAuth login — works with any provider. Posts payload to /auth/{providerId}. */
19
+ oauthLogin: (providerId: string, payload: Record<string, unknown>) => Promise<void>;
20
+ /** Login with email and password */
21
+ emailPasswordLogin: (email: string, password: string) => Promise<void>;
22
+ /** Register a new user */
23
+ register: (email: string, password: string, displayName?: string) => Promise<void>;
24
+ /** Skip login (for anonymous access if enabled) */
25
+ skipLogin: () => void;
26
+ /** Whether login was skipped */
27
+ loginSkipped: boolean;
28
+ /** Error from auth provider (login failure details) */
29
+ authProviderError: Error | null;
30
+ /** True when there are no users in the system — first-user bootstrap mode */
31
+ needsSetup: boolean;
32
+ /** Whether new user registration is enabled (always true during setup) */
33
+ registrationEnabled: boolean;
34
+ /** Request password reset email */
35
+ forgotPassword: (email: string) => Promise<void>;
36
+ /** Reset password using token from email */
37
+ resetPassword: (token: string, password: string) => Promise<void>;
38
+ /** Change password for authenticated user */
39
+ changePassword: (oldPassword: string, newPassword: string) => Promise<void>;
40
+ /** Update user profile */
41
+ updateProfile: (displayName?: string, photoURL?: string) => Promise<User>;
42
+ /** Fetch active sessions */
43
+ fetchSessions: () => Promise<DeviceSession[]>;
44
+ /** Revoke a session */
45
+ revokeSession: (sessionId: string) => Promise<void>;
46
+ /** Revoke all active sessions */
47
+ revokeAllSessions: () => Promise<void>;
48
+ /** Get internal API URL */
49
+ getApiUrl?: () => string | undefined;
50
+ /** Clear the current auth provider error */
51
+ clearError: () => void;
52
+ /** Set or clear the auth provider error */
53
+ setAuthProviderError: (error: Error | null) => void;
54
+ }
55
+
56
+ /**
57
+ * Structural type for the subset of `client.auth` (from `@rebasepro/client`)
58
+ * that the React hook delegates to. Avoids a hard dependency on
59
+ * `@rebasepro/client` while remaining fully type-safe.
60
+ */
61
+ export interface ClientAuth {
62
+ getSession(): RebaseSession | null;
63
+ onAuthStateChange(callback: (event: AuthChangeEvent, session: RebaseSession | null) => void): () => void;
64
+ isInitialized(): Promise<void>;
65
+ getAuthConfig(): Promise<AuthConfigResponse>;
66
+ signInWithEmail(email: string, password: string): Promise<unknown>;
67
+ signOut(): Promise<void>;
68
+ refreshSession(): Promise<RebaseSession>;
69
+ signUp(email: string, password: string, displayName?: string): Promise<unknown>;
70
+ signInWithGoogle(payload: { idToken: string } | { accessToken: string } | { code: string; redirectUri: string }): Promise<unknown>;
71
+ signInWithOAuth(providerId: string, payload: Record<string, unknown>): Promise<unknown>;
72
+ resetPasswordForEmail(email: string): Promise<unknown>;
73
+ resetPassword(token: string, password: string): Promise<unknown>;
74
+ changePassword(oldPassword: string, newPassword: string): Promise<unknown>;
75
+ updateUser(updates: { displayName?: string; photoURL?: string }): Promise<User>;
76
+ getSessions(): Promise<DeviceSession[]>;
77
+ revokeSession(sessionId: string): Promise<unknown>;
78
+ revokeAllSessions(): Promise<unknown>;
79
+ }
80
+
81
+ /**
82
+ * Props for useRebaseAuthController hook
83
+ */
84
+ export interface RebaseAuthControllerProps {
85
+ /** The Rebase Client instance */
86
+ client?: {
87
+ baseUrl?: string;
88
+ resolveToken?: () => Promise<string | null>;
89
+ setAuthTokenGetter?: (getter: () => Promise<string | null>) => void;
90
+ setOnUnauthorized?: (handler: () => Promise<boolean>) => void;
91
+ ws?: { setAuthTokenGetter: (getter: () => Promise<string | null>) => void };
92
+ auth?: ClientAuth;
93
+ };
94
+ /** Google OAuth client ID (optional, enables Google login) */
95
+ googleClientId?: string;
96
+ /** Callback when user signs out */
97
+ onSignOut?: () => void;
98
+ /** Define roles for a user after login */
99
+ defineRolesFor?: (user: User) => Promise<string[] | undefined> | string[] | undefined;
100
+ }
101
+
102
+ /**
103
+ * Auth response from backend login/register endpoints
104
+ */
105
+ export interface AuthResponse {
106
+ user: User;
107
+ tokens: AuthTokens;
108
+ }
109
+
110
+ /**
111
+ * Response from token refresh endpoint
112
+ */
113
+ export interface RefreshResponse {
114
+ tokens: AuthTokens;
115
+ }
@@ -0,0 +1,328 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import type { User, AuthChangeEvent, RebaseSession } from "@rebasepro/types";
3
+ import type { AuthConfigResponse } from "./api";
4
+ import { RebaseAuthController, RebaseAuthControllerProps } from "./types";
5
+
6
+ /**
7
+ * Auth controller hook for JWT-based authentication.
8
+ *
9
+ * This is a thin React binding over the headless SDK `client.auth`.
10
+ * It synchronizes React state with the SDK's internal state and delegates
11
+ * all operations to the SDK to ensure a single source of truth for
12
+ * authentication sessions across the entire application.
13
+ *
14
+ * @param props Configuration options
15
+ * @returns RebaseAuthController instance
16
+ */
17
+ export function useRebaseAuthController(
18
+ props: RebaseAuthControllerProps = {}
19
+ ): RebaseAuthController {
20
+ const { client, onSignOut, defineRolesFor } = props;
21
+ const auth = client?.auth;
22
+
23
+ const [user, setUser] = useState<User | null>(null);
24
+ const [authLoading, setAuthLoading] = useState(false);
25
+ const [initialLoading, setInitialLoading] = useState(true);
26
+ const [authError, setAuthError] = useState<Error | null>(null);
27
+ const [authProviderError, setAuthProviderError] = useState<Error | null>(null);
28
+ const [loginSkipped, setLoginSkipped] = useState(false);
29
+ const [extra, setExtra] = useState<unknown>(null);
30
+ const [authConfig, setAuthConfig] = useState<AuthConfigResponse | null>(null);
31
+
32
+ const isMountedRef = useRef(true);
33
+ // Keep a stable ref to onSignOut so the listener never goes stale.
34
+ const onSignOutRef = useRef(onSignOut);
35
+ onSignOutRef.current = onSignOut;
36
+ // Keep a stable ref to defineRolesFor too, so an inline (non-memoized)
37
+ // function from the consumer does not re-run the sync effect on every
38
+ // render — which would re-subscribe to auth events and re-fetch the
39
+ // (uncached) auth config each time.
40
+ const defineRolesForRef = useRef(defineRolesFor);
41
+ defineRolesForRef.current = defineRolesFor;
42
+
43
+ const clearError = useCallback(() => {
44
+ setAuthProviderError(null);
45
+ }, []);
46
+
47
+ // Sync state with client.auth
48
+ useEffect(() => {
49
+ if (!auth) {
50
+ setInitialLoading(false);
51
+ return;
52
+ }
53
+
54
+ isMountedRef.current = true;
55
+
56
+ const updateState = async (session: RebaseSession | null) => {
57
+ if (!isMountedRef.current) return;
58
+
59
+ let userToSet = session?.user ?? null;
60
+ const defineRolesFor = defineRolesForRef.current;
61
+ if (userToSet && defineRolesFor) {
62
+ try {
63
+ const roles = await defineRolesFor(userToSet);
64
+ if (roles && isMountedRef.current) {
65
+ userToSet = { ...userToSet, roles };
66
+ }
67
+ } catch (e) {
68
+ console.error("Error defining roles:", e);
69
+ }
70
+ }
71
+
72
+ if (isMountedRef.current) {
73
+ setUser(userToSet);
74
+ }
75
+ };
76
+
77
+ const syncState = async (event: AuthChangeEvent, session: RebaseSession | null) => {
78
+ await updateState(session);
79
+ if (event === "SIGNED_OUT") {
80
+ if (isMountedRef.current) {
81
+ setLoginSkipped(false);
82
+ }
83
+ onSignOutRef.current?.();
84
+ }
85
+ };
86
+
87
+ // Initial sync with whatever is currently in the SDK
88
+ updateState(auth.getSession());
89
+
90
+ // Wait for SDK initialization (restoring session from storage or silent refresh)
91
+ auth.isInitialized().then(() => {
92
+ if (isMountedRef.current) {
93
+ updateState(auth.getSession());
94
+ setInitialLoading(false);
95
+ }
96
+ }).catch(() => {
97
+ if (isMountedRef.current) setInitialLoading(false);
98
+ });
99
+
100
+ // Fetch backend auth configuration
101
+ auth.getAuthConfig().then((config: AuthConfigResponse) => {
102
+ if (isMountedRef.current) setAuthConfig(config);
103
+ }).catch(() => {});
104
+
105
+ const unsubscribe = auth.onAuthStateChange(syncState);
106
+
107
+ return () => {
108
+ isMountedRef.current = false;
109
+ unsubscribe();
110
+ };
111
+ }, [auth]);
112
+
113
+ const getAuthToken = useCallback(async (): Promise<string> => {
114
+ if (!auth) throw new Error("Rebase client with auth is required");
115
+ const session = auth.getSession();
116
+
117
+ // If we have a valid token, return it
118
+ if (session?.accessToken && session.expiresAt > Date.now() + 10000) {
119
+ return session.accessToken;
120
+ }
121
+
122
+ // Otherwise, refresh
123
+ const newSession = await auth.refreshSession();
124
+ return newSession.accessToken;
125
+ }, [auth]);
126
+
127
+ const signOut = useCallback(async () => {
128
+ if (!auth) return;
129
+ setAuthLoading(true);
130
+ try {
131
+ await auth.signOut();
132
+ // signOut emits SIGNED_OUT → syncState calls onSignOutRef.current
133
+ } catch (error: unknown) {
134
+ const err = error instanceof Error ? error : new Error(String(error));
135
+ setAuthProviderError(err);
136
+ } finally {
137
+ setAuthLoading(false);
138
+ }
139
+ }, [auth]);
140
+
141
+ const emailPasswordLogin = useCallback(async (email: string, password: string) => {
142
+ if (!auth) throw new Error("Rebase client with auth is required");
143
+ setAuthLoading(true);
144
+ setAuthProviderError(null);
145
+ try {
146
+ await auth.signInWithEmail(email, password);
147
+ } catch (error: unknown) {
148
+ const err = error instanceof Error ? error : new Error(String(error));
149
+ setAuthProviderError(err);
150
+ throw error;
151
+ } finally {
152
+ setAuthLoading(false);
153
+ }
154
+ }, [auth]);
155
+
156
+ const register = useCallback(async (email: string, password: string, displayName?: string) => {
157
+ if (!auth) throw new Error("Rebase client with auth is required");
158
+ setAuthLoading(true);
159
+ setAuthProviderError(null);
160
+ try {
161
+ await auth.signUp(email, password, displayName);
162
+ } catch (error: unknown) {
163
+ const err = error instanceof Error ? error : new Error(String(error));
164
+ setAuthProviderError(err);
165
+ throw error;
166
+ } finally {
167
+ setAuthLoading(false);
168
+ }
169
+ }, [auth]);
170
+
171
+ const googleLogin = useCallback(async (
172
+ payload: { idToken: string } | { accessToken: string } | { code: string; redirectUri: string }
173
+ ) => {
174
+ if (!auth) throw new Error("Rebase client with auth is required");
175
+ setAuthLoading(true);
176
+ setAuthProviderError(null);
177
+ try {
178
+ await auth.signInWithGoogle(payload);
179
+ } catch (error: unknown) {
180
+ const err = error instanceof Error ? error : new Error(String(error));
181
+ setAuthProviderError(err);
182
+ throw error;
183
+ } finally {
184
+ setAuthLoading(false);
185
+ }
186
+ }, [auth]);
187
+
188
+ const oauthLogin = useCallback(async (providerId: string, payload: Record<string, unknown>) => {
189
+ if (!auth) throw new Error("Rebase client with auth is required");
190
+ setAuthLoading(true);
191
+ setAuthProviderError(null);
192
+ try {
193
+ await auth.signInWithOAuth(providerId, payload);
194
+ } catch (error: unknown) {
195
+ const err = error instanceof Error ? error : new Error(String(error));
196
+ setAuthProviderError(err);
197
+ throw error;
198
+ } finally {
199
+ setAuthLoading(false);
200
+ }
201
+ }, [auth]);
202
+
203
+ const skipLogin = useCallback(() => {
204
+ setLoginSkipped(true);
205
+ setUser(null);
206
+ }, []);
207
+
208
+ const forgotPassword = useCallback(async (email: string) => {
209
+ if (!auth) throw new Error("Rebase client with auth is required");
210
+ setAuthLoading(true);
211
+ setAuthProviderError(null);
212
+ try {
213
+ await auth.resetPasswordForEmail(email);
214
+ } catch (error: unknown) {
215
+ const err = error instanceof Error ? error : new Error(String(error));
216
+ setAuthProviderError(err);
217
+ throw error;
218
+ } finally {
219
+ setAuthLoading(false);
220
+ }
221
+ }, [auth]);
222
+
223
+ const resetPassword = useCallback(async (token: string, password: string) => {
224
+ if (!auth) throw new Error("Rebase client with auth is required");
225
+ setAuthLoading(true);
226
+ setAuthProviderError(null);
227
+ try {
228
+ await auth.resetPassword(token, password);
229
+ } catch (error: unknown) {
230
+ const err = error instanceof Error ? error : new Error(String(error));
231
+ setAuthProviderError(err);
232
+ throw error;
233
+ } finally {
234
+ setAuthLoading(false);
235
+ }
236
+ }, [auth]);
237
+
238
+ const changePassword = useCallback(async (oldPassword: string, newPassword: string) => {
239
+ if (!auth) throw new Error("Rebase client with auth is required");
240
+ setAuthLoading(true);
241
+ setAuthProviderError(null);
242
+ try {
243
+ await auth.changePassword(oldPassword, newPassword);
244
+ // Sign out after password change as sessions are usually invalidated
245
+ await auth.signOut();
246
+ } catch (error: unknown) {
247
+ const err = error instanceof Error ? error : new Error(String(error));
248
+ setAuthProviderError(err);
249
+ throw error;
250
+ } finally {
251
+ setAuthLoading(false);
252
+ }
253
+ }, [auth]);
254
+
255
+ const updateProfile = useCallback(async (displayName?: string, photoURL?: string) => {
256
+ if (!auth) throw new Error("Rebase client with auth is required");
257
+ setAuthLoading(true);
258
+ setAuthProviderError(null);
259
+ try {
260
+ const updatedUser = await auth.updateUser({ displayName, photoURL });
261
+ return updatedUser;
262
+ } catch (error: unknown) {
263
+ const err = error instanceof Error ? error : new Error(String(error));
264
+ setAuthProviderError(err);
265
+ throw error;
266
+ } finally {
267
+ setAuthLoading(false);
268
+ }
269
+ }, [auth]);
270
+
271
+ const fetchSessions = useCallback(async () => {
272
+ if (!auth) throw new Error("Rebase client with auth is required");
273
+ return await auth.getSessions();
274
+ }, [auth]);
275
+
276
+ const revokeSession = useCallback(async (sessionId: string) => {
277
+ if (!auth) throw new Error("Rebase client with auth is required");
278
+ await auth.revokeSession(sessionId);
279
+ }, [auth]);
280
+
281
+ const revokeAllSessions = useCallback(async () => {
282
+ if (!auth) throw new Error("Rebase client with auth is required");
283
+ await auth.revokeAllSessions();
284
+ }, [auth]);
285
+
286
+ return {
287
+ user,
288
+ authLoading,
289
+ initialLoading,
290
+ authError,
291
+ authProviderError,
292
+ loginSkipped,
293
+ needsSetup: authConfig?.needsSetup ?? false,
294
+ registrationEnabled: authConfig?.registrationEnabled ?? false,
295
+ getAuthToken,
296
+ getApiUrl: () => client?.baseUrl,
297
+ signOut,
298
+ emailPasswordLogin,
299
+ register,
300
+ googleLogin,
301
+ oauthLogin,
302
+ skipLogin,
303
+ forgotPassword,
304
+ resetPassword,
305
+ changePassword,
306
+ updateProfile,
307
+ fetchSessions,
308
+ revokeSession,
309
+ revokeAllSessions,
310
+ clearError,
311
+ setAuthProviderError,
312
+ extra,
313
+ setExtra,
314
+ capabilities: {
315
+ emailPasswordLogin: true,
316
+ googleLogin: !!(props.googleClientId),
317
+ registration: authConfig?.registrationEnabled ?? false,
318
+ passwordReset: authConfig?.passwordReset ?? false,
319
+ // Defaults to true so backends predating this field keep showing the
320
+ // action. Adapters that don't support it report `false` explicitly.
321
+ adminPasswordReset: authConfig?.adminPasswordReset ?? true,
322
+ sessionManagement: true,
323
+ profileUpdate: true,
324
+ emailVerification: authConfig?.emailVerification ?? false,
325
+ enabledProviders: authConfig?.enabledProviders ?? []
326
+ }
327
+ };
328
+ }
@@ -0,0 +1,47 @@
1
+ import { Wand2Icon } from "@rebasepro/ui";
2
+ import React from "react";
3
+
4
+ export interface AIIconProps {
5
+ size?: "smallest" | "small" | "medium" | "large";
6
+ className?: string;
7
+ }
8
+
9
+ /**
10
+ * AI-styled AutoAwesome icon with gradient coloring.
11
+ * Used consistently across AI features for visual identification.
12
+ */
13
+ export function AIIcon({ size = "small", className }: AIIconProps) {
14
+ const sizeMap: Record<string, number> = {
15
+ smallest: 16,
16
+ small: 20,
17
+ medium: 24,
18
+ large: 28
19
+ };
20
+ const numericSize = typeof size === "string" ? sizeMap[size] || 20 : size;
21
+
22
+ return (
23
+ <Wand2Icon
24
+ size={numericSize}
25
+ className={className}
26
+ style={{
27
+ background: "linear-gradient(to right, var(--color-primary), var(--color-secondary))",
28
+ WebkitBackgroundClip: "text",
29
+ WebkitTextFillColor: "transparent",
30
+ backgroundClip: "text"
31
+ }}
32
+ />
33
+ );
34
+ }
35
+
36
+ /**
37
+ * Small animated dot indicator for AI-modified elements.
38
+ * Shows a pulsing gradient dot.
39
+ */
40
+ export function AIModifiedIndicator({ className }: { className?: string }) {
41
+ return (
42
+ <div
43
+ className={`w-2 h-2 rounded-full bg-gradient-to-r from-primary to-secondary animate-pulse ${className ?? ""}`}
44
+ title="AI modified"
45
+ />
46
+ );
47
+ }
File without changes