@choiceform/shared-auth 0.1.14 → 0.1.16

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 (95) hide show
  1. package/README.md +106 -450
  2. package/dist/api/auth-api.d.ts +28 -0
  3. package/dist/api/auth-api.d.ts.map +1 -0
  4. package/dist/api/auth-api.js +133 -0
  5. package/dist/api/client.d.ts +34 -0
  6. package/dist/api/client.d.ts.map +1 -0
  7. package/dist/api/client.js +104 -0
  8. package/dist/api/index.d.ts +12 -0
  9. package/dist/api/index.d.ts.map +1 -0
  10. package/dist/api/index.js +7 -0
  11. package/dist/api/organization-api.d.ts +96 -0
  12. package/dist/api/organization-api.d.ts.map +1 -0
  13. package/dist/api/organization-api.js +228 -0
  14. package/dist/api/team-api.d.ts +57 -0
  15. package/dist/api/team-api.d.ts.map +1 -0
  16. package/dist/api/team-api.js +118 -0
  17. package/dist/config.d.ts +4 -57
  18. package/dist/config.d.ts.map +1 -1
  19. package/dist/config.js +4 -6
  20. package/dist/core.d.ts +114 -72
  21. package/dist/core.d.ts.map +1 -1
  22. package/dist/core.js +35 -17
  23. package/dist/hooks/use-auth-init.d.ts +10 -0
  24. package/dist/hooks/use-auth-init.d.ts.map +1 -1
  25. package/dist/hooks/use-auth-init.js +59 -31
  26. package/dist/index.d.ts +12 -15
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +26 -13
  29. package/dist/init.d.ts +133 -92
  30. package/dist/init.d.ts.map +1 -1
  31. package/dist/init.js +12 -14
  32. package/dist/lib/auth-client.d.ts +49 -54
  33. package/dist/lib/auth-client.d.ts.map +1 -1
  34. package/dist/lib/auth-client.js +10 -16
  35. package/dist/services/companion-team.d.ts +16 -0
  36. package/dist/services/companion-team.d.ts.map +1 -0
  37. package/dist/services/companion-team.js +73 -0
  38. package/dist/services/index.d.ts +5 -0
  39. package/dist/services/index.d.ts.map +1 -0
  40. package/dist/services/index.js +4 -0
  41. package/dist/store/actions.d.ts +45 -33
  42. package/dist/store/actions.d.ts.map +1 -1
  43. package/dist/store/actions.js +135 -106
  44. package/dist/store/index.d.ts +8 -0
  45. package/dist/store/index.d.ts.map +1 -0
  46. package/dist/store/index.js +7 -0
  47. package/dist/store/state.d.ts +10 -7
  48. package/dist/store/state.d.ts.map +1 -1
  49. package/dist/store/state.js +31 -23
  50. package/dist/store/utils.d.ts +22 -71
  51. package/dist/store/utils.d.ts.map +1 -1
  52. package/dist/store/utils.js +28 -146
  53. package/dist/types/auth.d.ts +107 -0
  54. package/dist/types/auth.d.ts.map +1 -0
  55. package/dist/types/auth.js +4 -0
  56. package/dist/types/index.d.ts +8 -0
  57. package/dist/types/index.d.ts.map +1 -0
  58. package/dist/types/index.js +4 -0
  59. package/dist/types/organization.d.ts +111 -0
  60. package/dist/types/organization.d.ts.map +1 -0
  61. package/dist/types/organization.js +4 -0
  62. package/dist/types/team.d.ts +52 -0
  63. package/dist/types/team.d.ts.map +1 -0
  64. package/dist/types/team.js +4 -0
  65. package/dist/types/user.d.ts +44 -0
  66. package/dist/types/user.d.ts.map +1 -0
  67. package/dist/types/user.js +4 -0
  68. package/dist/utils/date.d.ts +10 -0
  69. package/dist/utils/date.d.ts.map +1 -0
  70. package/dist/utils/date.js +13 -0
  71. package/dist/utils/env.d.ts +20 -0
  72. package/dist/utils/env.d.ts.map +1 -0
  73. package/dist/utils/env.js +23 -0
  74. package/dist/utils/index.d.ts +7 -0
  75. package/dist/utils/index.d.ts.map +1 -0
  76. package/dist/utils/index.js +6 -0
  77. package/dist/utils/user-mapper.d.ts +21 -0
  78. package/dist/utils/user-mapper.d.ts.map +1 -0
  79. package/dist/utils/user-mapper.js +55 -0
  80. package/package.json +3 -4
  81. package/dist/components/auth-sync.d.ts +0 -25
  82. package/dist/components/auth-sync.d.ts.map +0 -1
  83. package/dist/components/auth-sync.js +0 -346
  84. package/dist/components/protected-route.d.ts +0 -18
  85. package/dist/components/protected-route.d.ts.map +0 -1
  86. package/dist/components/protected-route.js +0 -34
  87. package/dist/components/sign-in-page.d.ts +0 -21
  88. package/dist/components/sign-in-page.d.ts.map +0 -1
  89. package/dist/components/sign-in-page.js +0 -31
  90. package/dist/core/init-auth-sync.d.ts +0 -7
  91. package/dist/core/init-auth-sync.d.ts.map +0 -1
  92. package/dist/core/init-auth-sync.js +0 -34
  93. package/dist/types.d.ts +0 -87
  94. package/dist/types.d.ts.map +0 -1
  95. package/dist/types.js +0 -4
