@choiceform/shared-auth 0.1.17 → 0.1.18

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 (105) hide show
  1. package/README.md +286 -134
  2. package/dist/__tests__/auth-utils.test.d.ts +5 -0
  3. package/dist/__tests__/auth-utils.test.d.ts.map +1 -0
  4. package/dist/__tests__/auth-utils.test.js +96 -0
  5. package/dist/__tests__/store.test.d.ts +5 -0
  6. package/dist/__tests__/store.test.d.ts.map +1 -0
  7. package/dist/__tests__/store.test.js +210 -0
  8. package/dist/__tests__/user-mapper.test.d.ts +5 -0
  9. package/dist/__tests__/user-mapper.test.d.ts.map +1 -0
  10. package/dist/__tests__/user-mapper.test.js +76 -0
  11. package/dist/api/auth-api.d.ts +93 -9
  12. package/dist/api/auth-api.d.ts.map +1 -1
  13. package/dist/api/auth-api.js +219 -80
  14. package/dist/api/client.d.ts +10 -0
  15. package/dist/api/client.d.ts.map +1 -1
  16. package/dist/api/client.js +10 -0
  17. package/dist/api/organization-api.d.ts +2 -7
  18. package/dist/api/organization-api.d.ts.map +1 -1
  19. package/dist/api/organization-api.js +2 -17
  20. package/dist/api/team-api.d.ts +1 -5
  21. package/dist/api/team-api.d.ts.map +1 -1
  22. package/dist/api/team-api.js +5 -11
  23. package/dist/components/auth-sync.d.ts +27 -0
  24. package/dist/components/auth-sync.d.ts.map +1 -0
  25. package/dist/components/auth-sync.js +117 -0
  26. package/dist/components/protected-route.d.ts +18 -0
  27. package/dist/components/protected-route.d.ts.map +1 -0
  28. package/dist/components/protected-route.js +34 -0
  29. package/dist/components/sign-in-page.d.ts +21 -0
  30. package/dist/components/sign-in-page.d.ts.map +1 -0
  31. package/dist/components/sign-in-page.js +31 -0
  32. package/dist/config.js +1 -1
  33. package/dist/core.d.ts +148 -71
  34. package/dist/core.d.ts.map +1 -1
  35. package/dist/core.js +109 -28
  36. package/dist/hooks/index.d.ts +8 -0
  37. package/dist/hooks/index.d.ts.map +1 -0
  38. package/dist/hooks/index.js +7 -0
  39. package/dist/hooks/use-auth-init.d.ts +4 -0
  40. package/dist/hooks/use-auth-init.d.ts.map +1 -1
  41. package/dist/hooks/use-auth-init.js +16 -21
  42. package/dist/hooks/use-auth-sync.d.ts +60 -0
  43. package/dist/hooks/use-auth-sync.d.ts.map +1 -0
  44. package/dist/hooks/use-auth-sync.js +116 -0
  45. package/dist/hooks/use-email-verification.d.ts +85 -0
  46. package/dist/hooks/use-email-verification.d.ts.map +1 -0
  47. package/dist/hooks/use-email-verification.js +145 -0
  48. package/dist/hooks/use-protected-route.d.ts +67 -0
  49. package/dist/hooks/use-protected-route.d.ts.map +1 -0
  50. package/dist/hooks/use-protected-route.js +102 -0
  51. package/dist/index.d.ts +12 -6
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +43 -13
  54. package/dist/init.d.ts +127 -70
  55. package/dist/init.d.ts.map +1 -1
  56. package/dist/lib/auth-client.d.ts.map +1 -1
  57. package/dist/lib/auth-client.js +75 -2
  58. package/dist/services/auth-service.d.ts +101 -0
  59. package/dist/services/auth-service.d.ts.map +1 -0
  60. package/dist/services/auth-service.js +356 -0
  61. package/dist/services/callback-service.d.ts +33 -0
  62. package/dist/services/callback-service.d.ts.map +1 -0
  63. package/dist/services/callback-service.js +473 -0
  64. package/dist/services/companion-team.d.ts.map +1 -1
  65. package/dist/services/companion-team.js +41 -39
  66. package/dist/services/index.d.ts +2 -0
  67. package/dist/services/index.d.ts.map +1 -1
  68. package/dist/services/index.js +2 -0
  69. package/dist/store/actions.d.ts +54 -51
  70. package/dist/store/actions.d.ts.map +1 -1
  71. package/dist/store/actions.js +111 -243
  72. package/dist/store/computed.d.ts +72 -1
  73. package/dist/store/computed.d.ts.map +1 -1
  74. package/dist/store/computed.js +90 -3
  75. package/dist/store/index.d.ts +3 -3
  76. package/dist/store/index.d.ts.map +1 -1
  77. package/dist/store/index.js +2 -2
  78. package/dist/store/state.d.ts +10 -0
  79. package/dist/store/state.d.ts.map +1 -1
  80. package/dist/store/state.js +11 -1
  81. package/dist/store/utils.d.ts +3 -34
  82. package/dist/store/utils.d.ts.map +1 -1
  83. package/dist/store/utils.js +2 -22
  84. package/dist/types/auth.d.ts +106 -0
  85. package/dist/types/auth.d.ts.map +1 -1
  86. package/dist/types/callback.d.ts +35 -0
  87. package/dist/types/callback.d.ts.map +1 -0
  88. package/dist/types/callback.js +1 -0
  89. package/dist/types/index.d.ts +4 -3
  90. package/dist/types/index.d.ts.map +1 -1
  91. package/dist/types/organization.d.ts +19 -3
  92. package/dist/types/organization.d.ts.map +1 -1
  93. package/dist/types/team.d.ts +6 -2
  94. package/dist/types/team.d.ts.map +1 -1
  95. package/dist/types/user.d.ts +7 -3
  96. package/dist/types/user.d.ts.map +1 -1
  97. package/dist/utils/auth-utils.d.ts +60 -0
  98. package/dist/utils/auth-utils.d.ts.map +1 -0
  99. package/dist/utils/auth-utils.js +146 -0
  100. package/dist/utils/index.d.ts +1 -0
  101. package/dist/utils/index.d.ts.map +1 -1
  102. package/dist/utils/index.js +1 -0
  103. package/dist/utils/user-mapper.d.ts.map +1 -1
  104. package/dist/utils/user-mapper.js +2 -1
  105. package/package.json +10 -2
