@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,139 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useMemo, useState } from 'react';
4
+ import { RoboSystemsAuthClient } from '../auth-core/client';
5
+ import { getAppConfig } from '../auth-core/config';
6
+ import { useSSO } from '../auth-core/sso';
7
+ import { LogoBadge, Spinner } from '../ui-components';
8
+ /**
9
+ * Map a login failure to a user-facing message. A connectivity failure (the
10
+ * request never reached the server — `fetch` throws a `TypeError`) must NOT be
11
+ * reported as bad credentials: that sends users to reset a password that is
12
+ * actually correct. Reached-server auth rejection (401/403, or an empty/invalid
13
+ * auth body) stays "Invalid email or password"; 5xx gets its own message.
14
+ */
15
+ export function loginErrorMessage(error) {
16
+ var _a, _b, _c;
17
+ const err = error;
18
+ const status = (_a = err === null || err === void 0 ? void 0 : err.status) !== null && _a !== void 0 ? _a : (_b = err === null || err === void 0 ? void 0 : err.response) === null || _b === void 0 ? void 0 : _b.status;
19
+ const message = String((_c = err === null || err === void 0 ? void 0 : err.message) !== null && _c !== void 0 ? _c : '');
20
+ if (error instanceof TypeError ||
21
+ /failed to fetch|networkerror|load failed|fetch failed|err_(connection|network|name_not_resolved)/i.test(message)) {
22
+ return 'Unable to reach the server. Check your connection and try again.';
23
+ }
24
+ if (typeof status === 'number' && status >= 500) {
25
+ return 'The server ran into a problem. Please try again in a moment.';
26
+ }
27
+ return 'Invalid email or password';
28
+ }
29
+ export function SignInForm({ onSuccess, onRedirect, redirectTo = '/home', className = '', apiUrl, enableSSO = true, currentApp, }) {
30
+ const [formData, setFormData] = useState({
31
+ email: '',
32
+ password: '',
33
+ });
34
+ const [loading, setLoading] = useState(false);
35
+ const [error, setError] = useState('');
36
+ const [ssoChecking, setSSOChecking] = useState(enableSSO);
37
+ const [redirecting, setRedirecting] = useState(false);
38
+ const authClient = useMemo(() => new RoboSystemsAuthClient(apiUrl), [apiUrl]);
39
+ const { checkSSOAuthentication, handleSSOLogin } = useSSO(apiUrl);
40
+ // Check for existing authentication or SSO login on mount
41
+ useEffect(() => {
42
+ if (!enableSSO)
43
+ return;
44
+ const checkAuth = async () => {
45
+ try {
46
+ // Capture returnUrl before handleSSOLogin cleans up URL params
47
+ const urlParams = new URLSearchParams(window.location.search);
48
+ const ssoReturnUrl = urlParams.get('returnUrl');
49
+ // Check for SSO login from URL parameters
50
+ const ssoUser = await handleSSOLogin();
51
+ if (ssoUser) {
52
+ setRedirecting(true);
53
+ if (onSuccess) {
54
+ onSuccess(ssoUser);
55
+ }
56
+ // handleSSOLogin schedules navigation to returnUrl if present;
57
+ // only redirect to the default if no returnUrl was specified
58
+ if (!ssoReturnUrl) {
59
+ window.location.href = redirectTo;
60
+ }
61
+ return;
62
+ }
63
+ // Check if user is already authenticated via regular session
64
+ try {
65
+ const existingUser = await checkSSOAuthentication();
66
+ if (existingUser) {
67
+ setRedirecting(true);
68
+ if (onSuccess) {
69
+ onSuccess(existingUser);
70
+ }
71
+ window.location.href = redirectTo;
72
+ return;
73
+ }
74
+ }
75
+ catch (_a) {
76
+ // User not authenticated, continue to login form
77
+ }
78
+ // Only hide spinner if authentication failed
79
+ setSSOChecking(false);
80
+ }
81
+ catch (error) {
82
+ if (process.env.NODE_ENV === 'development') {
83
+ console.error('Auth check failed:', error);
84
+ }
85
+ setSSOChecking(false);
86
+ }
87
+ };
88
+ checkAuth();
89
+ }, [
90
+ enableSSO,
91
+ redirectTo,
92
+ onSuccess,
93
+ handleSSOLogin,
94
+ checkSSOAuthentication,
95
+ authClient,
96
+ ]);
97
+ const handleInputChange = (e) => {
98
+ const { name, value } = e.target;
99
+ setFormData((prev) => (Object.assign(Object.assign({}, prev), { [name]: value })));
100
+ };
101
+ const handleSubmit = async (e) => {
102
+ e.preventDefault();
103
+ setLoading(true);
104
+ setError('');
105
+ try {
106
+ const result = await authClient.login(formData.email, formData.password);
107
+ // Call onSuccess callback if provided
108
+ if (onSuccess) {
109
+ onSuccess(result.user);
110
+ }
111
+ // Set redirecting state to keep showing loading
112
+ setRedirecting(true);
113
+ // Use window.location.href for reliable redirect
114
+ window.location.href = redirectTo;
115
+ }
116
+ catch (error) {
117
+ setError(loginErrorMessage(error));
118
+ setLoading(false);
119
+ }
120
+ };
121
+ const handleSignUpClick = () => {
122
+ if (onRedirect) {
123
+ onRedirect('/register');
124
+ }
125
+ else {
126
+ window.location.href = '/register';
127
+ }
128
+ };
129
+ // Determine app name based on currentApp prop
130
+ const appName = getAppConfig(currentApp || 'robosystems').displayName;
131
+ if (ssoChecking || redirecting) {
132
+ return (_jsx("div", { className: "via-primary-950 flex min-h-screen items-center justify-center bg-linear-to-br from-black to-zinc-900 p-6", children: _jsx("div", { className: "w-full max-w-md", children: _jsxs("div", { className: "text-center", children: [_jsx(LogoBadge, { animate: "loop", className: "mx-auto h-16 w-16" }), _jsx("h1", { className: "font-heading mt-4 text-center text-2xl font-semibold tracking-tight text-white", children: appName })] }) }) }));
133
+ }
134
+ return (_jsx("div", { className: "via-primary-950 flex min-h-screen items-center justify-center bg-linear-to-br from-black to-zinc-900 px-4 py-12 sm:px-6 lg:px-8", children: _jsxs("div", { className: "w-full max-w-lg space-y-8", children: [_jsxs("div", { className: "text-center", children: [_jsx(LogoBadge, { animate: "once", className: "mx-auto h-14 w-14" }), _jsx("h1", { className: "font-heading mt-4 text-center text-2xl font-semibold tracking-tight text-white", children: appName }), _jsx("h2", { className: "mt-2 text-center text-xl font-semibold tracking-tight text-gray-300", children: "Sign in to your account" })] }), _jsxs("form", { className: ['mt-8 space-y-6', className].filter(Boolean).join(' '), onSubmit: handleSubmit, children: [error && (_jsx("div", { className: "rounded-md border border-red-800 bg-red-900/50 p-4", children: _jsx("div", { className: "text-sm text-red-300", children: error }) })), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: "email", className: "sr-only", children: "Email address" }), _jsx("input", { id: "email", name: "email", type: "email", autoComplete: "email", required: true, value: formData.email, onChange: handleInputChange, className: "focus:ring-primary-500 relative block w-full rounded-md border-0 bg-gray-800 px-5 py-4 text-base leading-7 text-white ring-1 ring-gray-600 ring-inset placeholder:text-gray-400 focus:z-10 focus:ring-2 focus:ring-inset", placeholder: "Email address", disabled: loading || redirecting })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "password", className: "sr-only", children: "Password" }), _jsx("input", { id: "password", name: "password", type: "password", autoComplete: "current-password", required: true, value: formData.password, onChange: handleInputChange, className: "focus:ring-primary-500 relative block w-full rounded-md border-0 bg-gray-800 px-5 py-4 text-base leading-7 text-white ring-1 ring-gray-600 ring-inset placeholder:text-gray-400 focus:z-10 focus:ring-2 focus:ring-inset", placeholder: "Password", disabled: loading || redirecting })] })] }), _jsx("div", { children: _jsxs("button", { type: "submit", disabled: loading || redirecting, className: "group bg-primary-600 hover:bg-primary-700 focus-visible:outline-primary-500 relative flex w-full justify-center rounded-md px-4 py-3 text-sm font-semibold text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid disabled:cursor-not-allowed disabled:opacity-50", children: [loading && _jsx(Spinner, { size: "sm", className: "mr-2 text-white" }), redirecting
135
+ ? 'Redirecting...'
136
+ : loading
137
+ ? 'Signing in...'
138
+ : 'Sign in'] }) }), _jsxs("div", { className: "flex flex-col items-center gap-2", children: [_jsx("a", { href: "/auth/forgot-password", className: "hover:text-primary-400 text-sm text-gray-300", children: "Forgot password?" }), _jsx("button", { type: "button", onClick: handleSignUpClick, className: "hover:text-primary-400 text-sm font-medium text-gray-300", children: "Don't have an account? Sign up" })] })] })] }) }));
139
+ }
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import type { AuthUser } from '../auth-core/types';
3
+ export interface SignUpFormProps {
4
+ onSuccess?: (user: AuthUser) => void;
5
+ onRedirect?: (url: string) => void;
6
+ redirectTo?: string;
7
+ className?: string;
8
+ showConfirmPassword?: boolean;
9
+ showTermsAcceptance?: boolean;
10
+ autoSignIn?: boolean;
11
+ apiUrl: string;
12
+ currentApp?: string;
13
+ turnstileSiteKey?: string;
14
+ }
15
+ export declare function SignUpForm({ onSuccess, onRedirect, redirectTo, className, showConfirmPassword, showTermsAcceptance, autoSignIn, apiUrl, currentApp, turnstileSiteKey, }: SignUpFormProps): React.JSX.Element;
@@ -0,0 +1,167 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
+ import { RoboSystemsAuthClient } from '../auth-core/client';
5
+ import { getAppConfig } from '../auth-core/config';
6
+ import { LogoBadge, Spinner } from '../ui-components';
7
+ import { TurnstileWidget } from './TurnstileWidget';
8
+ export function SignUpForm({ onSuccess, onRedirect, redirectTo = '/login', className = '', showConfirmPassword = true, showTermsAcceptance = true, autoSignIn = false, apiUrl, currentApp, turnstileSiteKey, }) {
9
+ const [formData, setFormData] = useState({
10
+ name: '',
11
+ email: '',
12
+ password: '',
13
+ confirmPassword: '',
14
+ });
15
+ const [loading, setLoading] = useState(false);
16
+ const [error, setError] = useState('');
17
+ const [captchaToken, setCaptchaToken] = useState(null);
18
+ const [captchaError, setCaptchaError] = useState(null);
19
+ const [passwordStrength, setPasswordStrength] = useState(null);
20
+ const [checkingPassword, setCheckingPassword] = useState(false);
21
+ const debounceRef = useRef(null);
22
+ const authClientRef = useRef(new RoboSystemsAuthClient(apiUrl));
23
+ const checkPassword = useCallback(async (password, email) => {
24
+ if (password.length < 4) {
25
+ setPasswordStrength(null);
26
+ return;
27
+ }
28
+ setCheckingPassword(true);
29
+ try {
30
+ const result = await authClientRef.current.checkPasswordStrength(password, email || undefined);
31
+ setPasswordStrength(result);
32
+ }
33
+ catch (_a) {
34
+ setPasswordStrength(null);
35
+ }
36
+ finally {
37
+ setCheckingPassword(false);
38
+ }
39
+ }, []);
40
+ useEffect(() => {
41
+ if (debounceRef.current)
42
+ clearTimeout(debounceRef.current);
43
+ if (formData.password.length < 4) {
44
+ setPasswordStrength(null);
45
+ return;
46
+ }
47
+ debounceRef.current = setTimeout(() => {
48
+ checkPassword(formData.password, formData.email);
49
+ }, 400);
50
+ return () => {
51
+ if (debounceRef.current)
52
+ clearTimeout(debounceRef.current);
53
+ };
54
+ }, [formData.password, formData.email, checkPassword]);
55
+ const handleInputChange = (e) => {
56
+ const { name, value } = e.target;
57
+ setFormData((prev) => (Object.assign(Object.assign({}, prev), { [name]: value })));
58
+ };
59
+ const handleCaptchaVerify = (token) => {
60
+ setCaptchaToken(token);
61
+ setCaptchaError(null);
62
+ };
63
+ const handleCaptchaError = (error) => {
64
+ setCaptchaError(error);
65
+ setCaptchaToken(null);
66
+ };
67
+ const handleCaptchaExpire = () => {
68
+ setCaptchaToken(null);
69
+ setCaptchaError('CAPTCHA expired. Please verify again.');
70
+ };
71
+ const handleSubmit = async (e) => {
72
+ var _a, _b;
73
+ e.preventDefault();
74
+ setLoading(true);
75
+ setError('');
76
+ setCaptchaError(null);
77
+ // Validate confirm password if enabled
78
+ if (showConfirmPassword && formData.password !== formData.confirmPassword) {
79
+ setError('Passwords do not match');
80
+ setLoading(false);
81
+ return;
82
+ }
83
+ // Validate password strength
84
+ if (checkingPassword) {
85
+ setError('Checking password strength, please wait...');
86
+ setLoading(false);
87
+ return;
88
+ }
89
+ if (passwordStrength && !passwordStrength.is_valid) {
90
+ setError(passwordStrength.errors.length > 0
91
+ ? passwordStrength.errors.join('. ')
92
+ : 'Password does not meet requirements');
93
+ setLoading(false);
94
+ return;
95
+ }
96
+ // Check CAPTCHA if site key is provided (production mode)
97
+ if (turnstileSiteKey && !captchaToken) {
98
+ setError('Please complete the security verification to continue');
99
+ setLoading(false);
100
+ return;
101
+ }
102
+ try {
103
+ const result = await authClientRef.current.register(formData.email, formData.password, formData.name, captchaToken || undefined);
104
+ // Call onSuccess callback if provided
105
+ if (onSuccess) {
106
+ onSuccess(result.user);
107
+ }
108
+ // Use window.location.href for reliable redirect
109
+ window.location.href = redirectTo;
110
+ }
111
+ catch (error) {
112
+ // Check if it's a CAPTCHA-related error
113
+ if (((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('CAPTCHA')) ||
114
+ ((_b = error === null || error === void 0 ? void 0 : error.message) === null || _b === void 0 ? void 0 : _b.includes('captcha'))) {
115
+ setError(error.message || 'CAPTCHA verification failed. Please try again.');
116
+ // Reset CAPTCHA on failure
117
+ setCaptchaToken(null);
118
+ }
119
+ else {
120
+ setError((error === null || error === void 0 ? void 0 : error.message) || 'Registration failed. Please try again.');
121
+ }
122
+ }
123
+ finally {
124
+ setLoading(false);
125
+ }
126
+ };
127
+ const handleSignInClick = () => {
128
+ if (onRedirect) {
129
+ onRedirect('/login');
130
+ }
131
+ else {
132
+ window.location.href = '/login';
133
+ }
134
+ };
135
+ const handleTermsClick = (link) => {
136
+ if (onRedirect) {
137
+ onRedirect(link);
138
+ }
139
+ else {
140
+ window.location.href = link;
141
+ }
142
+ };
143
+ // Determine app name based on currentApp prop
144
+ const appName = getAppConfig(currentApp || 'robosystems').displayName;
145
+ return (_jsx("div", { className: "via-primary-950 flex min-h-screen items-center justify-center bg-linear-to-br from-black to-zinc-900 px-4 py-12 sm:px-6 lg:px-8", children: _jsxs("div", { className: "w-full max-w-lg space-y-8", children: [_jsxs("div", { className: "text-center", children: [_jsx(LogoBadge, { animate: "once", className: "mx-auto h-14 w-14" }), _jsx("h1", { className: "font-heading mt-4 text-center text-2xl font-semibold tracking-tight text-white", children: appName }), _jsx("h2", { className: "mt-6 text-center text-xl font-semibold tracking-tight text-gray-300", children: "Create your account" })] }), _jsxs("form", { className: ['mt-8 space-y-6', className].filter(Boolean).join(' '), onSubmit: handleSubmit, children: [error && (_jsx("div", { className: "rounded-md border border-red-800 bg-red-900/50 p-4", children: _jsx("div", { className: "text-sm text-red-300", children: error }) })), captchaError && (_jsx("div", { className: "rounded-md border border-red-800 bg-red-900/50 p-4", children: _jsx("div", { className: "text-sm text-red-300", children: captchaError }) })), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: "name", className: "sr-only", children: "Full name" }), _jsx("input", { id: "name", name: "name", type: "text", autoComplete: "name", required: true, value: formData.name, onChange: handleInputChange, className: "focus:ring-primary-500 relative block w-full rounded-md border-0 bg-gray-800 px-5 py-4 text-base leading-7 text-white ring-1 ring-gray-600 ring-inset placeholder:text-gray-400 focus:z-10 focus:ring-2 focus:ring-inset", placeholder: "Full name", disabled: loading })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "email", className: "sr-only", children: "Email address" }), _jsx("input", { id: "email", name: "email", type: "email", autoComplete: "email", required: true, value: formData.email, onChange: handleInputChange, className: "focus:ring-primary-500 relative block w-full rounded-md border-0 bg-gray-800 px-5 py-4 text-base leading-7 text-white ring-1 ring-gray-600 ring-inset placeholder:text-gray-400 focus:z-10 focus:ring-2 focus:ring-inset", placeholder: "Email address", disabled: loading })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "password", className: "sr-only", children: "Password" }), _jsx("input", { id: "password", name: "password", type: "password", autoComplete: "new-password", required: true, value: formData.password, onChange: handleInputChange, className: "focus:ring-primary-500 relative block w-full rounded-md border-0 bg-gray-800 px-5 py-4 text-base leading-7 text-white ring-1 ring-gray-600 ring-inset placeholder:text-gray-400 focus:z-10 focus:ring-2 focus:ring-inset", placeholder: "Password", disabled: loading })] }), showConfirmPassword && (_jsxs("div", { children: [_jsx("label", { htmlFor: "confirmPassword", className: "sr-only", children: "Confirm password" }), _jsx("input", { id: "confirmPassword", name: "confirmPassword", type: "password", autoComplete: "new-password", required: true, value: formData.confirmPassword, onChange: handleInputChange, className: "focus:ring-primary-500 relative block w-full rounded-md border-0 bg-gray-800 px-5 py-4 text-base leading-7 text-white ring-1 ring-gray-600 ring-inset placeholder:text-gray-400 focus:z-10 focus:ring-2 focus:ring-inset", placeholder: "Confirm password", disabled: loading })] })), _jsx("div", { className: "min-h-[2.75rem]", children: passwordStrength && (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "h-1.5 flex-1 overflow-hidden rounded-full bg-gray-700", children: _jsx("div", { className: [
146
+ 'h-full rounded-full transition-all duration-300',
147
+ passwordStrength.score < 30
148
+ ? 'bg-red-500'
149
+ : passwordStrength.score < 60
150
+ ? 'bg-yellow-500'
151
+ : passwordStrength.score < 80
152
+ ? 'bg-primary-400'
153
+ : 'bg-green-500',
154
+ ].join(' '), style: { width: `${passwordStrength.score}%` } }) }), _jsx("span", { className: [
155
+ 'text-xs font-medium capitalize',
156
+ passwordStrength.score < 30
157
+ ? 'text-red-400'
158
+ : passwordStrength.score < 60
159
+ ? 'text-yellow-400'
160
+ : passwordStrength.score < 80
161
+ ? 'text-primary-300'
162
+ : 'text-green-400',
163
+ ].join(' '), children: passwordStrength.strength.replace('-', ' ') })] }), passwordStrength.errors.length > 0 && (_jsx("p", { className: "text-xs text-red-400", children: passwordStrength.errors[0] })), passwordStrength.errors.length === 0 &&
164
+ passwordStrength.suggestions.length > 0 && (_jsx("p", { className: "text-xs text-gray-400", children: passwordStrength.suggestions[0] }))] })) })] }), turnstileSiteKey && (_jsx("div", { className: "flex justify-center", children: _jsx(TurnstileWidget, { siteKey: turnstileSiteKey, onVerify: handleCaptchaVerify, onError: handleCaptchaError, onExpire: handleCaptchaExpire, theme: "dark", className: "flex justify-center", disabled: loading }) })), _jsx("div", { children: _jsxs("button", { type: "submit", disabled: loading ||
165
+ checkingPassword ||
166
+ (turnstileSiteKey && !captchaToken), className: "group bg-primary-600 hover:bg-primary-700 focus-visible:outline-primary-500 relative flex w-full justify-center rounded-md px-4 py-3 text-sm font-semibold text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid disabled:cursor-not-allowed disabled:opacity-50", children: [loading && _jsx(Spinner, { size: "sm", className: "mr-2 text-white" }), loading ? 'Creating account...' : 'Create account'] }) }), showTermsAcceptance && (_jsxs("div", { className: "text-center text-xs text-gray-400", children: ["By creating an account, you agree to our", ' ', _jsx("a", { href: `${process.env.NEXT_PUBLIC_ROBOSYSTEMS_APP_URL || 'https://robosystems.ai'}/pages/terms`, target: "_blank", rel: "noopener noreferrer", className: "hover:text-primary-400 font-medium text-gray-300", children: "Terms of Service" }), ' ', "and", ' ', _jsx("a", { href: `${process.env.NEXT_PUBLIC_ROBOSYSTEMS_APP_URL || 'https://robosystems.ai'}/pages/privacy`, target: "_blank", rel: "noopener noreferrer", className: "hover:text-primary-400 font-medium text-gray-300", children: "Privacy Policy" })] })), _jsx("div", { className: "text-center", children: _jsx("button", { type: "button", onClick: handleSignInClick, className: "hover:text-primary-400 font-medium text-gray-300", children: "Already have an account? Sign in" }) })] })] }) }));
167
+ }
@@ -0,0 +1,99 @@
1
+ import React from 'react';
2
+ export interface TurnstileWidgetProps {
3
+ /** Cloudflare Turnstile site key obtained from dashboard */
4
+ siteKey: string;
5
+ /** Callback fired when CAPTCHA verification succeeds */
6
+ onVerify: (token: string) => void;
7
+ /** Callback fired when CAPTCHA verification fails */
8
+ onError?: (error: string) => void;
9
+ /** Callback fired when CAPTCHA token expires */
10
+ onExpire?: () => void;
11
+ /** Callback fired when Turnstile script loads successfully */
12
+ onLoad?: () => void;
13
+ /** Widget theme - matches your site's color scheme */
14
+ theme?: 'light' | 'dark' | 'auto';
15
+ /** Widget size - use 'compact' for mobile-friendly layouts */
16
+ size?: 'normal' | 'compact';
17
+ /** Language code for widget text (e.g., 'en', 'es', 'fr') */
18
+ language?: string;
19
+ /** Additional CSS classes for styling the widget container */
20
+ className?: string;
21
+ /** Whether the widget should be disabled (prevents interaction) */
22
+ disabled?: boolean;
23
+ }
24
+ export interface TurnstileWidgetRef {
25
+ /** Reset the widget to its initial state, clearing any verification */
26
+ reset: () => void;
27
+ /** Get the current verification token if available */
28
+ getResponse: () => string | undefined;
29
+ }
30
+ declare global {
31
+ interface Window {
32
+ turnstile?: {
33
+ render: (element: HTMLElement | string, options: {
34
+ sitekey: string;
35
+ callback?: (token: string) => void;
36
+ 'error-callback'?: (error: string) => void;
37
+ 'expired-callback'?: () => void;
38
+ theme?: 'light' | 'dark' | 'auto';
39
+ size?: 'normal' | 'compact';
40
+ language?: string;
41
+ }) => string;
42
+ reset: (widgetId?: string) => void;
43
+ remove: (widgetId?: string) => void;
44
+ getResponse: (widgetId?: string) => string | undefined;
45
+ };
46
+ }
47
+ }
48
+ /**
49
+ * TurnstileWidget - A React component for Cloudflare Turnstile CAPTCHA
50
+ *
51
+ * This component provides a secure, privacy-focused CAPTCHA solution that:
52
+ * - Automatically loads the Turnstile script when needed
53
+ * - Handles widget lifecycle (render, reset, cleanup)
54
+ * - Provides TypeScript support with comprehensive types
55
+ * - Supports themes, languages, and different sizes
56
+ * - Prevents memory leaks with proper cleanup
57
+ *
58
+ * @example
59
+ * ```tsx
60
+ * <TurnstileWidget
61
+ * siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY}
62
+ * onVerify={(token) => console.log('Verified:', token)}
63
+ * theme="dark"
64
+ * />
65
+ * ```
66
+ */
67
+ export declare const TurnstileWidget: React.ForwardRefExoticComponent<TurnstileWidgetProps & React.RefAttributes<TurnstileWidgetRef>>;
68
+ /**
69
+ * useTurnstile - React hook for simplified Turnstile integration
70
+ *
71
+ * This hook manages the Turnstile widget state and provides:
72
+ * - Automatic token management
73
+ * - Error and expiration handling
74
+ * - Loading state tracking
75
+ * - Pre-configured TurnstileWidget component
76
+ *
77
+ * @param siteKey - Your Cloudflare Turnstile site key
78
+ * @returns Object containing token, error, loading state, and configured widget
79
+ *
80
+ * @example
81
+ * ```tsx
82
+ * const { token, error, isLoading, TurnstileWidget } = useTurnstile(siteKey)
83
+ *
84
+ * return (
85
+ * <form onSubmit={handleSubmit}>
86
+ * <TurnstileWidget theme="dark" />
87
+ * <button disabled={!token}>Submit</button>
88
+ * </form>
89
+ * )
90
+ * ```
91
+ */
92
+ export declare function useTurnstile(siteKey: string): {
93
+ token: string;
94
+ error: string;
95
+ isLoading: boolean;
96
+ isExpired: boolean;
97
+ reset: () => void;
98
+ TurnstileWidget: (props: Omit<TurnstileWidgetProps, "siteKey" | "onVerify" | "onError" | "onExpire" | "onLoad">) => React.JSX.Element;
99
+ };
@@ -0,0 +1,224 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React, { useCallback, useEffect, useRef } from 'react';
4
+ /**
5
+ * TurnstileWidget - A React component for Cloudflare Turnstile CAPTCHA
6
+ *
7
+ * This component provides a secure, privacy-focused CAPTCHA solution that:
8
+ * - Automatically loads the Turnstile script when needed
9
+ * - Handles widget lifecycle (render, reset, cleanup)
10
+ * - Provides TypeScript support with comprehensive types
11
+ * - Supports themes, languages, and different sizes
12
+ * - Prevents memory leaks with proper cleanup
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * <TurnstileWidget
17
+ * siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY}
18
+ * onVerify={(token) => console.log('Verified:', token)}
19
+ * theme="dark"
20
+ * />
21
+ * ```
22
+ */
23
+ export const TurnstileWidget = React.forwardRef(({ siteKey, onVerify, onError, onExpire, onLoad, theme = 'auto', size = 'normal', language, className = '', disabled = false, }, ref) => {
24
+ const containerRef = useRef(null);
25
+ const widgetIdRef = useRef(null);
26
+ const scriptLoadedRef = useRef(false);
27
+ const loadTurnstileScript = useCallback(() => {
28
+ if (scriptLoadedRef.current ||
29
+ document.querySelector('script[src*="challenges.cloudflare.com"]')) {
30
+ return Promise.resolve();
31
+ }
32
+ return new Promise((resolve, reject) => {
33
+ const script = document.createElement('script');
34
+ script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js';
35
+ script.async = true;
36
+ script.defer = true;
37
+ script.onload = () => {
38
+ scriptLoadedRef.current = true;
39
+ onLoad === null || onLoad === void 0 ? void 0 : onLoad();
40
+ resolve();
41
+ };
42
+ script.onerror = () => {
43
+ reject(new Error('Failed to load Turnstile script'));
44
+ };
45
+ document.head.appendChild(script);
46
+ });
47
+ }, [onLoad]);
48
+ const renderWidget = useCallback(() => {
49
+ if (!containerRef.current || !window.turnstile || !siteKey || disabled) {
50
+ return;
51
+ }
52
+ // Remove existing widget if present
53
+ if (widgetIdRef.current) {
54
+ try {
55
+ window.turnstile.remove(widgetIdRef.current);
56
+ }
57
+ catch (error) {
58
+ console.warn('Failed to remove existing Turnstile widget:', error);
59
+ }
60
+ widgetIdRef.current = null;
61
+ }
62
+ // Clear container
63
+ containerRef.current.innerHTML = '';
64
+ try {
65
+ widgetIdRef.current = window.turnstile.render(containerRef.current, {
66
+ sitekey: siteKey,
67
+ callback: (token) => {
68
+ onVerify(token);
69
+ },
70
+ 'error-callback': (error) => {
71
+ onError === null || onError === void 0 ? void 0 : onError(error);
72
+ },
73
+ 'expired-callback': () => {
74
+ onExpire === null || onExpire === void 0 ? void 0 : onExpire();
75
+ },
76
+ theme: theme,
77
+ size: size,
78
+ language,
79
+ });
80
+ }
81
+ catch (error) {
82
+ console.error('Failed to render Turnstile widget:', error);
83
+ onError === null || onError === void 0 ? void 0 : onError('Failed to render CAPTCHA widget');
84
+ }
85
+ }, [siteKey, onVerify, onError, onExpire, theme, size, language, disabled]);
86
+ const reset = useCallback(() => {
87
+ if (widgetIdRef.current && window.turnstile) {
88
+ try {
89
+ window.turnstile.reset(widgetIdRef.current);
90
+ }
91
+ catch (error) {
92
+ console.warn('Failed to reset Turnstile widget:', error);
93
+ }
94
+ }
95
+ }, []);
96
+ const getResponse = useCallback(() => {
97
+ if (widgetIdRef.current && window.turnstile) {
98
+ try {
99
+ return window.turnstile.getResponse(widgetIdRef.current);
100
+ }
101
+ catch (error) {
102
+ console.warn('Failed to get Turnstile response:', error);
103
+ }
104
+ }
105
+ return undefined;
106
+ }, []);
107
+ useEffect(() => {
108
+ if (!siteKey || disabled) {
109
+ return;
110
+ }
111
+ const initWidget = async () => {
112
+ try {
113
+ await loadTurnstileScript();
114
+ // Wait a bit for the script to fully initialize
115
+ setTimeout(() => {
116
+ if (window.turnstile) {
117
+ renderWidget();
118
+ }
119
+ }, 100);
120
+ }
121
+ catch (error) {
122
+ console.error('Failed to initialize Turnstile:', error);
123
+ onError === null || onError === void 0 ? void 0 : onError('Failed to load CAPTCHA');
124
+ }
125
+ };
126
+ initWidget();
127
+ return () => {
128
+ if (widgetIdRef.current && window.turnstile) {
129
+ try {
130
+ window.turnstile.remove(widgetIdRef.current);
131
+ }
132
+ catch (error) {
133
+ console.warn('Failed to cleanup Turnstile widget:', error);
134
+ }
135
+ }
136
+ widgetIdRef.current = null;
137
+ };
138
+ // eslint-disable-next-line react-hooks/exhaustive-deps
139
+ }, [siteKey, disabled]);
140
+ // Re-render widget ONLY when theme, size, or language actually changes
141
+ // Remove renderWidget from dependencies to prevent infinite loop
142
+ useEffect(() => {
143
+ if (widgetIdRef.current && window.turnstile && !disabled) {
144
+ renderWidget();
145
+ }
146
+ // eslint-disable-next-line react-hooks/exhaustive-deps
147
+ }, [theme, size, language, disabled]);
148
+ // Expose reset and getResponse methods via ref
149
+ React.useImperativeHandle(ref, () => ({
150
+ reset,
151
+ getResponse,
152
+ }), [reset, getResponse]);
153
+ if (!siteKey || disabled) {
154
+ return null;
155
+ }
156
+ return (_jsx("div", { className: className, children: _jsx("div", { ref: containerRef, className: "flex justify-center", "data-testid": "turnstile-widget" }) }));
157
+ });
158
+ TurnstileWidget.displayName = 'TurnstileWidget';
159
+ /**
160
+ * useTurnstile - React hook for simplified Turnstile integration
161
+ *
162
+ * This hook manages the Turnstile widget state and provides:
163
+ * - Automatic token management
164
+ * - Error and expiration handling
165
+ * - Loading state tracking
166
+ * - Pre-configured TurnstileWidget component
167
+ *
168
+ * @param siteKey - Your Cloudflare Turnstile site key
169
+ * @returns Object containing token, error, loading state, and configured widget
170
+ *
171
+ * @example
172
+ * ```tsx
173
+ * const { token, error, isLoading, TurnstileWidget } = useTurnstile(siteKey)
174
+ *
175
+ * return (
176
+ * <form onSubmit={handleSubmit}>
177
+ * <TurnstileWidget theme="dark" />
178
+ * <button disabled={!token}>Submit</button>
179
+ * </form>
180
+ * )
181
+ * ```
182
+ */
183
+ export function useTurnstile(siteKey) {
184
+ const [token, setToken] = React.useState(null);
185
+ const [error, setError] = React.useState(null);
186
+ const [isLoading, setIsLoading] = React.useState(false);
187
+ const [isExpired, setIsExpired] = React.useState(false);
188
+ const handleVerify = useCallback((verifiedToken) => {
189
+ setToken(verifiedToken);
190
+ setError(null);
191
+ setIsExpired(false);
192
+ setIsLoading(false);
193
+ }, []);
194
+ const handleError = useCallback((errorMessage) => {
195
+ setError(errorMessage);
196
+ setToken(null);
197
+ setIsExpired(false);
198
+ setIsLoading(false);
199
+ }, []);
200
+ const handleExpire = useCallback(() => {
201
+ setIsExpired(true);
202
+ setToken(null);
203
+ setError(null);
204
+ setIsLoading(false);
205
+ }, []);
206
+ const handleLoad = useCallback(() => {
207
+ setIsLoading(false);
208
+ setError(null);
209
+ }, []);
210
+ const reset = useCallback(() => {
211
+ setToken(null);
212
+ setError(null);
213
+ setIsExpired(false);
214
+ setIsLoading(true);
215
+ }, []);
216
+ return {
217
+ token,
218
+ error,
219
+ isLoading,
220
+ isExpired,
221
+ reset,
222
+ TurnstileWidget: React.useCallback((props) => (_jsx(TurnstileWidget, Object.assign({ siteKey: siteKey, onVerify: handleVerify, onError: handleError, onExpire: handleExpire, onLoad: handleLoad }, props))), [siteKey, handleVerify, handleError, handleExpire, handleLoad]),
223
+ };
224
+ }
@@ -0,0 +1,6 @@
1
+ export { AppSwitcher } from './AppSwitcher';
2
+ export { AuthGuard } from './AuthGuard';
3
+ export { AuthProvider, useAuth } from './AuthProvider';
4
+ export { SignInForm } from './SignInForm';
5
+ export { SignUpForm } from './SignUpForm';
6
+ export { TurnstileWidget, useTurnstile } from './TurnstileWidget';