@@ -1,67 +1,12 @@
1
1
  /**
2
- * 将日期转换为 ISO 字符串
2
+ * 认证 Actions
3
3
  */
4
- function toISOString(date) {
5
- if (!date)
6
- return undefined;
7
- return typeof date === "string" ? date : new Date(date).toISOString();
8
- }
4
+ import { extractSessionUser } from "../utils";
9
5
  /**
10
- * 从服务器响应中提取用户数据
11
- *
12
- * 支持多种数据结构格式以提供更好的兼容性:
13
- * - 标准格式: { user: {...}, session: {...} }
14
- * - 嵌套格式: { session: { user: {...} } }
15
- * - 包装格式: { data: { user: {...} } }
16
- * - 扁平格式: { id, email, ... }
17
- *
18
- * @param sessionData - 服务器返回的 session 数据
19
- * @returns 映射后的 SessionUser 对象,失败返回 null
20
- */
21
- function extractUserData(sessionData) {
22
- if (!sessionData || typeof sessionData !== "object")
23
- return null;
24
- const data = sessionData;
25
- // 尝试从不同的数据结构中提取用户信息
26
- const rawUserData = data.user || // 标准格式
27
- data.session?.user || // 嵌套格式
28
- data.data?.user || // 包装格式
29
- (data.id && data.email ? data : null); // 扁平格式
30
- if (!rawUserData || typeof rawUserData !== "object")
31
- return null;
32
- const user = rawUserData;
33
- // 提取 session 相关数据(activeOrganizationId 和 activeTeamId 存储在 session 中)
34
- const sessionInfo = data.session;
35
- const sessionCreatedAt = sessionInfo?.createdAt
36
- ? toISOString(sessionInfo.createdAt)
37
- : undefined;
38
- const activeOrganizationId = sessionInfo?.activeOrganizationId;
39
- const activeTeamId = sessionInfo?.activeTeamId;
40
- return {
41
- banExpires: user.banExpires,
42
- banReason: user.banReason,
43
- banned: user.banned,
44
- createdAt: toISOString(user.createdAt) ?? "",
45
- email: user.email,
46
- emailVerified: user.emailVerified ?? false,
47
- id: user.id,
48
- image: user.image || undefined,
49
- lastLoginAt: sessionCreatedAt,
50
- name: user.name,
51
- role: user.role,
52
- updatedAt: toISOString(user.updatedAt) ?? "",
53
- activeOrganizationId,
54
- activeTeamId,
55
- };
56
- }
57
- /**
58
- * 创建认证 actions
6
+ * 创建认证 Actions
59
7
  */
