@checkstack/auth-frontend 0.5.17 → 0.5.19

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # @checkstack/auth-frontend
2
2
 
3
+ ## 0.5.19
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [26d8bae]
8
+ - @checkstack/ui@1.3.0
9
+
10
+ ## 0.5.18
11
+
12
+ ### Patch Changes
13
+
14
+ - d1a2796: Enforce stricter code quality standards and eliminate AI slop anti-patterns.
15
+
16
+ **New utility**
17
+
18
+ - `extractErrorMessage(error, fallback?)` in `@checkstack/common` for consistent error extraction
19
+
20
+ **ESLint rules**
21
+
22
+ - `react-hooks/rules-of-hooks` and `exhaustive-deps` for hook correctness
23
+ - `no-console` in frontend packages — forces `toast` over silent `console.error`
24
+ - `no-restricted-syntax` banning `instanceof Error` — forces `extractErrorMessage`
25
+ - Custom `no-eslint-disable-any` rule preventing `@typescript-eslint/no-explicit-any` circumvention
26
+
27
+ **Refactoring**
28
+
29
+ - Replace 141 `instanceof Error` boilerplate patterns across the codebase
30
+ - Replace swallowed `console.error` with user-visible `toast.error()` feedback
31
+ - Remove 15 redundant `as` type casts in IntegrationsPage and ProviderConnectionsPage
32
+ - Consolidate 3 identical callback handlers into `handleDialogClose`
33
+ - Fix conditional React hook call in `FormField.tsx`
34
+ - Fix unstable useMemo deps in `Dashboard.tsx`
35
+ - Replace `useEffect`→`setState` with derived `useMemo` in `RegisterPage.tsx`
36
+ - Rewrite `keystore.test.ts` with typed `DrizzleMockChain` (eliminating 7 `any` suppressions)
37
+ - Delete obvious comments in `encryption.ts` and Teams `provider.ts`
38
+
39
+ - Updated dependencies [d1a2796]
40
+ - @checkstack/common@0.6.5
41
+ - @checkstack/ui@1.2.1
42
+ - @checkstack/frontend-api@0.3.9
43
+ - @checkstack/auth-common@0.6.1
44
+
3
45
  ## 0.5.17
4
46
 
5
47
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/auth-frontend",
3
- "version": "0.5.17",
3
+ "version": "0.5.19",
4
4
  "type": "module",
5
5
  "main": "src/index.tsx",
6
6
  "checkstack": {
@@ -17,21 +17,21 @@
17
17
  "test:e2e": "bunx playwright test"
18
18
  },
19
19
  "dependencies": {
20
- "@checkstack/frontend-api": "0.3.8",
21
- "@checkstack/common": "0.6.4",
22
- "@checkstack/ui": "1.1.3",
20
+ "@checkstack/frontend-api": "0.3.9",
21
+ "@checkstack/common": "0.6.5",
22
+ "@checkstack/ui": "1.2.1",
23
23
  "react": "^18.2.0",
24
24
  "react-router-dom": "^6.22.0",
25
25
  "lucide-react": "^0.344.0",
26
26
  "better-auth": "^1.1.8",
27
- "@checkstack/auth-common": "0.5.7"
27
+ "@checkstack/auth-common": "0.6.1"
28
28
  },
29
29
  "devDependencies": {
30
30
  "typescript": "^5.0.0",
31
31
  "@types/react": "^18.2.0",
32
32
  "@playwright/test": "^1.49.0",
33
33
  "@checkstack/test-utils-frontend": "0.0.4",
34
- "@checkstack/tsconfig": "0.0.4",
34
+ "@checkstack/tsconfig": "0.0.5",
35
35
  "@checkstack/scripts": "0.1.2"
36
36
  }
37
37
  }
@@ -29,6 +29,7 @@ import { Plus, Trash2, RotateCcw, Copy } from "lucide-react";
29
29
  import { usePluginClient } from "@checkstack/frontend-api";
30
30
  import { AuthApi } from "@checkstack/auth-common";
31
31
  import type { Role } from "../api";
32
+ import { extractErrorMessage } from "@checkstack/common";
32
33
 