package/README.md CHANGED
@@ -1,208 +1,360 @@
1
1
  # @choiceform/shared-auth
2
2
 
3
- 共享认证包 - 基于 Better Auth 的统一认证解决方案
3
+ 基于 Better Auth + Legend State 的共享认证库。
4
4
 
5
- ## 功能特性
5
+ ## 架构设计
6
6
 
7
- - Better Auth 集成(OAuth、Magic Link)
8
- - Legend State 状态管理
9
- - 自动伴生团队设置
10
- - Token 自动管理
11
- - 完整 TypeScript 类型支持
12
- - 预配置 API 客户端
13
-
14
- ## 安装
15
-
16
- ```bash
17
- pnpm add @choiceform/shared-auth
7
+ ```
8
+ ┌─────────────────────────────────────────────────────────────┐
9
+ │ createAuth() │
10
+ │ (core.ts) │
11
+ ├─────────────────────────────────────────────────────────────┤
12
+ │ │
13
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
14
+ │ │ Store │ │ Service │ │ API │ │ Hooks │ │
15
+ │ │ Layer │ │ Layer │ │ Layer │ │ Layer │ │
16
+ │ │ │ │ │ │ │ │ │ │
17
+ │ │authStore │ │authServ. │ │apiClient │ │useAuthSync │ │
18
+ │ │storeAct. │ │callback │ │authApi │ │useProtected │ │
19
+ │ │computed │ │companion │ │orgApi │ │useEmailVerif │ │
20
+ │ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │
21
+ │ │
22
+ └─────────────────────────────────────────────────────────────┘
18
23
  ```
19
24
 
20
- ## 环境变量
25
+ ### 四层架构
21
26
 
22
- ```bash
23
- VITE_ONEAUTH_BASE_URL=https://oneauth.choiceform.io
24
- ```
27
+ | 层级 | 职责 | 文件 |
28
+ |------|------|------|
29
+ | **Store Layer** | 响应式状态管理 | `store/state.ts`, `store/actions.ts`, `store/computed.ts` |
30
+ | **Service Layer** | 业务逻辑 | `services/auth-service.ts`, `services/callback-service.ts`, `services/companion-team.ts` |
31
+ | **API Layer** | HTTP 请求 | `api/client.ts`, `api/auth-api.ts`, `api/organization-api.ts`, `api/team-api.ts` |
32
+ | **Hooks Layer** | React 专用 | `hooks/use-auth-sync.ts`, `hooks/use-protected-route.ts`, `hooks/use-email-verification.ts` |
25
33
 
26
34
  ## 快速开始
27
35
 
28
- ### 初始化
29
-
30
36
  ```typescript
