@amaster.ai/auth-client 1.0.0-beta.6 → 1.1.0-beta.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.
package/README.md CHANGED
@@ -8,7 +8,7 @@ Authentication SDK for Amaster Platform - Complete client-side authentication so
8
8
  - 🔄 **Automatic Token Refresh**: JWT token auto-refresh before expiration
9
9
  - 📦 **Token Storage**: localStorage or sessionStorage with SSR support
10
10
  - 🎯 **Permission System**: Role-based and permission-based access control
11
- - 🔗 **OAuth Integration**: Google, GitHub, WeChat, and custom OAuth providers
11
+ - 🔗 **OAuth Integration**: Google, GitHub, WeChat OAuth, WeChat Mini Program, and custom OAuth providers
12
12
  - 📡 **Event System**: Listen to login, logout, and token events
13
13
  - 💾 **Session Management**: View and revoke active sessions
14
14
  - 🔒 **Type-Safe**: Full TypeScript support
@@ -65,22 +65,48 @@ if (authClient.hasPermission("user.read")) {
65
65
 
66
66
  ## Configuration
67
67
 
68
+ ### Zero Configuration (Recommended)
69
+
70
+ The SDK works out of the box with **zero configuration**:
71
+
72
+ ```typescript
73
+ const authClient = createAuthClient();
74
+
75
+ // Storage auto-detects environment:
76
+ // - Browser → localStorage
77
+ // - WeChat Mini Program → wx.setStorageSync
78
+ // - SSR/Node.js → no-op (no persistence)
79
+ ```
80
+
81
+ ### Optional Configuration
82
+
68
83
  ```typescript
69
84
  interface AuthClientOptions {
70
- baseURL?: string; // API base URL, defaults to window.location.origin
71
- storage?: "localStorage" | "sessionStorage"; // Token storage type, defaults to localStorage
72
- onTokenExpired?: () => void; // Token expiration callback
73
- onUnauthorized?: () => void; // Unauthorized (401) callback
85
+ baseURL?: string; // API base URL, defaults to window.location.origin
86
+ headers?: Record<string, string>; // Custom headers for all requests
87
+ onTokenExpired?: () => void; // Token expiration callback
88
+ onUnauthorized?: () => void; // Unauthorized (401) callback
74
89
  }
75
90
  ```
76
91
 
92
+ **Example:**
93
+
94
+ ```typescript
95
+ const authClient = createAuthClient({
96
+ baseURL: "https://api.example.com",
97
+ onTokenExpired: () => window.location.href = "/login",
98
+ onUnauthorized: () => alert("Session expired"),
99
+ });
100
+ ```
101
+
77
102
  **Built-in Features (Zero Config):**
78
103
 
104
+ - ✅ **Auto Environment Detection**: Works in browser, WeChat Mini Program, SSR
79
105
  - ✅ **Auto Token Refresh**: Refreshes 5 minutes before expiry
80
106
  - ✅ **Auto Permission Sync**: Syncs on page load and every token refresh
107
+ - ✅ **Smart Storage**: localStorage (browser), wx.storage (WeChat), no-op (SSR)
81
108
  - ✅ **Lightweight Data**: Only permission names cached, not full objects
82
109
  - ✅ **On-Demand DataScope**: Loads only when needed
83
- - ✅ **SSR Compatible**: Works in both browser and server environments
84
110
 
85
111
  ## Authentication
86
112
 
@@ -178,6 +204,177 @@ if (window.opener) {
178
204
  }
179
205
  ```
180
206
 
207
+ ### WeChat Mini Program Login
208
+
209
+ The SDK automatically detects WeChat Mini Program environment and uses `wx.storage`:
210
+
211
+ ```typescript
212
+ import { createAuthClient } from "@amaster.ai/auth-client";
213
+
214
+ // Zero configuration - auto-detects WeChat environment
215
+ const authClient = createAuthClient({
216
+ baseURL: "https://api.yourdomain.com"
217
+ });
218
+
219
+ // Login with WeChat code
220
+ wx.login({
221
+ success: async (res) => {
222
+ if (res.code) {
223
+ const result = await authClient.loginWithMiniProgram(res.code);
224
+
225
+ if (result.data) {
226
+ console.log("Logged in:", result.data.user);
227
+ // Token automatically saved to wx.storage
228
+ wx.switchTab({ url: '/pages/index/index' });
229
+ } else if (result.error) {
230
+ wx.showToast({
231
+ title: result.error.message || 'Login failed',
232
+ icon: 'none'
233
+ });
234
+ }
235
+ }
236
+ },
237
+ fail: (err) => {
238
+ console.error("wx.login failed:", err);
239
+ }
240
+ });
241
+ ```
242
+
243
+ **Get user phone number (optional):**
244
+
245
+ ```xml
246
+ <!-- WXML -->
247
+ <button
248
+ open-type="getPhoneNumber"
249
+ bindgetphonenumber="onGetPhoneNumber"
250
+ >
251
+ Get Phone Number
252
+ </button>
253
+ ```
254
+
255
+ ```typescript
256
+ // JS
257
+ async onGetPhoneNumber(e) {
258
+ const { code } = e.detail;
259
+
260
+ if (code) {
261
+ const result = await authClient.getMiniProgramPhoneNumber(code);
262
+
263
+ if (result.data) {
264
+ console.log("Phone number:", result.data.phone);
265
+ console.log("Verified:", result.data.phoneVerified);
266
+
267
+ // Update UI with phone number
268
+ this.setData({
269
+ phone: result.data.phone
270
+ });
271
+ } else if (result.error) {
272
+ wx.showToast({
273
+ title: result.error.message || 'Failed to get phone number',
274
+ icon: 'none'
275
+ });
276
+ }
277
+ } else {
278
+ // User denied authorization
279
+ console.log("User cancelled phone number authorization");
280
+ }
281
+ }
282
+ ```
283
+
284
+ **Complete Mini Program example:**
285
+
286
+ ```typescript
287
+ // pages/login/login.js
288
+ import { createAuthClient } from "@amaster.ai/auth-client";
289
+
290
+ const authClient = createAuthClient({
291
+ baseURL: "https://api.yourdomain.com"
292
+ });
293
+
294
+ Page({
295
+ data: {
296
+ userInfo: null,
297
+ hasPhone: false
298
+ },
299
+
300
+ // Auto-login on page load
301
+ onLoad() {
302
+ this.handleLogin();
303
+ },
304
+
305
+ // WeChat Mini Program login
306
+ async handleLogin() {
307
+ wx.showLoading({ title: 'Logging in...' });
308
+
309
+ wx.login({
310
+ success: async (res) => {
311
+ if (res.code) {
312
+ const result = await authClient.loginWithMiniProgram(res.code);
313
+
314
+ wx.hideLoading();
315
+
316
+ if (result.data) {
317
+ this.setData({
318
+ userInfo: result.data.user
319
+ });
320
+
321
+ // Navigate to home
322
+ wx.switchTab({ url: '/pages/index/index' });
323
+ } else {
324
+ wx.showToast({
325
+ title: 'Login failed',
326
+ icon: 'none'
327
+ });
328
+ }
329
+ }
330
+ },
331
+ fail: () => {
332
+ wx.hideLoading();
333
+ wx.showToast({
334
+ title: 'Login failed',
335
+ icon: 'none'
336
+ });
337
+ }
338
+ });
339
+ },
340
+
341
+ // Get phone number with user authorization
342
+ async onGetPhoneNumber(e) {
343
+ const { code } = e.detail;
344
+
345
+ if (!code) {
346
+ wx.showToast({
347
+ title: 'Authorization cancelled',
348
+ icon: 'none'
349
+ });
350
+ return;
351
+ }
352
+
353
+ wx.showLoading({ title: 'Getting phone...' });
354
+
355
+ const result = await authClient.getMiniProgramPhoneNumber(code);
356
+
357
+ wx.hideLoading();
358
+
359
+ if (result.data) {
360
+ this.setData({
361
+ hasPhone: true
362
+ });
363
+
364
+ wx.showToast({
365
+ title: 'Phone number obtained',
366
+ icon: 'success'
367
+ });
368
+ } else {
369
+ wx.showToast({
370
+ title: result.error?.message || 'Failed to get phone',
371
+ icon: 'none'
372
+ });
373
+ }
374
+ }
375
+ });
376
+ ```
377
+
181
378
  ### Logout
182
379
 
183
380
  ```typescript
package/dist/auth.d.cts CHANGED
@@ -14,7 +14,7 @@
14
14
  * ============================================================================
15
15
  */
16
16
  import { HttpClient, ClientResult } from '@amaster.ai/http-client';
17
- import { s as User, k as RegisterParams, f as LoginResponse, L as LoginParams, d as CodeLoginParams, S as SendCodeParams, r as SuccessResponse, b as CaptchaResponse, h as OAuthProvider, R as RefreshTokenResponse } from './types-Bgi_Lwkp.cjs';
17
+ import { q as User, i as RegisterParams, e as LoginResponse, L as LoginParams, c as CodeLoginParams, S as SendCodeParams, p as SuccessResponse, b as CaptchaResponse, g as OAuthProvider, M as MiniProgramPhoneResponse, R as RefreshTokenResponse } from './types-Be3I8qJG.cjs';
18
18
 
19
19
  /**
20
20
  * Authentication Module
@@ -116,6 +116,51 @@ declare function createAuthModule(deps: AuthModuleDeps): {
116
116
  * @category Authentication
117
117
  */
118
118
  handleOAuthCallback(): Promise<ClientResult<LoginResponse>>;
119
+ /**
120
+ * Login with WeChat Mini Program code
121
+ *
122
+ * @category Authentication
123
+ * @example
124
+ * ```typescript
125
+ * // In WeChat Mini Program
126
+ * wx.login({
127
+ * success: async (res) => {
128
+ * if (res.code) {
129
+ * const result = await auth.loginWithMiniProgram(res.code);
130
+ * if (result.data) {
131
+ * console.log("Logged in:", result.data.user);
132
+ * }
133
+ * }
134
+ * }
135
+ * });
136
+ * ```
137
+ */
138
+ loginWithMiniProgram(code: string): Promise<ClientResult<LoginResponse>>;
139
+ /**
140
+ * Get WeChat Mini Program user phone number
141
+ * Requires user authorization via getPhoneNumber button
142
+ *
143
+ * @category Authentication
144
+ * @example
145
+ * ```typescript
146
+ * // WXML
147
+ * <button open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber">
148
+ * Get Phone Number
149
+ * </button>
150
+ *
151
+ * // JS
152
+ * async onGetPhoneNumber(e) {
153
+ * const { code } = e.detail;
154
+ * if (code) {
155
+ * const result = await auth.getMiniProgramPhoneNumber(code);
156
+ * if (result.data) {
157
+ * console.log("Phone:", result.data.phone);
158
+ * }
159
+ * }
160
+ * }
161
+ * ```
162
+ */
163
+ getMiniProgramPhoneNumber(code: string): Promise<ClientResult<MiniProgramPhoneResponse>>;
119
164
  /**
120
165
  * Logout current user
121
166
  *
package/dist/auth.d.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  * ============================================================================
15
15
  */
16
16
  import { HttpClient, ClientResult } from '@amaster.ai/http-client';
17
- import { s as User, k as RegisterParams, f as LoginResponse, L as LoginParams, d as CodeLoginParams, S as SendCodeParams, r as SuccessResponse, b as CaptchaResponse, h as OAuthProvider, R as RefreshTokenResponse } from './types-Bgi_Lwkp.js';
17
+ import { q as User, i as RegisterParams, e as LoginResponse, L as LoginParams, c as CodeLoginParams, S as SendCodeParams, p as SuccessResponse, b as CaptchaResponse, g as OAuthProvider, M as MiniProgramPhoneResponse, R as RefreshTokenResponse } from './types-Be3I8qJG.js';
18
18
 
19
19
  /**
20
20
  * Authentication Module
@@ -116,6 +116,51 @@ declare function createAuthModule(deps: AuthModuleDeps): {
116
116
  * @category Authentication
117
117
  */
118
118
  handleOAuthCallback(): Promise<ClientResult<LoginResponse>>;
119
+ /**
120
+ * Login with WeChat Mini Program code
121
+ *
122
+ * @category Authentication
123
+ * @example
124
+ * ```typescript
125
+ * // In WeChat Mini Program
126
+ * wx.login({
127
+ * success: async (res) => {
128
+ * if (res.code) {
129
+ * const result = await auth.loginWithMiniProgram(res.code);
130
+ * if (result.data) {
131
+ * console.log("Logged in:", result.data.user);
132
+ * }
133
+ * }
134
+ * }
135
+ * });
136
+ * ```
137
+ */
138
+ loginWithMiniProgram(code: string): Promise<ClientResult<LoginResponse>>;
139
+ /**
140
+ * Get WeChat Mini Program user phone number
141
+ * Requires user authorization via getPhoneNumber button
142
+ *
143
+ * @category Authentication
144
+ * @example
145
+ * ```typescript
146
+ * // WXML
147
+ * <button open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber">
148
+ * Get Phone Number
149
+ * </button>
150
+ *
151
+ * // JS
152
+ * async onGetPhoneNumber(e) {
153
+ * const { code } = e.detail;
154
+ * if (code) {
155
+ * const result = await auth.getMiniProgramPhoneNumber(code);
156
+ * if (result.data) {
157
+ * console.log("Phone:", result.data.phone);
158
+ * }
159
+ * }
160
+ * }
161
+ * ```
162
+ */
163
+ getMiniProgramPhoneNumber(code: string): Promise<ClientResult<MiniProgramPhoneResponse>>;
119
164
  /**
120
165
  * Logout current user
121
166
  *
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var httpClient=require('@amaster.ai/http-client');var g=class{constructor(){this.events={};}on(e,n){this.events[e]||(this.events[e]=[]),this.events[e].push(n);}off(e,n){this.events[e]&&(this.events[e]=this.events[e].filter(s=>s!==n));}emit(e,...n){this.events[e]&&this.events[e].forEach(s=>{try{s(...n);}catch(r){console.error(`[AuthClient] Error in event handler for "${e}":`,r);}});}removeAllListeners(){this.events={};}};var l={ACCESS_TOKEN:"amaster_access_token",REFRESH_TOKEN:"amaster_refresh_token",USER:"amaster_user"};function R(i="localStorage"){if(!(typeof window<"u"&&typeof window[i]<"u"))return b();let n=window[i==="sessionStorage"?"sessionStorage":"localStorage"];return {getItem(s){try{return n.getItem(s)}catch(r){return console.error(`[AuthClient] Failed to get item from ${i}:`,r),null}},setItem(s,r){try{n.setItem(s,r);}catch(t){console.error(`[AuthClient] Failed to set item in ${i}:`,t);}},removeItem(s){try{n.removeItem(s);}catch(r){console.error(`[AuthClient] Failed to remove item from ${i}:`,r);}},clear(){try{n.removeItem(l.ACCESS_TOKEN),n.removeItem(l.REFRESH_TOKEN),n.removeItem(l.USER);}catch(s){console.error(`[AuthClient] Failed to clear ${i}:`,s);}}}}function b(){return {getItem(){return null},setItem(){},removeItem(){},clear(){}}}function _(i){try{let e=i.split(".");if(e.length!==3)return null;let s=(e[1]||"").replace(/-/g,"+").replace(/_/g,"/");if(typeof atob<"u"){let r=decodeURIComponent(atob(s).split("").map(t=>"%"+("00"+t.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(r)}if(typeof Buffer<"u"){let r=Buffer.from(s,"base64").toString("utf-8");return JSON.parse(r)}return null}catch(e){return console.error("[AuthClient] Failed to parse JWT token:",e),null}}var f=class{constructor(){this.refreshTimer=null;this.isRefreshing=false;this.refreshCallback=null;}setRefreshCallback(e){this.refreshCallback=e;}scheduleRefresh(e){this.clearSchedule(),!(e<=0)&&(this.refreshTimer=setTimeout(()=>{this.refresh();},e));}scheduleRefreshFromToken(e,n=300){let s=_(e);if(!s||!s.exp){console.warn("[AuthClient] Cannot schedule refresh: invalid token or missing exp claim");return}let r=s.exp*1e3,t=Date.now(),u=r-t-n*1e3;u<=0?(console.warn("[AuthClient] Token already expired or expiring soon, refreshing immediately"),this.refresh()):this.scheduleRefresh(u);}async refresh(){if(!this.isRefreshing){if(!this.refreshCallback){console.error("[AuthClient] No refresh callback set");return}this.isRefreshing=true;try{await this.refreshCallback();}catch(e){console.error("[AuthClient] Token refresh failed:",e);}finally{this.isRefreshing=false;}}}clearSchedule(){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null);}isCurrentlyRefreshing(){return this.isRefreshing}destroy(){this.clearSchedule(),this.refreshCallback=null,this.isRefreshing=false;}};function C(i){let{http:e,onLoginSuccess:n,storage:s,clearAuth:r}=i;return {async register(t){let o=await e.request({url:"/api/auth/register",method:"post",headers:{"Content-Type":"application/json"},data:t});return o.data?.user&&o.data?.accessToken&&n(o.data.user,o.data.accessToken),o},async login(t){let o=await e.request({url:"/api/auth/login",method:"post",headers:{"Content-Type":"application/json"},data:t});return o.data?.user&&o.data?.accessToken&&n(o.data.user,o.data.accessToken),o},async loginWithCode(t){let o=await e.request({url:"/api/auth/login-with-code",method:"post",headers:{"Content-Type":"application/json"},data:t});return o.data?.user&&o.data?.accessToken&&n(o.data.user,o.data.accessToken),o},async sendCode(t){return e.request({url:"/api/auth/send-code",method:"post",headers:{"Content-Type":"application/json"},data:t})},async getCaptcha(){return e.request({url:"/api/auth/captcha",method:"get"})},loginWithOAuth(t,o){if(typeof window>"u"){console.error("[AuthClient] OAuth login is only available in browser environment");return}let u=o?`/api/auth/oauth/${t}?redirect_url=${encodeURIComponent(o)}`:`/api/auth/oauth/${t}`;window.location.href=u;},async handleOAuthCallback(){if(typeof window>"u")return {data:null,error:{message:"OAuth callback is only available in browser environment",status:400},status:400};try{let t=window.location.hash.substring(1),o=new URLSearchParams(t),u=o.get("access_token"),c=o.get("user");if(!u||!c)return {data:null,error:{message:"OAuth callback failed: missing token or user data",status:400},status:400};let d=JSON.parse(decodeURIComponent(c));return n(d,u),{data:{user:d,accessToken:u},error:null,status:200}}catch(t){return {data:null,error:{message:`OAuth callback failed: ${t instanceof Error?t.message:String(t)}`,status:400},status:400}}},async logout(){let t=s.getItem("amaster_access_token"),o=await e.request({url:"/api/auth/logout",method:"post",headers:t?{Authorization:`Bearer ${t}`}:void 0});return r(),o},async refreshToken(){return e.request({url:"/api/auth/refresh",method:"post"})}}}function A(i){let{http:e,getCurrentUser:n,storage:s}=i;return {hasRole(r){let t=n();return !t||!t.roles?false:t.roles.includes(r)},hasPermission(r){let t=n();return !t||!t.permissions?false:t.permissions.includes(r)},hasAnyPermission(r){return r.some(t=>this.hasPermission(t))},hasAllPermissions(r){return r.every(t=>this.hasPermission(t))},async getPermissionScope(r){let t=s.getItem("amaster_access_token");return t?r?e.request({url:`/api/auth/permissions/${r}/scope`,method:"get",headers:{Authorization:`Bearer ${t}`}}):{data:null,error:{message:"Permission name is required",status:400},status:400}:{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function S(i){let{http:e,storage:n,onUserUpdate:s}=i;return {async getMe(){let r=n.getItem("amaster_access_token");if(!r)return {data:null,error:{message:"Not authenticated",status:401},status:401};let t=await e.request({url:"/api/auth/me",method:"get",headers:{Authorization:`Bearer ${r}`}});return t.data&&s(t.data),t},async updateMe(r){let t=n.getItem("amaster_access_token");if(!t)return {data:null,error:{message:"Not authenticated",status:401},status:401};let o=await e.request({url:"/api/auth/me",method:"put",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},data:r});return o.data&&s(o.data),o},async changePassword(r){let t=n.getItem("amaster_access_token");return t?e.request({url:"/api/auth/change-password",method:"post",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},data:r}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function v(i){let{http:e,storage:n}=i;return {async getOAuthBindings(){let s=n.getItem("amaster_access_token");return s?e.request({url:"/api/auth/oauth-bindings",method:"get",headers:{Authorization:`Bearer ${s}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},bindOAuth(s){if(typeof window>"u"){console.error("[AuthClient] OAuth binding is only available in browser environment");return}window.location.href=`/api/auth/oauth/${s}/bind`;},async unbindOAuth(s){let r=n.getItem("amaster_access_token");return r?e.request({url:`/api/auth/oauth/${s}/unbind`,method:"delete",headers:{Authorization:`Bearer ${r}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function k(i){let{http:e,storage:n}=i;return {async getSession(){let s=n.getItem("amaster_access_token");return s?e.request({url:"/api/auth/sessions/current",method:"get",headers:{Authorization:`Bearer ${s}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async getSessions(){let s=n.getItem("amaster_access_token");return s?e.request({url:"/api/auth/sessions",method:"get",headers:{Authorization:`Bearer ${s}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async revokeSession(s){let r=n.getItem("amaster_access_token");return r?s?e.request({url:`/api/auth/sessions/${s}`,method:"delete",headers:{Authorization:`Bearer ${r}`}}):{data:null,error:{message:"Session ID is required",status:400},status:400}:{data:null,error:{message:"Not authenticated",status:401},status:401}},async revokeAllSessions(){let s=n.getItem("amaster_access_token");return s?e.request({url:"/api/auth/sessions",method:"delete",headers:{Authorization:`Bearer ${s}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function H(i={},e=httpClient.createHttpClient()){let {storage:n="localStorage",onTokenExpired:s,onUnauthorized:r}=i,o=300,u=R(n),c=new g,d=new f,m=null;try{let a=u.getItem(l.USER);a&&(m=JSON.parse(a));}catch(a){console.error("[AuthClient] Failed to load user from storage:",a);}let h;d.setRefreshCallback(async()=>{let a=await h.refreshToken();a.data?(await h.getMe(),c.emit("tokenRefreshed",a.data.accessToken)):(c.emit("tokenExpired"),s?.());});function P(a,p){u.setItem(l.ACCESS_TOKEN,p),u.setItem(l.USER,JSON.stringify(a)),m=a,d.scheduleRefreshFromToken(p,o),c.emit("login",a);}function T(a){m=a,u.setItem(l.USER,JSON.stringify(a));}function y(){u.clear(),m=null,d.clearSchedule();}function E(){return m}let M=C({http:e,onLoginSuccess:P,storage:u,clearAuth:y}),U=A({http:e,getCurrentUser:E,storage:u}),w=S({http:e,storage:u,onUserUpdate:T}),O=v({http:e,storage:u}),I=k({http:e,storage:u});return h={...M,...U,...w,...O,...I,on(a,p){c.on(a,p);},off(a,p){c.off(a,p);},isAuthenticated(){return !!u.getItem(l.ACCESS_TOKEN)},getAccessToken(){return u.getItem(l.ACCESS_TOKEN)},setAccessToken(a){u.setItem(l.ACCESS_TOKEN,a),d.scheduleRefreshFromToken(a,o);},clearAuth:y},h.on("unauthorized",()=>{r?.();}),h.isAuthenticated()&&h.getMe().catch(a=>{console.warn("[AuthClient] Failed to sync user info on init:",a);}),h}exports.createAuthClient=H;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';var httpClient=require('@amaster.ai/http-client');var y=class{constructor(){this.events={};}on(e,r){this.events[e]||(this.events[e]=[]),this.events[e].push(r);}off(e,r){this.events[e]&&(this.events[e]=this.events[e].filter(t=>t!==r));}emit(e,...r){this.events[e]&&this.events[e].forEach(t=>{try{t(...r);}catch(a){console.error(`[AuthClient] Error in event handler for "${e}":`,a);}});}removeAllListeners(){this.events={};}};var l={ACCESS_TOKEN:"amaster_access_token",REFRESH_TOKEN:"amaster_refresh_token",USER:"amaster_user"};function N(){return typeof wx<"u"&&wx.getStorageSync?"wechat-miniprogram":typeof window<"u"&&typeof window.localStorage<"u"?"browser":"node"}function S(){switch(N()){case "wechat-miniprogram":return $();case "browser":return L();case "node":return q()}}function L(){let s=window.localStorage;return {getItem(e){try{return s.getItem(e)}catch(r){return console.error("[AuthClient] Failed to get item from localStorage:",r),null}},setItem(e,r){try{s.setItem(e,r);}catch(t){console.error("[AuthClient] Failed to set item in localStorage:",t);}},removeItem(e){try{s.removeItem(e);}catch(r){console.error("[AuthClient] Failed to remove item from localStorage:",r);}},clear(){try{s.removeItem(l.ACCESS_TOKEN),s.removeItem(l.REFRESH_TOKEN),s.removeItem(l.USER);}catch(e){console.error("[AuthClient] Failed to clear localStorage:",e);}}}}function $(){return {getItem(s){try{return wx.getStorageSync(s)||null}catch(e){return console.error("[AuthClient] Failed to get item from WeChat storage:",e),null}},setItem(s,e){try{wx.setStorageSync(s,e);}catch(r){console.error("[AuthClient] Failed to set item in WeChat storage:",r);}},removeItem(s){try{wx.removeStorageSync(s);}catch(e){console.error("[AuthClient] Failed to remove item from WeChat storage:",e);}},clear(){try{wx.removeStorageSync(l.ACCESS_TOKEN),wx.removeStorageSync(l.REFRESH_TOKEN),wx.removeStorageSync(l.USER);}catch(s){console.error("[AuthClient] Failed to clear WeChat storage:",s);}}}}function q(){let s=new Map;return {getItem(e){return s.get(e)??null},setItem(e,r){s.set(e,r);},removeItem(e){s.delete(e);},clear(){s.clear();}}}function B(s){try{let e=s.split(".");if(e.length!==3)return null;let t=(e[1]||"").replace(/-/g,"+").replace(/_/g,"/");if(typeof atob<"u"){let a=decodeURIComponent(atob(t).split("").map(o=>"%"+("00"+o.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(a)}if(typeof Buffer<"u"){let a=Buffer.from(t,"base64").toString("utf-8");return JSON.parse(a)}return null}catch(e){return console.error("[AuthClient] Failed to parse JWT token:",e),null}}var C=class{constructor(){this.refreshTimer=null;this.isRefreshing=false;this.refreshCallback=null;}setRefreshCallback(e){this.refreshCallback=e;}scheduleRefresh(e){this.clearSchedule(),!(e<=0)&&(this.refreshTimer=setTimeout(()=>{this.refresh();},e));}scheduleRefreshFromToken(e,r=300){let t=B(e);if(!t||!t.exp){console.warn("[AuthClient] Cannot schedule refresh: invalid token or missing exp claim");return}let a=t.exp*1e3,o=Date.now(),c=a-o-r*1e3;c<=0?(console.warn("[AuthClient] Token already expired or expiring soon, refreshing immediately"),this.refresh()):this.scheduleRefresh(c);}async refresh(){if(!this.isRefreshing){if(!this.refreshCallback){console.error("[AuthClient] No refresh callback set");return}this.isRefreshing=true;try{await this.refreshCallback();}catch(e){console.error("[AuthClient] Token refresh failed:",e);}finally{this.isRefreshing=false;}}}clearSchedule(){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null);}isCurrentlyRefreshing(){return this.isRefreshing}destroy(){this.clearSchedule(),this.refreshCallback=null,this.isRefreshing=false;}};function v(s){return s&&typeof s=="object"&&("statusCode"in s||"status"in s)&&"data"in s?s.data:s}var R={transformResponse:v,logErrors:true};function k(s){let{http:e,onLoginSuccess:r,storage:t,clearAuth:a}=s;return {async register(o){let n=await e.request({url:"/api/auth/register",method:"post",headers:{"Content-Type":"application/json"},data:o});return n.data?.user&&n.data?.accessToken&&r(n.data.user,n.data.accessToken),n},async login(o){let n=await e.request({url:"/api/auth/login",method:"post",headers:{"Content-Type":"application/json"},data:o});return n.data?.user&&n.data?.accessToken&&r(n.data.user,n.data.accessToken),n},async loginWithCode(o){let n=await e.request({url:"/api/auth/login-with-code",method:"post",headers:{"Content-Type":"application/json"},data:o});return n.data?.user&&n.data?.accessToken&&r(n.data.user,n.data.accessToken),n},async sendCode(o){return e.request({url:"/api/auth/send-code",method:"post",headers:{"Content-Type":"application/json"},data:o})},async getCaptcha(){return e.request({url:"/api/auth/captcha",method:"get"})},loginWithOAuth(o,n){if(typeof window>"u"){console.error("[AuthClient] OAuth login is only available in browser environment");return}let c=n?`/api/auth/oauth/${o}?redirect_url=${encodeURIComponent(n)}`:`/api/auth/oauth/${o}`;window.location.href=c;},async handleOAuthCallback(){if(typeof window>"u")return {data:null,error:{message:"OAuth callback is only available in browser environment",status:400},status:400};try{let o=window.location.hash.substring(1),n=new URLSearchParams(o),c=n.get("access_token"),h=n.get("user");if(!c||!h)return {data:null,error:{message:"OAuth callback failed: missing token or user data",status:400},status:400};let u=JSON.parse(decodeURIComponent(h));return r(u,c),{data:{user:u,accessToken:c},error:null,status:200}}catch(o){return {data:null,error:{message:`OAuth callback failed: ${o instanceof Error?o.message:String(o)}`,status:400},status:400}}},async loginWithMiniProgram(o){let n=await e.request({url:"/api/auth/miniprogram/login",method:"post",headers:{"Content-Type":"application/json"},data:{code:o}});return n.data?.user&&n.data?.accessToken&&r(n.data.user,n.data.accessToken),n},async getMiniProgramPhoneNumber(o){let n=t.getItem("amaster_access_token");return n?e.request({url:"/api/auth/miniprogram/phone",method:"post",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},data:{code:o}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async logout(){let o=t.getItem("amaster_access_token"),n=await e.request({url:"/api/auth/logout",method:"post",headers:o?{Authorization:`Bearer ${o}`}:void 0});return a(),n},async refreshToken(){return e.request({url:"/api/auth/refresh",method:"post"})}}}function P(s){let{getCurrentUser:e}=s;return {hasRole(r){let t=e();return !t||!t.roles?false:t.roles.includes(r)},hasPermission(r,t){let a=e();if(!a||!a.permissions)return false;let o=`${r}:${t}`;return a.permissions.includes(o)},hasAnyPermission(r){return r.some(({resource:t,action:a})=>this.hasPermission(t,a))},hasAllPermissions(r){return r.every(({resource:t,action:a})=>this.hasPermission(t,a))}}}function T(s){let{http:e,storage:r,onUserUpdate:t}=s;return {async getMe(){let a=r.getItem("amaster_access_token");if(!a)return {data:null,error:{message:"Not authenticated",status:401},status:401};let o=await e.request({url:"/api/auth/me",method:"get",headers:{Authorization:`Bearer ${a}`}});return o.data&&t(o.data),o},async updateMe(a){let o=r.getItem("amaster_access_token");if(!o)return {data:null,error:{message:"Not authenticated",status:401},status:401};let n=await e.request({url:"/api/auth/me",method:"put",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},data:a});return n.data&&t(n.data),n},async changePassword(a){let o=r.getItem("amaster_access_token");return o?e.request({url:"/api/auth/change-password",method:"post",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},data:a}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function w(s){let{http:e,storage:r}=s;return {async getOAuthBindings(){let t=r.getItem("amaster_access_token");return t?e.request({url:"/api/auth/oauth-bindings",method:"get",headers:{Authorization:`Bearer ${t}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},bindOAuth(t){if(typeof window>"u"){console.error("[AuthClient] OAuth binding is only available in browser environment");return}window.location.href=`/api/auth/oauth/${t}/bind`;},async unbindOAuth(t){let a=r.getItem("amaster_access_token");return a?e.request({url:`/api/auth/oauth/${t}/unbind`,method:"delete",headers:{Authorization:`Bearer ${a}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function E(s){let{http:e,storage:r}=s;return {async getSession(){let t=r.getItem("amaster_access_token");return t?e.request({url:"/api/auth/sessions/current",method:"get",headers:{Authorization:`Bearer ${t}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async getSessions(){let t=r.getItem("amaster_access_token");return t?e.request({url:"/api/auth/sessions",method:"get",headers:{Authorization:`Bearer ${t}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}},async revokeSession(t){let a=r.getItem("amaster_access_token");return a?t?e.request({url:`/api/auth/sessions/${t}`,method:"delete",headers:{Authorization:`Bearer ${a}`}}):{data:null,error:{message:"Session ID is required",status:400},status:400}:{data:null,error:{message:"Not authenticated",status:401},status:401}},async revokeAllSessions(){let t=r.getItem("amaster_access_token");return t?e.request({url:"/api/auth/sessions",method:"delete",headers:{Authorization:`Bearer ${t}`}}):{data:null,error:{message:"Not authenticated",status:401},status:401}}}}function z(s={},e){let {baseURL:r,headers:t,onTokenExpired:a,onUnauthorized:o}=s,n=e||httpClient.createHttpClient({...R,baseURL:r,headers:t}),h=300,u=S(),m=new y,f=new C,g=null;try{let i=u.getItem(l.USER);i&&(g=JSON.parse(i));}catch(i){console.error("[AuthClient] Failed to load user from storage:",i);}let d;f.setRefreshCallback(async()=>{let i=await d.refreshToken();i.data?(await d.getMe(),m.emit("tokenRefreshed",i.data.accessToken)):(m.emit("tokenExpired"),a?.());});function M(i,p){u.setItem(l.ACCESS_TOKEN,p),u.setItem(l.USER,JSON.stringify(i)),g=i,f.scheduleRefreshFromToken(p,h),m.emit("login",i);}function O(i){g=i,u.setItem(l.USER,JSON.stringify(i));}function A(){u.clear(),g=null,f.clearSchedule();}function U(){return g}let b=k({http:n,onLoginSuccess:M,storage:u,clearAuth:A}),x=P({getCurrentUser:U}),I=T({http:n,storage:u,onUserUpdate:O}),_=w({http:n,storage:u}),H=E({http:n,storage:u});return d={...b,...x,...I,..._,...H,on(i,p){m.on(i,p);},off(i,p){m.off(i,p);},isAuthenticated(){return !!u.getItem(l.ACCESS_TOKEN)},getAccessToken(){return u.getItem(l.ACCESS_TOKEN)},setAccessToken(i){u.setItem(l.ACCESS_TOKEN,i),f.scheduleRefreshFromToken(i,h);},clearAuth:A},d.on("unauthorized",()=>{o?.();}),d.isAuthenticated()&&d.getMe().catch(i=>{console.warn("[AuthClient] Failed to sync user info on init:",i);}),d}exports.createAuthClient=z;exports.defaultHttpClientOptions=R;exports.transformAmasterResponse=v;//# sourceMappingURL=index.cjs.map
2
2
  //# sourceMappingURL=index.cjs.map