33
34
  export interface Application {
34
35
  id: string;
@@ -89,7 +90,7 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
89
90
  },
90
91
  onError: (error) => {
91
92
  toast.error(
92
- error instanceof Error ? error.message : "Failed to create application",
93
+ extractErrorMessage(error, "Failed to create application"),
93
94
  );
94
95
  },
95
96
  });
@@ -100,7 +101,7 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
100
101
  },
101
102
  onError: (error) => {
102
103
  toast.error(
103
- error instanceof Error ? error.message : "Failed to update application",
104
+ extractErrorMessage(error, "Failed to update application"),
104
105
  );
105
106
  },
106
107
  });
@@ -113,7 +114,7 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
113
114
  },
114
115
  onError: (error) => {
115
116
  toast.error(
116
- error instanceof Error ? error.message : "Failed to delete application",
117
+ extractErrorMessage(error, "Failed to delete application"),
117
118
  );
118
119
  },
119
120
  });
@@ -130,9 +131,7 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
130
131
  },
131
132
  onError: (error) => {
132
133
  toast.error(
133
- error instanceof Error
134
- ? error.message
135
- : "Failed to regenerate secret",
134
+ extractErrorMessage(error, "Failed to regenerate secret"),
136
135
  );
137
136
  },
138
137
  });
@@ -20,6 +20,7 @@ import {
20
20
  Checkbox,
21
21
  } from "@checkstack/ui";
22
22
  import { getAuthClientLazy } from "../lib/auth-client";
23
+ import { extractErrorMessage } from "@checkstack/common";
23
24
 
24
25
  export const ChangePasswordPage = () => {
25
26
  const navigate = useNavigate();
@@ -84,7 +85,7 @@ export const ChangePasswordPage = () => {
84
85
  }
85
86
  } catch (error_) {
86
87
  setError(
87
- error_ instanceof Error ? error_.message : "Failed to change password"
88
+ extractErrorMessage(error_, "Failed to change password")
88
89
  );
89
90
  } finally {
90
91
  setLoading(false);
@@ -31,9 +31,8 @@ export const ForgotPasswordPage = () => {
31
31
  email,
32
32
  redirectTo: "/auth/reset-password",
33
33
  });
34
- } catch (error) {
34
+ } catch {
35
35
  // Silently handle errors to prevent timing attacks
36
- console.error("Password reset request failed:", error);
37
36
  } finally {
38
37
  setLoading(false);
39
38
  setSubmitted(true);
@@ -36,6 +36,8 @@ import {
36
36
  InfoBannerTitle,
37
37
  InfoBannerDescription,
38
38
  } from "@checkstack/ui";
39
+ import { useToast } from "@checkstack/ui";
40
+ import { extractErrorMessage } from "@checkstack/common";
39
41
  import { authApiRef, EnabledAuthStrategy } from "../api";
40
42
  import { useEnabledStrategies } from "../hooks/useEnabledStrategies";
41
43
  import { useAccessRules } from "../hooks/useAccessRules";
@@ -57,6 +59,7 @@ export const LoginPage = () => {
57
59
  const authApi = useApi(authApiRef);
58
60
  const authClient = usePluginClient(AuthApi);
59
61
  const { strategies, loading: strategiesLoading } = useEnabledStrategies();
62
+ const toast = useToast();
60
63
 
61
64
  // Query: Registration status
62
65
  const { data: registrationData } = authClient.getRegistrationStatus.useQuery(
@@ -90,7 +93,7 @@ export const LoginPage = () => {
90
93
  await authApi.signInWithSocial(strategy.id);
91
94
  } catch (error) {
92
95
  setError("Failed to initialize social login");
93
- console.error("Social login failed:", error);
96
+ toast.error(extractErrorMessage(error, "Failed to initialize social login"));
94
97
  }
95
98
  return;
96
99
  }
@@ -138,7 +141,7 @@ export const LoginPage = () => {
138
141
  setError(data.error?.message || "Authentication failed");
139
142
  }
140
143
  } catch (error) {
141
- console.error("Social login failed:", error);
144
+ toast.error(extractErrorMessage(error, "Social login failed"));
142
145
  }
143
146
  };