31
37
  import { initAuth } from "@choiceform/shared-auth"
32
38
 
33
- export const auth = initAuth({
34
- baseURL: import.meta.env.VITE_ONEAUTH_BASE_URL,
35
- tokenStorageKey: "auth-token",
39
+ // 初始化
40
+ const auth = initAuth({
41
+ baseURL: "https://api.example.com",
36
42
  })
43
+
44
+ // 导出供全局使用
45
+ export { auth }
37
46
  ```
38
47
 
39
- ### 在应用中使用
48
+ ## Hooks(推荐使用)
49
+
50
+ ### useAuthSync
51
+
52
+ 认证状态同步,替代手动同步 Better Auth session:
40
53
 
41
54
  ```typescript
42
- import { use$ } from "@legendapp/state/react"
43
- import { auth } from "./lib/auth"
55
+ import { useAuthSync } from "@choiceform/shared-auth"
44
56
 
45
57
  function App() {
46
- const { isAuthenticated, user } = use$(auth.authStore)
58
+ useAuthSync(auth, {
59
+ skipCompanionTeamPaths: ['/auth/callback', '/auth/delete-success'],
60
+ onAuthChange: (isAuthenticated) => {
61
+ if (isAuthenticated) {
62
+ syncTheme()
63
+ syncLanguage()
64
+ }
65
+ }
66
+ })
67
+
68
+ return <YourApp />
69
+ }
70
+ ```
71
+
72
+ ### useProtectedRoute
73
+
74
+ 路由保护,检查认证状态和邮箱验证:
47
75
 
48
- if (!isAuthenticated) {
49
- return <SignInPage />
76
+ ```typescript
77
+ import { useProtectedRoute } from "@choiceform/shared-auth"
78
+
79
+ function ProtectedRoute({ children }) {
80
+ const navigate = useNavigate()
81
+ const location = useLocation()
82
+
83
+ const { status, shouldRender, redirectPath } = useProtectedRoute(auth, {
84
+ pathname: location.pathname,
85
+ publicRoutePrefixes: ['/resources', '/public'],
86
+ requireEmailVerified: true,
87
+ lang: 'us'
88
+ })
89
+
90
+ useEffect(() => {
91
+ if (redirectPath) {
92
+ navigate(redirectPath, { replace: true })
93
+ }
94
+ }, [redirectPath])
95
+
96
+ if (!shouldRender) {
97
+ return <Loading />
50
98
  }
51
99
 
52
- return <MainApp user={user} />
100
+ return <>{children}</>
53
101
  }
54
102
  ```
55
103
 
56
- ### 登录
104
+ ### useEmailVerification
57
105
 
58
- ```typescript
59
- // OAuth 登录
60
- const handleOAuthSignIn = async () => {
61
- const callbackURL = new URL("/dashboard", window.location.origin)
62
- const newUserCallbackURL = new URL("/dashboard?isNew=true", window.location.origin)
63
-
64
- await auth.authActions.signIn(
65
- "github",
66
- callbackURL.toString(),
67
- newUserCallbackURL.toString()
68
- )
69
- }
106
+ 邮箱验证流程:
70
107
 
71
- // Magic Link 登录
72
- const handleMagicLink = async (email: string) => {
73
- const callbackURL = new URL("/dashboard", window.location.origin)
74
-
75
- await auth.authActions.signInWithMagicLink(
108
+ ```typescript
109
+ import { useEmailVerification } from "@choiceform/shared-auth"
110
+
111
+ function VerifyEmailPage({ email, lang }) {
112
+ const navigate = useNavigate()
113
+
114
+ const {
115
+ isLoading,
116
+ isAlreadyVerified,
117
+ isCountingDown,
118
+ countdown,
119
+ resendVerification,
120
+ changeEmail
121
+ } = useEmailVerification(auth, {
76
122
  email,
77
- callbackURL.toString()
123
+ lang,
124
+ onRedirect: (path) => navigate(path),
125
+ onSendSuccess: (email) => toast.success(`验证邮件已发送到 ${email}`),
126
+ onSendError: () => toast.error('发送失败'),
127
+ onAlreadyVerified: () => navigate('/community')
128
+ })
129
+
130
+ return (
131
+ <div>
132
+ {isAlreadyVerified ? (
133
+ <p>邮箱已验证,正在跳转...</p>
134
+ ) : (
135
+ <>
136
+ <button onClick={resendVerification} disabled={isLoading || isCountingDown}>
137
+ {isCountingDown ? `${countdown}s 后重试` : '重新发送'}
138
+ </button>
139
+ <button onClick={changeEmail}>使用其他邮箱</button>
140
+ </>
141
+ )}
142
+ </div>
78
143
  )
79
144
  }
80
145
  ```
81
146
 
82
- ### 认证状态同步
147
+ ## 服务层
83
148
 
84
- 新用户登录后,使用 `setupCompanionTeam` 设置伴生组织和团队:
149
+ ### callbackService
150
+
151
+ 处理各种认证回调(OAuth、邮箱验证、删除用户等):
85
152
 
86
153
  ```typescript
87
- import { setupCompanionTeam } from "@choiceform/shared-auth"
88
-
89
- // 在认证成功后调用
90
- setupCompanionTeam(auth, token, {
91
- isNewUser: searchParams.get("isNew") === "true",
92
- onComplete: () => {
93
- // 刷新 session
94
- },
154
+ import { createCallbackService } from "@choiceform/shared-auth"
155
+
156
+ const callbackService = createCallbackService(auth, {
157
+ lang: 'us',
158
+ defaultRedirect: '/',
159
+ signInPath: '/sign-in',
160
+ linkExpiredPath: '/auth/link-expired',
161
+ deleteSuccessPath: '/auth/delete-success',
95
162
  })
96
- ```
97
163
 
98
- ## API
164
+ // 处理 OAuth 回调
165
+ const result = await callbackService.handleOAuthCallback(token, isNewUser)
99
166
 
100
- ### `initAuth(config)`
167
+ // 处理邮箱验证回调
168
+ const result = await callbackService.handleEmailVerificationCallback(token)
101
169
 
102
- 快速初始化(使用默认配置)。
170
+ // 处理删除用户回调
171
+ const result = await callbackService.handleDeleteUserCallback(token, userEmail)
103
172
 
104
- | 参数 | 类型 | 说明 |
105
- |------|------|------|
106
- | baseURL | string | OneAuth API 地址 |
107
- | tokenStorageKey | string | localStorage key(默认 `auth-token`) |
108
- | plugins | BetterAuthPlugin[] | Better Auth 插件 |
173
+ // 统一处理入口
174
+ const result = await callbackService.handleCallback(type, token, { isNewUser, userEmail, invitationId })
175
+ ```
109
176
 
110
- ### `createAuth(config)`
177
+ ### authService
111
178
 
112
- 创建认证实例(完整配置)。
179
+ 业务逻辑封装:
113
180
 
114
- ### AuthInstance
181
+ ```typescript
182
+ // OAuth 登录
183
+ await auth.authService.signInWithOAuth("github", callbackURL, newUserCallbackURL, errorCallbackURL)
115
184
 
116
- | 属性 | 说明 |
117
- |------|------|
118
- | authStore | Legend State store |
119
- | authActions | 认证操作(signIn, signOut 等) |
120
- | authApi | 认证 API |
121
- | organizationApi | 组织 API |
122
- | teamApi | 团队 API |
123
- | tokenStorage | Token 存储工具 |
185
+ // Magic Link 登录
186
+ await auth.authService.signInWithMagicLink(email, callbackURL, name, newUserCallbackURL)
124
187
 
125
- ### 工具方法
188
+ // 邮箱密码登录
189
+ const result = await auth.authService.signInWithEmail(email, password)
126
190
 
127
- ```typescript
128
- // 获取用户
129
- const user = auth.getCurrentUser()
130
- const userId = auth.getCurrentUserId()
191
+ // 邮箱密码注册
192
+ const result = await auth.authService.signUpWithEmail(email, password, name, callbackURL)
131
193
 
132
- // 认证状态
133
- const authenticated = auth.isAuthenticated()
134
- const loading = auth.isLoading()
135
- const loaded = auth.isLoaded()
194
+ // 登出
195
+ await auth.authService.signOut("/sign-in")
136
196
 
137
- // 等待认证完成
138
- await auth.waitForAuth()
197
+ // 删除账户
198
+ await auth.authService.deleteUser(callbackURL, password)
139
199
 
140
- // Token
141
- const token = auth.getAuthTokenSync()
142
- const headers = auth.getAuthHeadersSync()
200
+ // 使用 Token 获取 Session
201
+ await auth.authService.fetchAndSetSession(token)
143
202
  ```
144
203
 
145
- ## 类型
204
+ ## 工具函数
146
205
 
147
206
  ```typescript
148
- import type {
149
- SessionUser,
150
- AuthState,
151
- AuthConfig,
152
- Organization,
153
- Team,
154
- Member,
207
+ import {
208
+ // 验证
209
+ isValidEmail,
210
+
211
+ // 错误解析
212
+ parseAuthError,
213
+ isTokenExpiredError,
214
+ AUTH_ERROR_CODES,
215
+
216
+ // URL 工具
217
+ getNameFromEmail,
218
+ buildAuthUrl,
219
+ buildAuthPath,
220
+ clearAuthParams,
221
+
222
+ // 用户映射
223
+ extractSessionUser,
155
224
  } from "@choiceform/shared-auth"
156
- ```
157
-
158
- ### SessionUser
159
225
 
160
- ```typescript
161
- interface SessionUser {
162
- id: string
163
- email: string
164
- name: string
165
- image?: string
166
- metadata?: { color?: string }
167
- inherentOrganizationId?: string // 伴生组织
168
- inherentTeamId?: string // 伴生团队
169
- activeOrganizationId?: string
170
- activeTeamId?: string
226
+ // 验证邮箱
227
+ if (isValidEmail(email)) {
171
228
  // ...
172
229
  }
230
+
231
+ // 解析错误
232
+ const { code, message, isKnownError } = parseAuthError(error)
233
+
234
+ // 检查 token 是否过期
235
+ if (isTokenExpiredError(error)) {
236
+ // 重新登录
237
+ }
238
+
239
+ // 构建 URL
240
+ const url = buildAuthPath('/verify-email', { lang: 'us', email })
173
241
  ```
174
242
 
175
- ## 响应式状态
243
+ ## Store Layer
244
+
245
+ ### authStore (Legend State Observable)
246
+
247
+ 响应式状态,可在 React 组件中使用:
176
248
 
177
249
  ```typescript
178
250
  import { use$ } from "@legendapp/state/react"
179
- import { auth } from "./lib/auth"
180
251
 
181
- function Profile() {
252
+ function MyComponent() {
182
253
  const user = use$(auth.authStore.user)
183
- const loading = use$(auth.authStore.loading)
184
-
185
- if (loading) return <Loading />
186
- if (!user) return <SignIn />
187
-
188
- return <div>Hello, {user.name}</div>
254
+ const isAuthenticated = use$(auth.authStore.isAuthenticated)
255
+ const error = use$(auth.authStore.error)
256
+
257
+ // Computed 状态
258
+ const isReady = use$(auth.authComputed.isReady)
259
+ const activeOrganizationId = use$(auth.authComputed.activeOrganizationId)
189
260
  }
190
261
  ```
191
262
 
192
- ## 更新日志
263
+ ### storeActions
193
264
 
194
- ### v0.2.0
265
+ 状态管理:
195
266
 
196
- - 服务器地址更换为 `https://oneauth.choiceform.io`
197
- - 伴生组织/团队改从 session 获取(`inherentOrganizationId`、`inherentTeamId`)
198
- - 新增 `onboard` API、Magic Link 支持
199
- - 移除 UI 组件(`AuthSync`、`ProtectedRoute`、`SignInPage`),由业务端实现
200
- - 代码清理和优化
267
+ ```typescript
268
+ // 读取
269
+ auth.storeActions.getUser()
270
+ auth.storeActions.getUserId()
271
+ auth.storeActions.isAuthenticated()
272
+ auth.storeActions.isLoading()
273
+ auth.storeActions.isLoaded()
274
+
275
+ // 更新
276
+ auth.storeActions.setUser(user)
277
+ auth.storeActions.updateUser({ name: "New Name" })
278
+ auth.storeActions.setLoading(true)
279
+ auth.storeActions.setError("Error message")
280
+ auth.storeActions.clearAuth()
281
+
282
+ // Active 状态
283
+ auth.storeActions.setActiveOrganizationId(orgId)
284
+ auth.storeActions.setActiveTeamId(teamId)
285
+ ```
201
286
 
202
- ### v0.1.x
287
+ ## 目录结构
203
288
 
204
- - 初始版本
289
+ ```
290
+ src/
291
+ ├── api/ # API Layer
292
+ │ ├── client.ts # HTTP 客户端
293
+ │ ├── auth-api.ts # 认证 API
294
+ │ ├── organization-api.ts
295
+ │ └── team-api.ts
296
+ ├── services/ # Service Layer
297
+ │ ├── auth-service.ts # 认证业务逻辑
298
+ │ ├── callback-service.ts # 回调处理
299
+ │ └── companion-team.ts # 伴生团队设置
300
+ ├── store/ # Store Layer
301
+ │ ├── state.ts # 状态定义
302
+ │ ├── actions.ts # 状态操作
303
+ │ ├── computed.ts # 计算属性
304
+ │ └── utils.ts # 工具函数
305
+ ├── hooks/ # React Hooks
306
+ │ ├── use-auth-init.ts
307
+ │ ├── use-auth-sync.ts
308
+ │ ├── use-protected-route.ts
309
+ │ └── use-email-verification.ts
310
+ ├── utils/ # 工具函数
311
+ │ ├── auth-utils.ts # 认证工具
312
+ │ ├── user-mapper.ts
313
+ │ └── env.ts
314
+ ├── types/ # 类型定义
315
+ ├── lib/ # Better Auth 客户端
316
+ ├── core.ts # 核心入口
317
+ ├── init.ts # 快速初始化
318
+ ├── config.ts # 默认配置
319
+ └── index.ts # 导出入口
320
+ ```
205
321
 
206
- ## License
322
+ ## 类型导出
207
323
 
208
- MIT
324
+ ```typescript
325
+ import type {
326
+ // 核心
327
+ AuthInstance,
328
+ AuthState,
329
+ SessionUser,
330
+
331
+ // 服务
332
+ AuthService,
333
+ CallbackService,
334
+ CallbackType,
335
+ CallbackResult,
336
+
337
+ // Hooks
338
+ UseAuthSyncConfig,
339
+ UseProtectedRouteConfig,
340
+ UseProtectedRouteResult,
341
+ ProtectionStatus,
342
+ UseEmailVerificationConfig,
343
+ UseEmailVerificationResult,
344
+
345
+ // 组织
346
+ Organization,
347
+ Member,
348
+ MemberWithUser,
349
+ Invitation,
350
+ MemberRole,
351
+
352
+ // 团队
353
+ Team,
354
+ TeamMember,
355
+
356
+ // 工具
357
+ AuthErrorCode,
358
+ ParsedAuthError,
359
+ } from "@choiceform/shared-auth"
360
+ ```
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 工具函数测试
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=auth-utils.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-utils.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/auth-utils.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * 工具函数测试
3
+ */
4
+ import { describe, it, expect } from "vitest";
5
+ import { isValidEmail, parseAuthError, isTokenExpiredError, getNameFromEmail, buildAuthPath, AUTH_ERROR_CODES, } from "../utils/auth-utils";
6
+ describe("isValidEmail", () => {
7
+ it("应该验证有效的邮箱", () => {
8
+ expect(isValidEmail("test@example.com")).toBe(true);
9
+ expect(isValidEmail("user.name@domain.co")).toBe(true);
10
+ expect(isValidEmail("user+tag@example.org")).toBe(true);
11
+ });
12
+ it("应该拒绝无效的邮箱", () => {
13
+ expect(isValidEmail("")).toBe(false);
14
+ expect(isValidEmail("invalid")).toBe(false);
15
+ expect(isValidEmail("@example.com")).toBe(false);
16
+ expect(isValidEmail("test@")).toBe(false);
17
+ expect(isValidEmail("test@.com")).toBe(false);
18
+ expect(isValidEmail("test @example.com")).toBe(false);
19
+ });
20
+ });
21
+ describe("parseAuthError", () => {
22
+ it("应该解析 JSON 格式的错误", () => {
23
+ const error = JSON.stringify({ code: "PASSWORD_TOO_SHORT", message: "Password too short" });
24
+ const result = parseAuthError(error);
25
+ expect(result.code).toBe("PASSWORD_TOO_SHORT");
26
+ expect(result.isKnownError).toBe(true);
27
+ });
28
+ it("应该处理包含已知错误代码的字符串", () => {
29
+ const result = parseAuthError("Error: INVALID_EMAIL_OR_PASSWORD");
30
+ expect(result.code).toBe("INVALID_EMAIL_OR_PASSWORD");
31
+ expect(result.isKnownError).toBe(true);
32
+ });
33
+ it("应该处理未知错误", () => {
34
+ const result = parseAuthError("Some unknown error");
35
+ expect(result.code).toBeNull();
36
+ expect(result.isKnownError).toBe(false);
37
+ expect(result.message).toBe("Some unknown error");
38
+ });
39
+ it("应该处理 null 输入", () => {
40
+ const result = parseAuthError(null);
41
+ expect(result.code).toBeNull();
42
+ expect(result.message).toBe("");
43
+ expect(result.isKnownError).toBe(false);
44
+ });
45
+ it("应该识别所有已知的错误代码", () => {
46
+ for (const code of AUTH_ERROR_CODES) {
47
+ const error = JSON.stringify({ code, message: "test" });
48
+ const result = parseAuthError(error);
49
+ expect(result.code).toBe(code);
50
+ expect(result.isKnownError).toBe(true);
51
+ }
52
+ });
53
+ });
54
+ describe("isTokenExpiredError", () => {
55
+ it("应该识别 INVALID_TOKEN 错误", () => {
56
+ expect(isTokenExpiredError(JSON.stringify({ code: "INVALID_TOKEN" }))).toBe(true);
57
+ });
58
+ it("应该识别 SESSION_EXPIRED 错误", () => {
59
+ expect(isTokenExpiredError(JSON.stringify({ code: "SESSION_EXPIRED" }))).toBe(true);
60
+ });
61
+ it("应该识别包含 expired 的消息", () => {
62
+ expect(isTokenExpiredError("Token has expired")).toBe(true);
63
+ });
64
+ it("应该拒绝非过期错误", () => {
65
+ expect(isTokenExpiredError("Password too short")).toBe(false);
66
+ expect(isTokenExpiredError(null)).toBe(false);
67
+ });
68
+ it("应该接受 ParsedAuthError 对象", () => {
69
+ expect(isTokenExpiredError({ code: "INVALID_TOKEN", message: "", isKnownError: true })).toBe(true);
70
+ expect(isTokenExpiredError({ code: null, message: "expired", isKnownError: false })).toBe(true);
71
+ });
72
+ });
73
+ describe("getNameFromEmail", () => {
74
+ it("应该从邮箱中提取名称", () => {
75
+ expect(getNameFromEmail("john.doe@example.com")).toBe("john.doe");
76
+ expect(getNameFromEmail("user@domain.com")).toBe("user");
77
+ expect(getNameFromEmail("test+tag@example.org")).toBe("test+tag");
78
+ });
79
+ it("应该处理空字符串", () => {
80
+ expect(getNameFromEmail("")).toBe("");
81
+ });
82
+ });
83
+ describe("buildAuthPath", () => {
84
+ it("应该构建带参数的路径", () => {
85
+ const path = buildAuthPath("/sign-in", { lang: "us", email: "test@example.com" });
86
+ expect(path).toBe("/sign-in?lang=us&email=test%40example.com");
87
+ });
88
+ it("应该忽略 null 和 undefined 参数", () => {
89
+ const path = buildAuthPath("/sign-in", { lang: "us", email: null, name: undefined });
90
+ expect(path).toBe("/sign-in?lang=us");
91
+ });
92
+ it("应该返回无参数的路径", () => {
93
+ const path = buildAuthPath("/sign-in", {});
94
+ expect(path).toBe("/sign-in");
95
+ });
96
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Store 测试
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/store.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}