60
8
  export function createAuthActions(authStore, tokenStorage, config, authClient) {
61
- const { baseURL, defaultRedirectAfterLogin, getSessionEndpoint = "/v1/auth/get-session", callbackURLBuilder, skipTokenCleanupOnError = false, } = config;
62
- const buildCallbackURL = callbackURLBuilder || ((redirectTo, baseURL) => {
63
- return `${baseURL}/v1/auth/redirect?to=${encodeURIComponent(redirectTo)}`;
64
- });
9
+ const { baseURL, getSessionEndpoint = "/v1/auth/get-session", skipTokenCleanupOnError = false, } = config;
65
10
  return {
66
11
  /**
67
12
  * 初始化认证状态
@@ -84,21 +29,12 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
84
29
  },
85
30
  /**
86
31
  * 使用 Bearer Token 获取 session
87
- *
88
- * 流程:
89
- * 1. 保存 token 到 localStorage
90
- * 2. 使用 Bearer Token 请求 session endpoint
91
- * 3. 解析响应数据并提取用户信息
92
- * 4. 更新 authStore 状态
93
- *
94
- * @param token - Bearer Token
95
32
  */
96
33
  async fetchSessionWithToken(token) {
97
34
  try {
98
35
  if (!token) {
99
36
  throw new Error("Token is required");
100
37
  }
101
- // 保存 token 到 localStorage(供 authClient 使用)
102
38
  tokenStorage.save(token);
103
39
  const endpoint = `${baseURL}${getSessionEndpoint}`;
104
40
  const response = await fetch(endpoint, {
@@ -112,20 +48,17 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
112
48
  throw new Error(`Failed to fetch session: ${response.status}`);
113
49
  }
114
50
  const responseText = await response.text();
115
- // 解析响应数据
116
51
  let sessionData = null;
117
52
  try {
118
53
  if (responseText && responseText !== "null" && responseText !== "") {
119
54
  sessionData = JSON.parse(responseText);
120
55
  }
121
56
  }
122
- catch (error) {
123
- console.error("[fetchSessionWithToken] Failed to parse session response:", error);
57
+ catch {
124
58
  this.handleUnauthorized();
125
59
  return;
126
60
  }
127
- // 提取并映射用户数据
128
- const userData = extractUserData(sessionData);
61
+ const userData = extractSessionUser(sessionData);
129
62
  if (userData) {
130
63
  authStore.user.set(userData);
131
64
  authStore.isAuthenticated.set(true);
@@ -133,21 +66,15 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
133
66
  authStore.loading.set(false);
134
67
  }
135
68
  else {
136
- console.error("[fetchSessionWithToken] Invalid session data received");
137
69
  this.handleUnauthorized();
138
70
  }
139
71
  }
140
- catch (error) {
141
- console.error("[fetchSessionWithToken] Failed to fetch session:", error);
72
+ catch {
142
73
  this.handleUnauthorized();
143
74
  }
144
75
  },
145
76
  /**
146
- * 处理未授权错误(401 或其他认证失败)
147
- * 清理所有认证状态和本地存储
148
- *
149
- * 注意:如果启用了 skipTokenCleanupOnError,token 不会被清除
150
- * 这对于开发环境很有用,避免因网络波动等问题频繁登出
77
+ * 处理未授权
151
78
  */
152
79
  handleUnauthorized() {
153
80
  authStore.user.set(null);
@@ -173,37 +100,28 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
173
100
  authStore.error.set(error);
174
101
  },
175
102
  /**
176
- * 登录 - 使用 OAuth(默认 GitHub)
103
+ * OAuth 登录
104
+ *
105
+ * 直接调用 better-auth 的 signIn.social,传入完整的回调 URL
177
106
  *
178
- * @param provider OAuth 提供商,默认为 'github'
179
- * @param redirectTo 登录成功后的重定向地址(完整 URL 或相对路径)
180
- * 如果未提供,将使用 defaultRedirectAfterLogin 配置
107
+ * @param provider - OAuth 提供商(如 github
108
+ * @param callbackURL - 登录成功后的完整回调 URL
109
+ * @param newUserCallbackURL - 新用户的回调 URL(可选)
110
+ * @param errorCallbackURL - 登录失败后的完整回调 URL(可选)
181
111
  */
182
- async signIn(provider = "github", redirectTo) {
183
- // 防止重复点击
112
+ async signIn(provider, callbackURL, newUserCallbackURL, errorCallbackURL) {
184
113
  if (authStore.loading.get()) {
185
114
  return;
186
115
  }
187
116
  try {
188
117
  authStore.loading.set(true);
189
118
  authStore.error.set(null);
190
- if (!baseURL) {
191
- throw new Error("Auth baseURL is not configured");
192
- }
193
- // 构建重定向地址:相对路径自动添加 origin
194
- const finalRedirectTo = redirectTo
195
- ? redirectTo.startsWith("http")
196
- ? redirectTo
197
- : `${window.location.origin}${redirectTo}`
198
- : defaultRedirectAfterLogin
199
- ? `${window.location.origin}${defaultRedirectAfterLogin}`
200
- : `${window.location.origin}/community`;
201
- const callbackURL = buildCallbackURL(finalRedirectTo, baseURL);
202
119
  await authClient.signIn.social({
203
120
  provider,
204
121
  callbackURL,
122
+ newUserCallbackURL,
123
+ errorCallbackURL,
205
124
  });
206
- // 注意:OAuth 重定向后,loading 状态会在页面刷新时重置
207
125
  }
208
126
  catch (error) {
209
127
  const message = error instanceof Error ? error.message : "Sign in failed";
@@ -212,12 +130,126 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
212
130
  }
213
131
  },
214
132
  /**
215
- * 登出
133
+ * Magic Link 登录
134
+ *
135
+ * 发送 Magic Link 到用户邮箱
136
+ *
137
+ * @param email - 用户邮箱
138
+ * @param callbackURL - 登录成功后的完整回调 URL
139
+ * @param name - 用户名称(可选,用于新用户)
140
+ * @param newUserCallbackURL - 新用户的回调 URL(可选)
141
+ * @returns 是否发送成功
142
+ */
143
+ async signInWithMagicLink(email, callbackURL, name, newUserCallbackURL) {
144
+ if (authStore.loading.get()) {
145
+ return false;
146
+ }
147
+ if (!authClient.signIn.magicLink) {
148
+ authStore.error.set("Magic link is not available. Please add magicLinkClient() plugin.");
149
+ return false;
150
+ }
151
+ try {
152
+ authStore.loading.set(true);
153
+ authStore.error.set(null);
154
+ await authClient.signIn.magicLink({
155
+ email,
156
+ name,
157
+ callbackURL,
158
+ newUserCallbackURL,
159
+ });
160
+ authStore.loading.set(false);
161
+ return true;
162
+ }
163
+ catch (error) {
164
+ const message = error instanceof Error ? error.message : "Failed to send magic link";
165
+ authStore.error.set(message);
166
+ authStore.loading.set(false);
167
+ return false;
168
+ }
169
+ },
170
+ /**
171
+ * Email/Password 登录
172
+ *
173
+ * @param email - 邮箱
174
+ * @param password - 密码
175
+ * @returns 是否登录成功
176
+ */
177
+ async signInWithEmail(email, password) {
178
+ if (authStore.loading.get()) {
179
+ return false;
180
+ }
181
+ if (!authClient.signIn.email) {
182
+ authStore.error.set("Email sign in is not available.");
183
+ return false;
184
+ }
185
+ try {
186
+ authStore.loading.set(true);
187
+ authStore.error.set(null);
188
+ await authClient.signIn.email({ email, password }, {
189
+ onSuccess: (context) => {
190
+ const token = context.response.headers.get("set-auth-token");
191
+ if (token) {
192
+ tokenStorage.save(token);
193
+ }
194
+ },
195
+ onError: (context) => {
196
+ authStore.error.set(context.error.message || "Login failed");
197
+ },
198
+ });
199
+ authStore.loading.set(false);
200
+ return true;
201
+ }
202
+ catch (error) {
203
+ const message = error instanceof Error ? error.message : "Login failed";
204
+ authStore.error.set(message);
205
+ authStore.loading.set(false);
206
+ return false;
207
+ }
208
+ },
209
+ /**
210
+ * Email/Password 注册
216
211
  *
217
- * @param redirectTo 登出后的重定向地址(可选),如果不提供则不执行自动跳转
212
+ * @param email - 邮箱
213
+ * @param password - 密码
214
+ * @param name - 用户名
215
+ * @returns 是否注册成功
216
+ */
217
+ async signUpWithEmail(email, password, name) {
218
+ if (authStore.loading.get()) {
219
+ return false;
220
+ }
221
+ if (!authClient.signUp?.email) {
222
+ authStore.error.set("Email sign up is not available.");
223
+ return false;
224
+ }
225
+ try {
226
+ authStore.loading.set(true);
227
+ authStore.error.set(null);
228
+ await authClient.signUp.email({ email, password, name }, {
229
+ onSuccess: (context) => {
230
+ const token = context.response.headers.get("set-auth-token");
231
+ if (token) {
232
+ tokenStorage.save(token);
233
+ }
234
+ },
235
+ onError: (context) => {
236
+ authStore.error.set(context.error.message || "Sign up failed");
237
+ },
238
+ });
239
+ authStore.loading.set(false);
240
+ return true;
241
+ }
242
+ catch (error) {
243
+ const message = error instanceof Error ? error.message : "Sign up failed";
244
+ authStore.error.set(message);
245
+ authStore.loading.set(false);
246
+ return false;
247
+ }
248
+ },
249
+ /**
250
+ * 登出
218
251
  */
219
252
  async signOut(redirectTo) {
220
- // 防止重复调用
221
253
  if (authStore.loading.get()) {
222
254
  return;
223
255
  }
@@ -225,11 +257,9 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
225
257
  authStore.loading.set(true);
226
258
  authStore.error.set(null);
227
259
  await authClient.signOut();
228
- // 清理本地状态
229
260
  authStore.user.set(null);
230
261
  authStore.isAuthenticated.set(false);
231
262
  tokenStorage.clear();
232
- // 如果提供了重定向地址,则执行跳转
233
263
  if (redirectTo) {
234
264
  window.location.href = redirectTo;
235
265
  }
@@ -237,7 +267,6 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
237
267
  catch (error) {
238
268
  const message = error instanceof Error ? error.message : "Sign out failed";
239
269
  authStore.error.set(message);
240
- // 即使出错,也尝试清理本地状态
241
270
  authStore.user.set(null);
242
271
  authStore.isAuthenticated.set(false);
243
272
  tokenStorage.clear();
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Store 导出
3
+ */
4
+ export { createAuthStore, createTokenStorage } from "./state";
5
+ export { createAuthActions, type AuthActions } from "./actions";
6
+ export { createAuthComputed } from "./computed";
7
+ export { getCurrentUser, getCurrentUserId, isAuthenticated, isLoading, isLoaded, waitForAuth, getAuthToken, getAuthTokenSync, getAuthHeaders, getAuthHeadersSync, handle401Response, createUserManager, createBoundAuthUtils, } from "./utils";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAC7D,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,SAAS,CAAA"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Store 导出
3
+ */
4
+ export { createAuthStore, createTokenStorage } from "./state";
5
+ export { createAuthActions } from "./actions";
6
+ export { createAuthComputed } from "./computed";
7
+ export { getCurrentUser, getCurrentUserId, isAuthenticated, isLoading, isLoaded, waitForAuth, getAuthToken, getAuthTokenSync, getAuthHeaders, getAuthHeadersSync, handle401Response, createUserManager, createBoundAuthUtils, } from "./utils";
@@ -1,16 +1,19 @@
1
+ /**
2
+ * 认证状态 Store
3
+ */
1
4
  import type { AuthState } from "../types";
5
+ import type { TokenStorage } from "../api";
6
+ /**
7
+ * 创建 Token 存储工具
8
+ */
9
+ export declare function createTokenStorage(tokenStorageKey: string): TokenStorage;
2
10
  /**
3
- * 创建认证状态 store
11
+ * 创建认证状态 Store
4
12
  */
5
13
  export declare function createAuthStore(config: {
6
14
  tokenStorageKey: string;
7
15
  }): {
8
16
  authStore: import("@legendapp/state").Observable<AuthState>;
9
- tokenStorage: {
10
- save(token: string | null): void;
11
- get(): string | null;
12
- clear(): void;
13
- };
17
+ tokenStorage: TokenStorage;
14
18
  };
15
- export type TokenStorage = ReturnType<typeof createAuthStore>["tokenStorage"];
16
19
  //# sourceMappingURL=state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/store/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzC;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IAAE,eAAe,EAAE,MAAM,CAAA;CAAE;;;oBAyBnD,MAAM,GAAG,IAAI;eAclB,MAAM,GAAG,IAAI;;;EAavB;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,cAAc,CAAC,CAAA"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/store/state.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAE1C;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,MAAM,GAAG,YAAY,CA4BxE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IAAE,eAAe,EAAE,MAAM,CAAA;CAAE;;;EA2BlE"}
@@ -1,53 +1,61 @@
1
+ /**
2
+ * 认证状态 Store
3
+ */
1
4
  import { observable } from "@legendapp/state";
2
5
  /**
3
- * 创建认证状态 store
6
+ * 创建 Token 存储工具
4
7
  */
5
- export function createAuthStore(config) {
6
- const { tokenStorageKey } = config;
7
- // 从 localStorage 读取初始 token
8
+ export function createTokenStorage(tokenStorageKey) {
8
9
  const getStoredToken = () => {
9
10
  try {
10
- const stored = localStorage.getItem(tokenStorageKey);
11
- if (!stored)
12
- return null;
13
- return stored;
11
+ return localStorage.getItem(tokenStorageKey);
14
12
  }
15
13
  catch {
16
14
  return null;
17
15
  }
18
16
  };
19
- const authStore = observable({
20
- user: null,
21
- isAuthenticated: false,
22
- isLoaded: false,
23
- loading: true,
24
- error: null,
25
- token: getStoredToken(),
26
- });
27
- // 手动管理 token 的 localStorage 存储
28
- const tokenStorage = {
17
+ return {
18
+ get: getStoredToken,
29
19
  save(token) {
30
20
  try {
31
21
  if (token) {
32
22
  localStorage.setItem(tokenStorageKey, token);
33
- authStore.token.set(token);
34
23
  }
35
24
  else {
36
25
  localStorage.removeItem(tokenStorageKey);
37
- authStore.token.set(null);
38
26
  }
39
27
  }
40
28
  catch (error) {
41
29
  console.error("Failed to save token to localStorage:", error);
42
30
  }
43
31
  },
44
- get() {
45
- return getStoredToken();
46
- },
47
32
  clear() {
48
33
  this.save(null);
49
34
  },
50
35
  };
36
+ }
37
+ /**
38
+ * 创建认证状态 Store
39
+ */
40
+ export function createAuthStore(config) {
41
+ const { tokenStorageKey } = config;
42
+ // 创建 token 存储工具
43
+ const tokenStorage = createTokenStorage(tokenStorageKey);
44
+ // 创建 observable store
45
+ const authStore = observable({
46
+ user: null,
47
+ isAuthenticated: false,
48
+ isLoaded: false,
49
+ loading: true,
50
+ error: null,
51
+ token: tokenStorage.get(),
52
+ });
53
+ // 同步 token 到 store
54
+ const originalSave = tokenStorage.save.bind(tokenStorage);
55
+ tokenStorage.save = (token) => {
56
+ originalSave(token);
57
+ authStore.token.set(token);
58
+ };
51
59
  return {
52
60
  authStore,
53
61
  tokenStorage,
@@ -1,103 +1,54 @@
1
+ /**
2
+ * Store 工具函数
3
+ */
1
4
  import type { Observable } from "@legendapp/state";
2
5
  import type { AuthState, SessionUser } from "../types";
3
- import type { TokenStorage } from "./state";
6
+ import type { ApiClient, TokenStorage } from "../api";
4
7
  import type { AuthActions } from "./actions";
5
- /**
6
- * 获取当前用户
7
- */
8
8
  export declare function getCurrentUser(authStore: Observable<AuthState>): SessionUser | null;
9
- /**
10
- * 获取当前用户ID
11
- */
12
9
  export declare function getCurrentUserId(authStore: Observable<AuthState>): string | null;
13
- /**
14
- * 安全获取当前用户ID
15
- */
16
- export declare function getCurrentUserIdSafe(authStore: Observable<AuthState>): string | null;
17
- /**
18
- * 检查是否已认证
19
- */
20
10
  export declare function isAuthenticated(authStore: Observable<AuthState>): boolean;
21
- /**
22
- * 检查是否正在加载
23
- */
24
11
  export declare function isLoading(authStore: Observable<AuthState>): boolean;
25
- /**
26
- * 检查是否已加载
27
- */
28
12
  export declare function isLoaded(authStore: Observable<AuthState>): boolean;
29
- /**
30
- * 等待认证完成
31
- * 使用轮询方式检查认证状态(简单但有效)
32
- *
33
- * 注意:如果长时间未加载,Promise 不会自动 reject
34
- * 建议在外层添加超时处理
35
- */
36
13
  export declare function waitForAuth(authStore: Observable<AuthState>): Promise<void>;
37
- /**
38
- * 获取认证令牌
39
- */
40
- export declare function getAuthToken(authStore: Observable<AuthState>, tokenStorage: TokenStorage, authActions: AuthActions, authClient: {
41
- getSession: () => Promise<unknown>;
42
- }): Promise<string | null>;
43
- /**
44
- * 获取认证头
45
- */
46
- export declare function getAuthHeaders(authStore: Observable<AuthState>, tokenStorage: TokenStorage, authActions: AuthActions, authClient: {
47
- getSession: () => Promise<unknown>;
48
- }): Promise<Record<string, string>>;
49
- /**
50
- * 处理 401 响应
51
- */
14
+ export declare function getAuthTokenSync(tokenStorage: TokenStorage): string | null;
15
+ export declare function getAuthToken(tokenStorage: TokenStorage): Promise<string | null>;
16
+ export declare function getAuthHeadersSync(tokenStorage: TokenStorage): Record<string, string>;
17
+ export declare function getAuthHeaders(tokenStorage: TokenStorage): Promise<Record<string, string>>;
52
18
  export declare function handle401Response(response: Response, authActions: AuthActions): Response;
53
- /**
54
- * API 客户端工具
55
- */
56
- export declare function createApiClient(authStore: Observable<AuthState>, tokenStorage: TokenStorage, authActions: AuthActions, authClient: {
57
- getSession: () => Promise<unknown>;
58
- }): {
59
- get(url: string, options?: RequestInit): Promise<Response>;
60
- post(url: string, body?: unknown, options?: RequestInit): Promise<Response>;
61
- put(url: string, body?: unknown, options?: RequestInit): Promise<Response>;
62
- delete(url: string, options?: RequestInit): Promise<Response>;
63
- };
64
- /**
65
- * 用户管理器
66
- */
67
19
  export declare function createUserManager(authStore: Observable<AuthState>): {
68
- getUser(): SessionUser | null;
69
- getUserId(): string | null;
20
+ getUser: () => SessionUser | null;
21
+ getUserId: () => string | null;
70
22
  };
71
23
  /**
72
- * 从 AuthInstance 创建绑定好的工具函数集合
73
- * 这样就不需要在每个项目中重复包装工具函数
24
+ * 创建绑定好的工具函数集合
74
25
  */
75
26
  export declare function createBoundAuthUtils(instance: {
27
+ apiClient: ApiClient;
28
+ authActions: AuthActions;
76
29
  authStore: Observable<AuthState>;
77
30
  tokenStorage: TokenStorage;
78
- authActions: AuthActions;
79
- authClient: {
80
- getSession: () => Promise<unknown>;
81
- };
82
31
  }): {
83
32
  getCurrentUser: () => SessionUser | null;
84
33
  getCurrentUserId: () => string | null;
85
- getCurrentUserIdSafe: () => string | null;
86
34
  isAuthenticated: () => boolean;
87
35
  isLoading: () => boolean;
88
36
  isLoaded: () => boolean;
89
37
  waitForAuth: () => Promise<void>;
90
38
  getAuthToken: () => Promise<string | null>;
39
+ getAuthTokenSync: () => string | null;
91
40
  getAuthHeaders: () => Promise<Record<string, string>>;
41
+ getAuthHeadersSync: () => Record<string, string>;
92
42
  apiClient: {
93
- get(url: string, options?: RequestInit): Promise<Response>;
94
- post(url: string, body?: unknown, options?: RequestInit): Promise<Response>;
95
- put(url: string, body?: unknown, options?: RequestInit): Promise<Response>;
96
- delete(url: string, options?: RequestInit): Promise<Response>;
43
+ get<T = unknown>(path: string, options?: RequestInit): Promise<import("..").ApiResponse<T>>;
44
+ post<T = unknown>(path: string, body?: unknown, options?: RequestInit): Promise<import("..").ApiResponse<T>>;
45
+ put<T = unknown>(path: string, body?: unknown, options?: RequestInit): Promise<import("..").ApiResponse<T>>;
46
+ delete<T = unknown>(path: string, options?: RequestInit): Promise<import("..").ApiResponse<T>>;
47
+ fetch(path: string, options?: RequestInit): Promise<Response>;
97
48
  };
98
49
  userManager: {
99
- getUser(): SessionUser | null;
100
- getUserId(): string | null;
50
+ getUser: () => SessionUser | null;
51
+ getUserId: () => string | null;
101
52
  };
102
53
  };
103
54
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/store/utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/store/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE5C,wBAAgB,cAAc,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,WAAW,GAAG,IAAI,CAEnF;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,MAAM,GAAG,IAAI,CAEhF;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,OAAO,CAEzE;AAED,wBAAgB,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,OAAO,CAEnE;AAED,wBAAgB,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,OAAO,CAElE;AAED,wBAAsB,WAAW,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAajF;AAED,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAE1E;AAED,wBAAsB,YAAY,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAErF;AAED,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIrF;AAED,wBAAsB,cAAc,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAEhG;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,GAAG,QAAQ,CAKxF;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC;;;EAKjE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE;IAC7C,SAAS,EAAE,SAAS,CAAA;IACpB,WAAW,EAAE,WAAW,CAAA;IACxB,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;IAChC,YAAY,EAAE,YAAY,CAAA;CAC3B;;;;;;;;;;;;;;;;;;;;;;EAiBA"}