144
147
 
@@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom";
3
3
  import { User, Lock, Mail, CheckCircle, AlertCircle } from "lucide-react";
4
4
  import { usePluginClient } from "@checkstack/frontend-api";
5
5
  import { AuthApi, authRoutes, passwordSchema } from "@checkstack/auth-common";
6
- import { resolveRoute } from "@checkstack/common";
6
+ import { resolveRoute, extractErrorMessage} from "@checkstack/common";
7
7
  import {
8
8
  Button,
9
9
  Input,
@@ -111,7 +111,7 @@ export const OnboardingPage = () => {
111
111
  }
112
112
  } catch (error_) {
113
113
  const message =
114
- error_ instanceof Error ? error_.message : "Failed to complete setup";
114
+ extractErrorMessage(error_, "Failed to complete setup");
115
115
  setError(message);
116
116
  } finally {
117
117
  setLoading(false);
@@ -1,10 +1,10 @@
1
- import React, { useState, useEffect } from "react";
1
+ import React, { useState, useMemo } from "react";
2
2
  import { Link } from "react-router-dom";
3
3
  import { AlertCircle } from "lucide-react";
4
4
  import { useApi, usePluginClient } from "@checkstack/frontend-api";
5
5
  import { authApiRef } from "../api";
6
6
  import { AuthApi, authRoutes, passwordSchema } from "@checkstack/auth-common";
7
- import { resolveRoute } from "@checkstack/common";
7
+ import { resolveRoute, extractErrorMessage } from "@checkstack/common";
8
8
  import {
9
9
  Button,
10
10
  Input,
@@ -20,6 +20,7 @@ import {
20
20
  InfoBannerContent,
21
21
  InfoBannerTitle,
22
22
  InfoBannerDescription,
23
+ useToast,
23
24
  } from "@checkstack/ui";
24
25
  import { useEnabledStrategies } from "../hooks/useEnabledStrategies";
25
26
  import { SocialProviderButton } from "./SocialProviderButton";
@@ -30,25 +31,18 @@ export const RegisterPage = () => {
30
31
  const [email, setEmail] = useState("");
31
32
  const [password, setPassword] = useState("");
32
33
  const [loading, setLoading] = useState(false);
33
- const [validationErrors, setValidationErrors] = useState<string[]>([]);
34
34
 
35
35
  const authApi = useApi(authApiRef);
36
36
  const authClient = usePluginClient(AuthApi);
37
37
  const { strategies, loading: strategiesLoading } = useEnabledStrategies();
38
38
  const authBetterClient = getAuthClientLazy();
39
+ const toast = useToast();
39
40
 
40
- // Validate password on change
41
- useEffect(() => {
42
- if (password) {
43
- const result = passwordSchema.safeParse(password);
44
- if (result.success) {
45
- setValidationErrors([]);
46
- } else {
47
- setValidationErrors(result.error.issues.map((issue) => issue.message));
48
- }
49
- } else {
50
- setValidationErrors([]);
51
- }
41
+ // Derive validation errors directly from password (no state/effect needed)
42
+ const validationErrors = useMemo(() => {
43
+ if (!password) return [];
44
+ const result = passwordSchema.safeParse(password);
45
+ return result.success ? [] : result.error.issues.map((issue) => issue.message);
52
46
  }, [password]);
53
47
 
54
48
  // Query: Registration status
@@ -73,13 +67,13 @@ export const RegisterPage = () => {
73
67
  password,
74
68
  });
75
69
  if (res.error) {
76
- console.error("Registration failed:", res.error);
70
+ toast.error(res.error.message ?? "Registration failed");
77
71
  } else {
78
72
  // Use full page navigation to ensure session state refreshes in navbar
79
73
  globalThis.location.href = "/";
80
74
  }
81
75
  } catch (error) {
82
- console.error("Registration failed:", error);
76
+ toast.error(extractErrorMessage(error, "Registration failed"));
83
77
  } finally {
84
78
  setLoading(false);
85
79
  }
@@ -90,7 +84,7 @@ export const RegisterPage = () => {
90
84
  await authApi.signInWithSocial(provider);
91
85
  // Navigation will happen automatically after OAuth redirect
92
86
  } catch (error) {
93
- console.error("Social registration failed:", error);
87
+ toast.error(extractErrorMessage(error, "Social registration failed"));
94
88
  }
95
89
  };
96
90
 
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
2
2
  import { Link, useSearchParams, useNavigate } from "react-router-dom";
3
3
  import { Lock, ArrowLeft, CheckCircle, AlertCircle } from "lucide-react";
4
4
  import { authRoutes, passwordSchema } from "@checkstack/auth-common";
5
- import { resolveRoute } from "@checkstack/common";
5
+ import { resolveRoute, extractErrorMessage} from "@checkstack/common";
6
6
  import {
7
7
  Button,
8
8
  Input,
@@ -83,7 +83,7 @@ export const ResetPasswordPage = () => {
83
83
  }
84
84
  } catch (error_) {
85
85
  setError(
86
- error_ instanceof Error ? error_.message : "Failed to reset password"
86
+ extractErrorMessage(error_, "Failed to reset password")
87
87
  );
88
88
  } finally {
89
89
  setLoading(false);
@@ -21,6 +21,7 @@ import { usePluginClient } from "@checkstack/frontend-api";
21
21
  import { AuthApi } from "@checkstack/auth-common";
22
22
  import type { Role, AccessRuleEntry } from "../api";
23
23
  import { RoleDialog } from "./RoleDialog";
24
+ import { extractErrorMessage } from "@checkstack/common";
24
25
 
25
26
  export interface RolesTabProps {
26
27
  roles: Role[];
@@ -58,7 +59,7 @@ export const RolesTab: React.FC<RolesTabProps> = ({
58
59
  },
59
60
  onError: (error) => {
60
61
  toast.error(
61
- error instanceof Error ? error.message : "Failed to create role",
62
+ extractErrorMessage(error, "Failed to create role"),
62
63
  );
63
64
  },
64
65
  });
@@ -70,7 +71,7 @@ export const RolesTab: React.FC<RolesTabProps> = ({
70
71
  },
71
72
  onError: (error) => {
72
73
  toast.error(
73
- error instanceof Error ? error.message : "Failed to update role",
74
+ extractErrorMessage(error, "Failed to update role"),
74
75
  );
75
76
  },
76
77
  });
@@ -83,7 +84,7 @@ export const RolesTab: React.FC<RolesTabProps> = ({
83
84
  },
84
85
  onError: (error) => {
85
86
  toast.error(
86
- error instanceof Error ? error.message : "Failed to delete role",
87
+ extractErrorMessage(error, "Failed to delete role"),
87
88
  );
88
89
  },
89
90
  });
@@ -19,6 +19,7 @@ import { usePluginClient } from "@checkstack/frontend-api";
19
19
  import { AuthApi } from "@checkstack/auth-common";
20
20
  import type { AuthStrategy } from "../api";
21
21
  import { AuthStrategyCard } from "./AuthStrategyCard";
22
+ import { extractErrorMessage } from "@checkstack/common";
22
23
 
23
24
  export interface StrategiesTabProps {
24
25
  strategies: AuthStrategy[];
@@ -88,7 +89,7 @@ export const StrategiesTab: React.FC<StrategiesTabProps> = ({
88
89
  },
89
90
  onError: (error) => {
90
91
  toast.error(
91
- error instanceof Error ? error.message : "Failed to update strategy"
92
+ extractErrorMessage(error, "Failed to update strategy")
92
93
  );
93
94
  },
94
95
  });
@@ -99,7 +100,7 @@ export const StrategiesTab: React.FC<StrategiesTabProps> = ({
99
100
  },
100
101
  onError: (error) => {
101
102
  toast.error(
102
- error instanceof Error ? error.message : "Failed to save settings"
103
+ extractErrorMessage(error, "Failed to save settings")
103
104
  );
104
105
  },
105
106
  });
