@scalemule/nextjs 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.
@@ -2109,23 +2109,31 @@ var ScaleMuleServer = class {
2109
2109
  /**
2110
2110
  * Logout user
2111
2111
  */
2112
- logout: async (sessionToken) => {
2112
+ logout: async (sessionToken, options) => {
2113
2113
  return this.request("POST", "/v1/auth/logout", {
2114
- body: { session_token: sessionToken }
2114
+ sessionToken,
2115
+ body: { session_token: sessionToken },
2116
+ onTokenRotated: options?.onTokenRotated
2115
2117
  });
2116
2118
  },
2117
2119
  /**
2118
2120
  * Get current user from session token
2119
2121
  */
2120
- me: async (sessionToken) => {
2121
- return this.request("GET", "/v1/auth/me", { sessionToken });
2122
+ me: async (sessionToken, options) => {
2123
+ return this.request("GET", "/v1/auth/me", {
2124
+ sessionToken,
2125
+ onTokenRotated: options?.onTokenRotated
2126
+ });
2122
2127
  },
2123
2128
  /**
2124
2129
  * Refresh session token
2125
2130
  */
2126
- refresh: async (sessionToken) => {
2131
+ refresh: async (sessionToken, options) => {
2127
2132
  return this.request("POST", "/v1/auth/refresh", {
2128
- body: { session_token: sessionToken }
2133
+ sessionToken,
2134
+ clientContext: options?.clientContext,
2135
+ isAutoRefresh: options?.isAutoRefresh,
2136
+ onTokenRotated: options?.onTokenRotated
2129
2137
  });
2130
2138
  },
2131
2139
  /**
@@ -2157,7 +2165,8 @@ var ScaleMuleServer = class {
2157
2165
  if (options?.email) {
2158
2166
  return this.request("POST", "/v1/auth/resend-verification", {
2159
2167
  sessionToken: sessionTokenOrEmail,
2160
- body: { email: options.email }
2168
+ body: { email: options.email },
2169
+ onTokenRotated: options?.onTokenRotated
2161
2170
  });
2162
2171
  }
2163
2172
  if (sessionTokenOrEmail.includes("@")) {
@@ -2166,7 +2175,8 @@ var ScaleMuleServer = class {
2166
2175
  });
2167
2176
  }
2168
2177
  return this.request("POST", "/v1/auth/resend-verification", {
2169
- sessionToken: sessionTokenOrEmail
2178
+ sessionToken: sessionTokenOrEmail,
2179
+ onTokenRotated: options?.onTokenRotated
2170
2180
  });
2171
2181
  }
2172
2182
  };
@@ -2177,37 +2187,41 @@ var ScaleMuleServer = class {
2177
2187
  /**
2178
2188
  * Update user profile
2179
2189
  */
2180
- update: async (sessionToken, data) => {
2190
+ update: async (sessionToken, data, options) => {
2181
2191
  return this.request("PATCH", "/v1/auth/profile", {
2182
2192
  sessionToken,
2183
- body: data
2193
+ body: data,
2194
+ onTokenRotated: options?.onTokenRotated
2184
2195
  });
2185
2196
  },
2186
2197
  /**
2187
2198
  * Change password
2188
2199
  */
2189
- changePassword: async (sessionToken, currentPassword, newPassword) => {
2200
+ changePassword: async (sessionToken, currentPassword, newPassword, options) => {
2190
2201
  return this.request("POST", "/v1/auth/change-password", {
2191
2202
  sessionToken,
2192
- body: { current_password: currentPassword, new_password: newPassword }
2203
+ body: { current_password: currentPassword, new_password: newPassword },
2204
+ onTokenRotated: options?.onTokenRotated
2193
2205
  });
2194
2206
  },
2195
2207
  /**
2196
2208
  * Change email
2197
2209
  */
2198
- changeEmail: async (sessionToken, newEmail, password) => {
2210
+ changeEmail: async (sessionToken, newEmail, password, options) => {
2199
2211
  return this.request("POST", "/v1/auth/change-email", {
2200
2212
  sessionToken,
2201
- body: { new_email: newEmail, password }
2213
+ body: { new_email: newEmail, password },
2214
+ onTokenRotated: options?.onTokenRotated
2202
2215
  });
2203
2216
  },
2204
2217
  /**
2205
2218
  * Delete account
2206
2219
  */
2207
- deleteAccount: async (sessionToken, password) => {
2220
+ deleteAccount: async (sessionToken, password, options) => {
2208
2221
  return this.request("DELETE", "/v1/auth/me", {
2209
2222
  sessionToken,
2210
- body: { password }
2223
+ body: { password },
2224
+ onTokenRotated: options?.onTokenRotated
2211
2225
  });
2212
2226
  }
2213
2227
  };
@@ -2668,6 +2682,9 @@ var ScaleMuleServer = class {
2668
2682
  this.apiKey = config.apiKey;
2669
2683
  this.gatewayUrl = resolveGatewayUrl(config);
2670
2684
  this.debug = config.debug || false;
2685
+ this.onRefreshStart = config.onRefreshStart;
2686
+ this.onRefreshEnd = config.onRefreshEnd;
2687
+ this.onAutoRefreshFailed = config.onAutoRefreshFailed;
2671
2688
  this.money = createMoneyClient({
2672
2689
  apiKey: this.apiKey,
2673
2690
  gatewayUrl: this.gatewayUrl,
@@ -2711,6 +2728,10 @@ var ScaleMuleServer = class {
2711
2728
  headers: headers2,
2712
2729
  body: options.body ? JSON.stringify(options.body) : void 0
2713
2730
  });
2731
+ const rotated = response.headers.get("x-rotated-session-token");
2732
+ if (rotated && options.onTokenRotated) {
2733
+ options.onTokenRotated(rotated);
2734
+ }
2714
2735
  const text = await response.text();
2715
2736
  let responseData = null;
2716
2737
  try {
@@ -2722,6 +2743,33 @@ var ScaleMuleServer = class {
2722
2743
  code: `HTTP_${response.status}`,
2723
2744
  message: responseData?.message || text || response.statusText
2724
2745
  };
2746
+ if (response.status === 401 && options.sessionToken && !options.isAutoRefresh) {
2747
+ if (this.debug) console.log("[ScaleMule Server] 401 received, attempting auto-refresh...");
2748
+ try {
2749
+ this.onRefreshStart?.();
2750
+ const refreshData = await this.auth.refresh(options.sessionToken, {
2751
+ clientContext: options.clientContext,
2752
+ isAutoRefresh: true
2753
+ // Prevent infinite loops
2754
+ });
2755
+ const newToken = refreshData.session_token;
2756
+ if (this.debug) console.log("[ScaleMule Server] Auto-refresh succeeded, retrying original request...");
2757
+ options.onTokenRotated?.(newToken);
2758
+ return this.request(method, path, {
2759
+ ...options,
2760
+ sessionToken: newToken,
2761
+ isAutoRefresh: true
2762
+ // Don't refresh again on this retry
2763
+ });
2764
+ } catch (refreshErr) {
2765
+ if (this.debug) console.error("[ScaleMule Server] Auto-refresh failed:", refreshErr);
2766
+ const refreshApiError = refreshErr instanceof ScaleMuleApiError ? { code: refreshErr.code, message: refreshErr.message } : { code: "REFRESH_FAILED", message: "Auto-refresh failed" };
2767
+ this.onAutoRefreshFailed?.(refreshApiError);
2768
+ throw new ScaleMuleApiError(error);
2769
+ } finally {
2770
+ this.onRefreshEnd?.();
2771
+ }
2772
+ }
2725
2773
  throw new ScaleMuleApiError(error);
2726
2774
  }
2727
2775
  const data = responseData?.data !== void 0 ? responseData.data : responseData;
@@ -3243,8 +3291,16 @@ function createAuthRoutes(config = {}) {
3243
3291
  // ==================== Logout ====================
3244
3292
  case "logout": {
3245
3293
  const session = await getSession();
3294
+ let rotated = null;
3246
3295
  if (session) {
3247
- await sm.auth.logout(session.sessionToken);
3296
+ try {
3297
+ await sm.auth.logout(session.sessionToken, {
3298
+ onTokenRotated: (newToken) => {
3299
+ rotated = newToken;
3300
+ }
3301
+ });
3302
+ } catch {
3303
+ }
3248
3304
  }
3249
3305
  if (config.onLogout) {
3250
3306
  await config.onLogout();
@@ -3309,6 +3365,7 @@ function createAuthRoutes(config = {}) {
3309
3365
  case "resend-verification": {
3310
3366
  const { email } = body;
3311
3367
  const session = await getSession();
3368
+ let rotated = null;
3312
3369
  if (email) {
3313
3370
  try {
3314
3371
  await sm.auth.resendVerification(email);
@@ -3326,7 +3383,11 @@ function createAuthRoutes(config = {}) {
3326
3383
  return errorResponse("UNAUTHORIZED", "Email or session required", 401);
3327
3384
  }
3328
3385
  try {
3329
- await sm.auth.resendVerification(session.sessionToken);
3386
+ await sm.auth.resendVerification(session.sessionToken, {
3387
+ onTokenRotated: (newToken) => {
3388
+ rotated = newToken;
3389
+ }
3390
+ });
3330
3391
  } catch (err) {
3331
3392
  const apiErr = err instanceof ScaleMuleApiError ? err : null;
3332
3393
  return errorResponse(
@@ -3335,6 +3396,14 @@ function createAuthRoutes(config = {}) {
3335
3396
  400
3336
3397
  );
3337
3398
  }
3399
+ if (rotated) {
3400
+ return withRefreshedSession(
3401
+ rotated,
3402
+ session.userId,
3403
+ { success: true, data: { message: "Verification email sent" } },
3404
+ cookieOptions
3405
+ );
3406
+ }
3338
3407
  return successResponse({ message: "Verification email sent" });
3339
3408
  }
3340
3409
  // ==================== Refresh Session ====================
@@ -3369,11 +3438,17 @@ function createAuthRoutes(config = {}) {
3369
3438
  if (!current_password || !new_password) {
3370
3439
  return errorResponse("VALIDATION_ERROR", "Current and new password required", 400);
3371
3440
  }
3441
+ let rotated = null;
3372
3442
  try {
3373
3443
  await sm.user.changePassword(
3374
3444
  session.sessionToken,
3375
3445
  current_password,
3376
- new_password
3446
+ new_password,
3447
+ {
3448
+ onTokenRotated: (newToken) => {
3449
+ rotated = newToken;
3450
+ }
3451
+ }
3377
3452
  );
3378
3453
  } catch (err) {
3379
3454
  const apiErr = err instanceof ScaleMuleApiError ? err : null;
@@ -3383,6 +3458,14 @@ function createAuthRoutes(config = {}) {
3383
3458
  400
3384
3459
  );
3385
3460
  }
3461
+ if (rotated) {
3462
+ return withRefreshedSession(
3463
+ rotated,
3464
+ session.userId,
3465
+ { success: true, data: { message: "Password changed successfully" } },
3466
+ cookieOptions
3467
+ );
3468
+ }
3386
3469
  return successResponse({ message: "Password changed successfully" });
3387
3470
  }
3388
3471
  // ==================== Switch Account ====================
@@ -3463,14 +3546,27 @@ function createAuthRoutes(config = {}) {
3463
3546
  return withNorm(errorResponse("UNAUTHORIZED", "Authentication required", 401));
3464
3547
  }
3465
3548
  let userData;
3549
+ let rotated = null;
3466
3550
  try {
3467
- userData = await sm.auth.me(session.sessionToken);
3551
+ userData = await sm.auth.me(session.sessionToken, {
3552
+ onTokenRotated: (newToken) => {
3553
+ rotated = newToken;
3554
+ }
3555
+ });
3468
3556
  } catch {
3469
3557
  return withNorm(clearSession(
3470
3558
  { error: { code: "SESSION_EXPIRED", message: "Session expired" } },
3471
3559
  cookieOptions
3472
3560
  ));
3473
3561
  }
3562
+ if (rotated) {
3563
+ return withNorm(withRefreshedSession(
3564
+ rotated,
3565
+ session.userId,
3566
+ { user: userData, sessionToken: rotated, userId: session.userId },
3567
+ cookieOptions
3568
+ ));
3569
+ }
3474
3570
  return withNorm(successResponse({ user: userData, sessionToken: session.sessionToken, userId: session.userId }));
3475
3571
  }
3476
3572
  // ==================== Get Session Status ====================
@@ -3549,8 +3645,13 @@ function createAuthRoutes(config = {}) {
3549
3645
  const body = await request.json().catch(() => ({}));
3550
3646
  const { full_name, avatar_url } = body;
3551
3647
  let updatedUser;
3648
+ let rotated = null;
3552
3649
  try {
3553
- updatedUser = await sm.user.update(session.sessionToken, { full_name, avatar_url });
3650
+ updatedUser = await sm.user.update(session.sessionToken, { full_name, avatar_url }, {
3651
+ onTokenRotated: (newToken) => {
3652
+ rotated = newToken;
3653
+ }
3654
+ });
3554
3655
  } catch (err) {
3555
3656
  const apiErr = err instanceof ScaleMuleApiError ? err : null;
3556
3657
  return errorResponse(
@@ -3559,6 +3660,14 @@ function createAuthRoutes(config = {}) {
3559
3660
  400
3560
3661
  );
3561
3662
  }
3663
+ if (rotated) {
3664
+ return withRefreshedSession(
3665
+ rotated,
3666
+ session.userId,
3667
+ { success: true, data: { user: updatedUser } },
3668
+ cookieOptions
3669
+ );
3670
+ }
3562
3671
  return successResponse({ user: updatedUser });
3563
3672
  }
3564
3673
  default:
@@ -1,3 +1,3 @@
1
- export { g as createWebhookHandler } from '../webhook-handler-CUp_A0_p.mjs';
1
+ export { g as createWebhookHandler } from '../webhook-handler-DsxJS_He.mjs';
2
2
  import '@scalemule/money';
3
3
  import '../index-DTWyUcyd.mjs';
@@ -1,3 +1,3 @@
1
- export { g as createWebhookHandler } from '../webhook-handler-C07AK4QB.js';
1
+ export { g as createWebhookHandler } from '../webhook-handler-CTx4Uh8u.js';
2
2
  import '@scalemule/money';
3
3
  import '../index-DTWyUcyd.js';
@@ -1,5 +1,5 @@
1
1
  import { MoneyClient } from '@scalemule/money';
2
- import { $ as RegisterRequest, p as ClientContext, U as User, v as LoginRequest, L as LoginResponse, c as ListFilesParams, t as ListFilesResponse, a9 as StorageFile, af as UploadResponse } from './index-DTWyUcyd.js';
2
+ import { A as ApiError, $ as RegisterRequest, p as ClientContext, U as User, v as LoginRequest, L as LoginResponse, c as ListFilesParams, t as ListFilesResponse, a9 as StorageFile, af as UploadResponse } from './index-DTWyUcyd.js';
3
3
 
4
4
  /**
5
5
  * Server-Side ScaleMule Client
@@ -18,12 +18,21 @@ interface ServerConfig {
18
18
  gatewayUrl?: string;
19
19
  /** Enable debug logging */
20
20
  debug?: boolean;
21
+ /** Callback when a 401 auto-refresh starts */
22
+ onRefreshStart?: () => void;
23
+ /** Callback when a 401 auto-refresh finishes */
24
+ onRefreshEnd?: () => void;
25
+ /** Callback when a 401 auto-refresh fails */
26
+ onAutoRefreshFailed?: (error: ApiError) => void;
21
27
  }
22
28
  declare function resolveGatewayUrl(config: ServerConfig): string;
23
29
  declare class ScaleMuleServer {
24
30
  private apiKey;
25
31
  private gatewayUrl;
26
32
  private debug;
33
+ private onRefreshStart?;
34
+ private onRefreshEnd?;
35
+ private onAutoRefreshFailed?;
27
36
  money: MoneyClient;
28
37
  constructor(config: ServerConfig);
29
38
  moneyWithSession(sessionToken: string): MoneyClient;
@@ -55,15 +64,23 @@ declare class ScaleMuleServer {
55
64
  /**
56
65
  * Logout user
57
66
  */
58
- logout: (sessionToken: string) => Promise<void>;
67
+ logout: (sessionToken: string, options?: {
68
+ onTokenRotated?: (newToken: string) => void;
69
+ }) => Promise<void>;
59
70
  /**
60
71
  * Get current user from session token
61
72
  */
62
- me: (sessionToken: string) => Promise<User>;
73
+ me: (sessionToken: string, options?: {
74
+ onTokenRotated?: (newToken: string) => void;
75
+ }) => Promise<User>;
63
76
  /**
64
77
  * Refresh session token
65
78
  */
66
- refresh: (sessionToken: string) => Promise<{
79
+ refresh: (sessionToken: string, options?: {
80
+ clientContext?: ClientContext;
81
+ isAutoRefresh?: boolean;
82
+ onTokenRotated?: (newToken: string) => void;
83
+ }) => Promise<{
67
84
  session_token: string;
68
85
  expires_at: string;
69
86
  }>;
@@ -99,6 +116,7 @@ declare class ScaleMuleServer {
99
116
  */
100
117
  resendVerification: (sessionTokenOrEmail: string, options?: {
101
118
  email?: string;
119
+ onTokenRotated?: (newToken: string) => void;
102
120
  }) => Promise<{
103
121
  message: string;
104
122
  }>;
@@ -110,23 +128,31 @@ declare class ScaleMuleServer {
110
128
  update: (sessionToken: string, data: {
111
129
  full_name?: string;
112
130
  avatar_url?: string;
131
+ }, options?: {
132
+ onTokenRotated?: (newToken: string) => void;
113
133
  }) => Promise<User>;
114
134
  /**
115
135
  * Change password
116
136
  */
117
- changePassword: (sessionToken: string, currentPassword: string, newPassword: string) => Promise<{
137
+ changePassword: (sessionToken: string, currentPassword: string, newPassword: string, options?: {
138
+ onTokenRotated?: (newToken: string) => void;
139
+ }) => Promise<{
118
140
  message: string;
119
141
  }>;
120
142
  /**
121
143
  * Change email
122
144
  */
123
- changeEmail: (sessionToken: string, newEmail: string, password: string) => Promise<{
145
+ changeEmail: (sessionToken: string, newEmail: string, password: string, options?: {
146
+ onTokenRotated?: (newToken: string) => void;
147
+ }) => Promise<{
124
148
  message: string;
125
149
  }>;
126
150
  /**
127
151
  * Delete account
128
152
  */
129
- deleteAccount: (sessionToken: string, password: string) => Promise<{
153
+ deleteAccount: (sessionToken: string, password: string, options?: {
154
+ onTokenRotated?: (newToken: string) => void;
155
+ }) => Promise<{
130
156
  message: string;
131
157
  }>;
132
158
  };
@@ -1,5 +1,5 @@
1
1
  import { MoneyClient } from '@scalemule/money';
2
- import { $ as RegisterRequest, p as ClientContext, U as User, v as LoginRequest, L as LoginResponse, c as ListFilesParams, t as ListFilesResponse, a9 as StorageFile, af as UploadResponse } from './index-DTWyUcyd.mjs';
2
+ import { A as ApiError, $ as RegisterRequest, p as ClientContext, U as User, v as LoginRequest, L as LoginResponse, c as ListFilesParams, t as ListFilesResponse, a9 as StorageFile, af as UploadResponse } from './index-DTWyUcyd.mjs';
3
3
 
4
4
  /**
5
5
  * Server-Side ScaleMule Client
@@ -18,12 +18,21 @@ interface ServerConfig {
18
18
  gatewayUrl?: string;
19
19
  /** Enable debug logging */
20
20
  debug?: boolean;
21
+ /** Callback when a 401 auto-refresh starts */
22
+ onRefreshStart?: () => void;
23
+ /** Callback when a 401 auto-refresh finishes */
24
+ onRefreshEnd?: () => void;
25
+ /** Callback when a 401 auto-refresh fails */
26
+ onAutoRefreshFailed?: (error: ApiError) => void;
21
27
  }
22
28
  declare function resolveGatewayUrl(config: ServerConfig): string;
23
29
  declare class ScaleMuleServer {
24
30
  private apiKey;
25
31
  private gatewayUrl;
26
32
  private debug;
33
+ private onRefreshStart?;
34
+ private onRefreshEnd?;
35
+ private onAutoRefreshFailed?;
27
36
  money: MoneyClient;
28
37
  constructor(config: ServerConfig);
29
38
  moneyWithSession(sessionToken: string): MoneyClient;
@@ -55,15 +64,23 @@ declare class ScaleMuleServer {
55
64
  /**
56
65
  * Logout user
57
66
  */
58
- logout: (sessionToken: string) => Promise<void>;
67
+ logout: (sessionToken: string, options?: {
68
+ onTokenRotated?: (newToken: string) => void;
69
+ }) => Promise<void>;
59
70
  /**
60
71
  * Get current user from session token
61
72
  */
62
- me: (sessionToken: string) => Promise<User>;
73
+ me: (sessionToken: string, options?: {
74
+ onTokenRotated?: (newToken: string) => void;
75
+ }) => Promise<User>;
63
76
  /**
64
77
  * Refresh session token
65
78
  */
66
- refresh: (sessionToken: string) => Promise<{
79
+ refresh: (sessionToken: string, options?: {
80
+ clientContext?: ClientContext;
81
+ isAutoRefresh?: boolean;
82
+ onTokenRotated?: (newToken: string) => void;
83
+ }) => Promise<{
67
84
  session_token: string;
68
85
  expires_at: string;
69
86
  }>;
@@ -99,6 +116,7 @@ declare class ScaleMuleServer {
99
116
  */
100
117
  resendVerification: (sessionTokenOrEmail: string, options?: {
101
118
  email?: string;
119
+ onTokenRotated?: (newToken: string) => void;
102
120
  }) => Promise<{
103
121
  message: string;
104
122
  }>;
@@ -110,23 +128,31 @@ declare class ScaleMuleServer {
110
128
  update: (sessionToken: string, data: {
111
129
  full_name?: string;
112
130
  avatar_url?: string;
131
+ }, options?: {
132
+ onTokenRotated?: (newToken: string) => void;
113
133
  }) => Promise<User>;
114
134
  /**
115
135
  * Change password
116
136
  */
117
- changePassword: (sessionToken: string, currentPassword: string, newPassword: string) => Promise<{
137
+ changePassword: (sessionToken: string, currentPassword: string, newPassword: string, options?: {
138
+ onTokenRotated?: (newToken: string) => void;
139
+ }) => Promise<{
118
140
  message: string;
119
141
  }>;
120
142
  /**
121
143
  * Change email
122
144
  */
123
- changeEmail: (sessionToken: string, newEmail: string, password: string) => Promise<{
145
+ changeEmail: (sessionToken: string, newEmail: string, password: string, options?: {
146
+ onTokenRotated?: (newToken: string) => void;
147
+ }) => Promise<{
124
148
  message: string;
125
149
  }>;
126
150
  /**
127
151
  * Delete account
128
152
  */
129
- deleteAccount: (sessionToken: string, password: string) => Promise<{
153
+ deleteAccount: (sessionToken: string, password: string, options?: {
154
+ onTokenRotated?: (newToken: string) => void;
155
+ }) => Promise<{
130
156
  message: string;
131
157
  }>;
132
158
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/nextjs",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "ScaleMule SDK for Next.js applications - authentication, storage, and user management",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -60,7 +60,7 @@
60
60
  "dependencies": {
61
61
  "@scalemule/ledvery": "^0.0.1",
62
62
  "@scalemule/money": "^0.0.1",
63
- "@scalemule/sdk": "^0.0.47"
63
+ "@scalemule/sdk": "^0.0.48"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "next": ">=14.0.0",