@@ -112,7 +113,7 @@ export const StrategiesTab: React.FC<StrategiesTabProps> = ({
112
113
  },
113
114
  onError: (error) => {
114
115
  toast.error(
115
- error instanceof Error ? error.message : "Failed to reload auth"
116
+ extractErrorMessage(error, "Failed to reload auth")
116
117
  );
117
118
  setReloading(false);
118
119
  },
@@ -33,6 +33,7 @@ import {
33
33
  accessApiRef,
34
34
  } from "@checkstack/frontend-api";
35
35
  import { AuthApi, authAccess } from "@checkstack/auth-common";
36
+ import { extractErrorMessage } from "@checkstack/common";
36
37
 
37
38
  interface TeamAccess {
38
39
  teamId: string;
@@ -121,7 +122,7 @@ export const TeamAccessEditor: React.FC<TeamAccessEditorProps> = ({
121
122
  },
122
123
  onError: (error) => {
123
124
  toast.error(
124
- error instanceof Error ? error.message : "Failed to update access",
125
+ extractErrorMessage(error, "Failed to update access"),
125
126
  );
126
127
  },
127
128
  });
@@ -134,7 +135,7 @@ export const TeamAccessEditor: React.FC<TeamAccessEditorProps> = ({
134
135
  },
135
136
  onError: (error) => {
136
137
  toast.error(
137
- error instanceof Error ? error.message : "Failed to remove access",
138
+ extractErrorMessage(error, "Failed to remove access"),
138
139
  );
139
140
  },
140
141
  });
@@ -146,7 +147,7 @@ export const TeamAccessEditor: React.FC<TeamAccessEditorProps> = ({
146
147
  },
147
148
  onError: (error) => {
148
149
  toast.error(
149
- error instanceof Error ? error.message : "Failed to update settings",
150
+ extractErrorMessage(error, "Failed to update settings"),
150
151
  );
151
152
  },
152
153
  });
@@ -43,6 +43,7 @@ import {
43
43
  import { usePluginClient } from "@checkstack/frontend-api";
44
44
  import { AuthApi } from "@checkstack/auth-common";
45
45
  import type { AuthUser } from "../api";
46
+ import { extractErrorMessage } from "@checkstack/common";
46
47
 
47
48
  interface Team {
48
49
  id: string;
@@ -116,7 +117,7 @@ export const TeamsTab: React.FC<TeamsTabProps> = ({
116
117
  },
117
118
  onError: (error) => {
118
119
  toast.error(
119
- error instanceof Error ? error.message : "Failed to create team",
120
+ extractErrorMessage(error, "Failed to create team"),
120
121
  );
121
122
  },
122
123
  });
@@ -130,7 +131,7 @@ export const TeamsTab: React.FC<TeamsTabProps> = ({
130
131
  },
131
132
  onError: (error) => {
132
133
  toast.error(
133
- error instanceof Error ? error.message : "Failed to update team",
134
+ extractErrorMessage(error, "Failed to update team"),
134
135
  );
135
136
  },
136
137
  });
@@ -144,7 +145,7 @@ export const TeamsTab: React.FC<TeamsTabProps> = ({
144
145
  },
145
146
  onError: (error) => {
146
147
  toast.error(
147
- error instanceof Error ? error.message : "Failed to delete team",
148
+ extractErrorMessage(error, "Failed to delete team"),
148
149
  );
149
150
  },
150
151
  });
@@ -158,7 +159,7 @@ export const TeamsTab: React.FC<TeamsTabProps> = ({
158
159
  },
159
160
  onError: (error) => {
160
161
  toast.error(
161
- error instanceof Error ? error.message : "Failed to add member",
162
+ extractErrorMessage(error, "Failed to add member"),
162
163
  );
163
164
  },
164
165
  });
@@ -171,7 +172,7 @@ export const TeamsTab: React.FC<TeamsTabProps> = ({
171
172
  },
172
173
  onError: (error) => {
173
174
  toast.error(
174
- error instanceof Error ? error.message : "Failed to remove member",
175
+ extractErrorMessage(error, "Failed to remove member"),
175
176
  );
176
177
  },
177
178
  });
@@ -183,7 +184,7 @@ export const TeamsTab: React.FC<TeamsTabProps> = ({
183
184
  },
184
185
  onError: (error) => {
185
186
  toast.error(
186
- error instanceof Error ? error.message : "Failed to promote to manager",
187
+ extractErrorMessage(error, "Failed to promote to manager"),
187
188
  );
188
189
  },
189
190
  });
@@ -195,9 +196,7 @@ export const TeamsTab: React.FC<TeamsTabProps> = ({
195
196
  },
196
197
  onError: (error) => {
197
198
  toast.error(
198
- error instanceof Error
199
- ? error.message
200
- : "Failed to remove manager role",
199
+ extractErrorMessage(error, "Failed to remove manager role"),
201
200
  );
202
201
  },
203
202
  });
@@ -23,6 +23,7 @@ import { usePluginClient } from "@checkstack/frontend-api";
23
23
  import { AuthApi } from "@checkstack/auth-common";
24
24
  import type { AuthUser, Role, AuthStrategy } from "../api";
25
25
  import { CreateUserDialog } from "./CreateUserDialog";
26
+ import { extractErrorMessage } from "@checkstack/common";
26
27
 
27
28
  export interface UsersTabProps {
28
29
  users: (AuthUser & { roles: string[] })[];
@@ -66,7 +67,7 @@ export const UsersTab: React.FC<UsersTabProps> = ({
66
67
  },
67
68
  onError: (error) => {
68
69
  toast.error(
69
- error instanceof Error ? error.message : "Failed to delete user",
70
+ extractErrorMessage(error, "Failed to delete user"),
70
71
  );
71
72
  },
72
73
  });
@@ -77,7 +78,7 @@ export const UsersTab: React.FC<UsersTabProps> = ({
77
78
  },
78
79
  onError: (error) => {
79
80
  toast.error(
80
- error instanceof Error ? error.message : "Failed to update roles",
81
+ extractErrorMessage(error, "Failed to update roles"),
81
82
  );
82
83
  },
83
84
  });
@@ -89,7 +90,7 @@ export const UsersTab: React.FC<UsersTabProps> = ({
89
90
  },
90
91
  onError: (error) => {
91
92
  toast.error(
92
- error instanceof Error ? error.message : "Failed to create user",
93
+ extractErrorMessage(error, "Failed to create user"),
93
94
  );
94
95
  throw error;
95
96
  },
package/src/index.tsx CHANGED
@@ -108,6 +108,7 @@ class BetterAuthApi implements AuthApi {
108
108
  }
109
109
 
110
110
  useSession() {
111
+ // eslint-disable-next-line react-hooks/rules-of-hooks -- Class adapter delegates to hook; consumed as API, not a component
111
112
  return useSessionContext();
112
113
  }
113
114
  }
@@ -178,6 +179,7 @@ export const authPlugin = createFrontendPlugin({
178
179
  createSlotExtension(UserMenuItemsSlot, {
179
180
  id: "auth.user-menu.settings",
180
181
  component: ({ accessRules: userPerms }: UserMenuItemsContext) => {
182
+ // eslint-disable-next-line react-hooks/rules-of-hooks -- Inline component used via createSlotExtension
181
183
  const navigate = useNavigate();
182
184
  const qualifiedId = `${pluginMetadata.pluginId}.${authAccess.strategies.id}`;
183
185
  const canManage =
@@ -198,6 +200,7 @@ export const authPlugin = createFrontendPlugin({
198
200
  createSlotExtension(UserMenuItemsSlot, {
199
201
  id: "auth.user-menu.profile",
200
202
  component: () => {
203
+ // eslint-disable-next-line react-hooks/rules-of-hooks -- Inline component used via createSlotExtension
201
204
  const navigate = useNavigate();
202
205
 
203
206
  return (
@@ -8,6 +8,7 @@ import type { AccessRule } from "@checkstack/common";
8
8
  */
9
9
  export class AuthAccessApi implements AccessApi {
10
10
  useAccess(accessRule: AccessRule): { loading: boolean; allowed: boolean } {
11
+ // eslint-disable-next-line react-hooks/rules-of-hooks -- Class adapter delegates to hook; consumed as API, not a component
11
12
  const { accessRules, loading } = useAccessRules();
12
13
 
13
14
  if (loading) {