@scalemule/nextjs 0.1.16 → 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.
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a8 as StorageAdapter } from './index-DTWyUcyd.mjs';
1
+ import { a8 as StorageAdapter, A as ApiError } from './index-DTWyUcyd.mjs';
2
2
 
3
3
  /**
4
4
  * ScaleMule API Client
@@ -38,6 +38,12 @@ interface ClientConfig {
38
38
  enableAccountSwitcher?: boolean;
39
39
  /** Privacy level for account switcher */
40
40
  accountSwitcherPrivacy?: 'full' | 'masked' | 'minimal';
41
+ /** Optional: Callback when a 401 auto-refresh starts. */
42
+ onRefreshStart?: () => void;
43
+ /** Optional: Callback when a 401 auto-refresh finishes. */
44
+ onRefreshEnd?: () => void;
45
+ /** Optional: Callback when an automatic 401 refresh fails. */
46
+ onAutoRefreshFailed?: (error: ApiError) => void;
41
47
  }
42
48
  interface RequestOptions extends RequestInit {
43
49
  /** Skip adding auth headers (for public endpoints) */
@@ -48,6 +54,8 @@ interface RequestOptions extends RequestInit {
48
54
  retries?: number;
49
55
  /** Skip retries */
50
56
  skipRetry?: boolean;
57
+ /** Internal flag to prevent infinite refresh loops */
58
+ isAutoRefresh?: boolean;
51
59
  }
52
60
  /**
53
61
  * ScaleMule API Client
@@ -69,6 +77,10 @@ declare class ScaleMuleClient {
69
77
  private sessionGate;
70
78
  private resolveSessionGate;
71
79
  private workspaceId;
80
+ private refreshPromise;
81
+ private onRefreshStart?;
82
+ private onRefreshEnd?;
83
+ private onAutoRefreshFailed?;
72
84
  constructor(config: ClientConfig);
73
85
  /**
74
86
  * Sync offline queue when coming back online
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a8 as StorageAdapter } from './index-DTWyUcyd.js';
1
+ import { a8 as StorageAdapter, A as ApiError } from './index-DTWyUcyd.js';
2
2
 
3
3
  /**
4
4
  * ScaleMule API Client
@@ -38,6 +38,12 @@ interface ClientConfig {
38
38
  enableAccountSwitcher?: boolean;
39
39
  /** Privacy level for account switcher */
40
40
  accountSwitcherPrivacy?: 'full' | 'masked' | 'minimal';
41
+ /** Optional: Callback when a 401 auto-refresh starts. */
42
+ onRefreshStart?: () => void;
43
+ /** Optional: Callback when a 401 auto-refresh finishes. */
44
+ onRefreshEnd?: () => void;
45
+ /** Optional: Callback when an automatic 401 refresh fails. */
46
+ onAutoRefreshFailed?: (error: ApiError) => void;
41
47
  }
42
48
  interface RequestOptions extends RequestInit {
43
49
  /** Skip adding auth headers (for public endpoints) */
@@ -48,6 +54,8 @@ interface RequestOptions extends RequestInit {
48
54
  retries?: number;
49
55
  /** Skip retries */
50
56
  skipRetry?: boolean;
57
+ /** Internal flag to prevent infinite refresh loops */
58
+ isAutoRefresh?: boolean;
51
59
  }
52
60
  /**
53
61
  * ScaleMule API Client
@@ -69,6 +77,10 @@ declare class ScaleMuleClient {
69
77
  private sessionGate;
70
78
  private resolveSessionGate;
71
79
  private workspaceId;
80
+ private refreshPromise;
81
+ private onRefreshStart?;
82
+ private onRefreshEnd?;
83
+ private onAutoRefreshFailed?;
72
84
  constructor(config: ClientConfig);
73
85
  /**
74
86
  * Sync offline queue when coming back online
package/dist/client.js CHANGED
@@ -278,6 +278,7 @@ var ScaleMuleClient = class {
278
278
  this.sessionGate = null;
279
279
  this.resolveSessionGate = null;
280
280
  this.workspaceId = null;
281
+ this.refreshPromise = null;
281
282
  this.apiKey = config.apiKey;
282
283
  this.applicationId = config.applicationId || null;
283
284
  this.gatewayUrl = resolveGatewayUrl(config);
@@ -285,6 +286,9 @@ var ScaleMuleClient = class {
285
286
  this.storage = config.storage || createDefaultStorage();
286
287
  this.enableRateLimitQueue = config.enableRateLimitQueue || false;
287
288
  this.enableOfflineQueue = config.enableOfflineQueue || false;
289
+ this.onRefreshStart = config.onRefreshStart;
290
+ this.onRefreshEnd = config.onRefreshEnd;
291
+ this.onAutoRefreshFailed = config.onAutoRefreshFailed;
288
292
  if (this.enableRateLimitQueue) {
289
293
  this.rateLimitQueue = new RateLimitQueue();
290
294
  }
@@ -503,6 +507,10 @@ var ScaleMuleClient = class {
503
507
  signal: controller.signal
504
508
  });
505
509
  clearTimeout(timeoutId);
510
+ const rotatedToken = response.headers.get("x-rotated-session-token");
511
+ if (rotatedToken && this.sessionToken && this.userId) {
512
+ this.setSession(rotatedToken, this.userId);
513
+ }
506
514
  const text = await response.text();
507
515
  let responseData = null;
508
516
  try {
@@ -512,6 +520,30 @@ var ScaleMuleClient = class {
512
520
  if (!response.ok) {
513
521
  const rawError = responseData?.error;
514
522
  const error = rawError && typeof rawError === "object" ? rawError : { code: `HTTP_${response.status}`, message: typeof rawError === "string" ? rawError : responseData?.message || text || response.statusText };
523
+ if (response.status === 401 && this.sessionToken && !options.isAutoRefresh) {
524
+ if (this.debug) console.log("[ScaleMule] 401 received, attempting auto-refresh...");
525
+ if (!this.refreshPromise) {
526
+ this.onRefreshStart?.();
527
+ this.refreshPromise = this.post("/api/auth/refresh", {}, { isAutoRefresh: true });
528
+ }
529
+ try {
530
+ await this.refreshPromise;
531
+ if (this.debug) console.log("[ScaleMule] Auto-refresh succeeded, retrying original request...");
532
+ attempt--;
533
+ if (this.sessionToken) {
534
+ headers.set("Authorization", `Bearer ${this.sessionToken}`);
535
+ }
536
+ continue;
537
+ } catch (refreshErr) {
538
+ if (this.debug) console.error("[ScaleMule] Auto-refresh failed:", refreshErr);
539
+ const refreshApiError = refreshErr instanceof ScaleMuleApiError ? { code: refreshErr.code, message: refreshErr.message, field: refreshErr.field } : { code: "REFRESH_FAILED", message: "Auto-refresh failed" };
540
+ this.onAutoRefreshFailed?.(refreshApiError);
541
+ throw new ScaleMuleApiError(error);
542
+ } finally {
543
+ this.refreshPromise = null;
544
+ this.onRefreshEnd?.();
545
+ }
546
+ }
515
547
  if (attempt < maxRetries && RETRYABLE_STATUS_CODES.has(response.status)) {
516
548
  lastError = error;
517
549
  const delay = getBackoffDelay(attempt);
package/dist/client.mjs CHANGED
@@ -276,6 +276,7 @@ var ScaleMuleClient = class {
276
276
  this.sessionGate = null;
277
277
  this.resolveSessionGate = null;
278
278
  this.workspaceId = null;
279
+ this.refreshPromise = null;
279
280
  this.apiKey = config.apiKey;
280
281
  this.applicationId = config.applicationId || null;
281
282
  this.gatewayUrl = resolveGatewayUrl(config);
@@ -283,6 +284,9 @@ var ScaleMuleClient = class {
283
284
  this.storage = config.storage || createDefaultStorage();
284
285
  this.enableRateLimitQueue = config.enableRateLimitQueue || false;
285
286
  this.enableOfflineQueue = config.enableOfflineQueue || false;
287
+ this.onRefreshStart = config.onRefreshStart;
288
+ this.onRefreshEnd = config.onRefreshEnd;
289
+ this.onAutoRefreshFailed = config.onAutoRefreshFailed;
286
290
  if (this.enableRateLimitQueue) {
287
291
  this.rateLimitQueue = new RateLimitQueue();
288
292
  }
@@ -501,6 +505,10 @@ var ScaleMuleClient = class {
501
505
  signal: controller.signal
502
506
  });
503
507
  clearTimeout(timeoutId);
508
+ const rotatedToken = response.headers.get("x-rotated-session-token");
509
+ if (rotatedToken && this.sessionToken && this.userId) {
510
+ this.setSession(rotatedToken, this.userId);
511
+ }
504
512
  const text = await response.text();
505
513
  let responseData = null;
506
514
  try {
@@ -510,6 +518,30 @@ var ScaleMuleClient = class {
510
518
  if (!response.ok) {
511
519
  const rawError = responseData?.error;
512
520
  const error = rawError && typeof rawError === "object" ? rawError : { code: `HTTP_${response.status}`, message: typeof rawError === "string" ? rawError : responseData?.message || text || response.statusText };
521
+ if (response.status === 401 && this.sessionToken && !options.isAutoRefresh) {
522
+ if (this.debug) console.log("[ScaleMule] 401 received, attempting auto-refresh...");
523
+ if (!this.refreshPromise) {
524
+ this.onRefreshStart?.();
525
+ this.refreshPromise = this.post("/api/auth/refresh", {}, { isAutoRefresh: true });
526
+ }
527
+ try {
528
+ await this.refreshPromise;
529
+ if (this.debug) console.log("[ScaleMule] Auto-refresh succeeded, retrying original request...");
530
+ attempt--;
531
+ if (this.sessionToken) {
532
+ headers.set("Authorization", `Bearer ${this.sessionToken}`);
533
+ }
534
+ continue;
535
+ } catch (refreshErr) {
536
+ if (this.debug) console.error("[ScaleMule] Auto-refresh failed:", refreshErr);
537
+ const refreshApiError = refreshErr instanceof ScaleMuleApiError ? { code: refreshErr.code, message: refreshErr.message, field: refreshErr.field } : { code: "REFRESH_FAILED", message: "Auto-refresh failed" };
538
+ this.onAutoRefreshFailed?.(refreshApiError);
539
+ throw new ScaleMuleApiError(error);
540
+ } finally {
541
+ this.refreshPromise = null;
542
+ this.onRefreshEnd?.();
543
+ }
544
+ }
513
545
  if (attempt < maxRetries && RETRYABLE_STATUS_CODES.has(response.status)) {
514
546
  lastError = error;
515
547
  const delay = getBackoffDelay(attempt);
package/dist/index.js CHANGED
@@ -7023,6 +7023,7 @@ var ScaleMuleClient2 = class {
7023
7023
  this.sessionGate = null;
7024
7024
  this.resolveSessionGate = null;
7025
7025
  this.workspaceId = null;
7026
+ this.refreshPromise = null;
7026
7027
  this.apiKey = config.apiKey;
7027
7028
  this.applicationId = config.applicationId || null;
7028
7029
  this.gatewayUrl = resolveGatewayUrl(config);
@@ -7030,6 +7031,9 @@ var ScaleMuleClient2 = class {
7030
7031
  this.storage = config.storage || createDefaultStorage2();
7031
7032
  this.enableRateLimitQueue = config.enableRateLimitQueue || false;
7032
7033
  this.enableOfflineQueue = config.enableOfflineQueue || false;
7034
+ this.onRefreshStart = config.onRefreshStart;
7035
+ this.onRefreshEnd = config.onRefreshEnd;
7036
+ this.onAutoRefreshFailed = config.onAutoRefreshFailed;
7033
7037
  if (this.enableRateLimitQueue) {
7034
7038
  this.rateLimitQueue = new RateLimitQueue2();
7035
7039
  }
@@ -7248,6 +7252,10 @@ var ScaleMuleClient2 = class {
7248
7252
  signal: controller.signal
7249
7253
  });
7250
7254
  clearTimeout(timeoutId);
7255
+ const rotatedToken = response.headers.get("x-rotated-session-token");
7256
+ if (rotatedToken && this.sessionToken && this.userId) {
7257
+ this.setSession(rotatedToken, this.userId);
7258
+ }
7251
7259
  const text = await response.text();
7252
7260
  let responseData = null;
7253
7261
  try {
@@ -7257,6 +7265,30 @@ var ScaleMuleClient2 = class {
7257
7265
  if (!response.ok) {
7258
7266
  const rawError = responseData?.error;
7259
7267
  const error = rawError && typeof rawError === "object" ? rawError : { code: `HTTP_${response.status}`, message: typeof rawError === "string" ? rawError : responseData?.message || text || response.statusText };
7268
+ if (response.status === 401 && this.sessionToken && !options.isAutoRefresh) {
7269
+ if (this.debug) console.log("[ScaleMule] 401 received, attempting auto-refresh...");
7270
+ if (!this.refreshPromise) {
7271
+ this.onRefreshStart?.();
7272
+ this.refreshPromise = this.post("/api/auth/refresh", {}, { isAutoRefresh: true });
7273
+ }
7274
+ try {
7275
+ await this.refreshPromise;
7276
+ if (this.debug) console.log("[ScaleMule] Auto-refresh succeeded, retrying original request...");
7277
+ attempt--;
7278
+ if (this.sessionToken) {
7279
+ headers.set("Authorization", `Bearer ${this.sessionToken}`);
7280
+ }
7281
+ continue;
7282
+ } catch (refreshErr) {
7283
+ if (this.debug) console.error("[ScaleMule] Auto-refresh failed:", refreshErr);
7284
+ const refreshApiError = refreshErr instanceof ScaleMuleApiError ? { code: refreshErr.code, message: refreshErr.message, field: refreshErr.field } : { code: "REFRESH_FAILED", message: "Auto-refresh failed" };
7285
+ this.onAutoRefreshFailed?.(refreshApiError);
7286
+ throw new ScaleMuleApiError(error);
7287
+ } finally {
7288
+ this.refreshPromise = null;
7289
+ this.onRefreshEnd?.();
7290
+ }
7291
+ }
7260
7292
  if (attempt < maxRetries && RETRYABLE_STATUS_CODES3.has(response.status)) {
7261
7293
  lastError = error;
7262
7294
  const delay = getBackoffDelay2(attempt);
package/dist/index.mjs CHANGED
@@ -7003,6 +7003,7 @@ var ScaleMuleClient2 = class {
7003
7003
  this.sessionGate = null;
7004
7004
  this.resolveSessionGate = null;
7005
7005
  this.workspaceId = null;
7006
+ this.refreshPromise = null;
7006
7007
  this.apiKey = config.apiKey;
7007
7008
  this.applicationId = config.applicationId || null;
7008
7009
  this.gatewayUrl = resolveGatewayUrl(config);
@@ -7010,6 +7011,9 @@ var ScaleMuleClient2 = class {
7010
7011
  this.storage = config.storage || createDefaultStorage2();
7011
7012
  this.enableRateLimitQueue = config.enableRateLimitQueue || false;
7012
7013
  this.enableOfflineQueue = config.enableOfflineQueue || false;
7014
+ this.onRefreshStart = config.onRefreshStart;
7015
+ this.onRefreshEnd = config.onRefreshEnd;
7016
+ this.onAutoRefreshFailed = config.onAutoRefreshFailed;
7013
7017
  if (this.enableRateLimitQueue) {
7014
7018
  this.rateLimitQueue = new RateLimitQueue2();
7015
7019
  }
@@ -7228,6 +7232,10 @@ var ScaleMuleClient2 = class {
7228
7232
  signal: controller.signal
7229
7233
  });
7230
7234
  clearTimeout(timeoutId);
7235
+ const rotatedToken = response.headers.get("x-rotated-session-token");
7236
+ if (rotatedToken && this.sessionToken && this.userId) {
7237
+ this.setSession(rotatedToken, this.userId);
7238
+ }
7231
7239
  const text = await response.text();
7232
7240
  let responseData = null;
7233
7241
  try {
@@ -7237,6 +7245,30 @@ var ScaleMuleClient2 = class {
7237
7245
  if (!response.ok) {
7238
7246
  const rawError = responseData?.error;
7239
7247
  const error = rawError && typeof rawError === "object" ? rawError : { code: `HTTP_${response.status}`, message: typeof rawError === "string" ? rawError : responseData?.message || text || response.statusText };
7248
+ if (response.status === 401 && this.sessionToken && !options.isAutoRefresh) {
7249
+ if (this.debug) console.log("[ScaleMule] 401 received, attempting auto-refresh...");
7250
+ if (!this.refreshPromise) {
7251
+ this.onRefreshStart?.();
7252
+ this.refreshPromise = this.post("/api/auth/refresh", {}, { isAutoRefresh: true });
7253
+ }
7254
+ try {
7255
+ await this.refreshPromise;
7256
+ if (this.debug) console.log("[ScaleMule] Auto-refresh succeeded, retrying original request...");
7257
+ attempt--;
7258
+ if (this.sessionToken) {
7259
+ headers.set("Authorization", `Bearer ${this.sessionToken}`);
7260
+ }
7261
+ continue;
7262
+ } catch (refreshErr) {
7263
+ if (this.debug) console.error("[ScaleMule] Auto-refresh failed:", refreshErr);
7264
+ const refreshApiError = refreshErr instanceof ScaleMuleApiError ? { code: refreshErr.code, message: refreshErr.message, field: refreshErr.field } : { code: "REFRESH_FAILED", message: "Auto-refresh failed" };
7265
+ this.onAutoRefreshFailed?.(refreshApiError);
7266
+ throw new ScaleMuleApiError(error);
7267
+ } finally {
7268
+ this.refreshPromise = null;
7269
+ this.onRefreshEnd?.();
7270
+ }
7271
+ }
7240
7272
  if (attempt < maxRetries && RETRYABLE_STATUS_CODES3.has(response.status)) {
7241
7273
  lastError = error;
7242
7274
  const delay = getBackoffDelay2(attempt);
@@ -132,23 +132,31 @@ var ScaleMuleServer = class {
132
132
  /**
133
133
  * Logout user
134
134
  */
135
- logout: async (sessionToken) => {
135
+ logout: async (sessionToken, options) => {
136
136
  return this.request("POST", "/v1/auth/logout", {
137
- body: { session_token: sessionToken }
137
+ sessionToken,
138
+ body: { session_token: sessionToken },
139
+ onTokenRotated: options?.onTokenRotated
138
140
  });
139
141
  },
140
142
  /**
141
143
  * Get current user from session token
142
144
  */
143
- me: async (sessionToken) => {
144
- return this.request("GET", "/v1/auth/me", { sessionToken });
145
+ me: async (sessionToken, options) => {
146
+ return this.request("GET", "/v1/auth/me", {
147
+ sessionToken,
148
+ onTokenRotated: options?.onTokenRotated
149
+ });
145
150
  },
146
151
  /**
147
152
  * Refresh session token
148
153
  */
149
- refresh: async (sessionToken) => {
154
+ refresh: async (sessionToken, options) => {
150
155
  return this.request("POST", "/v1/auth/refresh", {
151
- body: { session_token: sessionToken }
156
+ sessionToken,
157
+ clientContext: options?.clientContext,
158
+ isAutoRefresh: options?.isAutoRefresh,
159
+ onTokenRotated: options?.onTokenRotated
152
160
  });
153
161
  },
154
162
  /**
@@ -180,7 +188,8 @@ var ScaleMuleServer = class {
180
188
  if (options?.email) {
181
189
  return this.request("POST", "/v1/auth/resend-verification", {
182
190
  sessionToken: sessionTokenOrEmail,
183
- body: { email: options.email }
191
+ body: { email: options.email },
192
+ onTokenRotated: options?.onTokenRotated
184
193
  });
185
194
  }
186
195
  if (sessionTokenOrEmail.includes("@")) {
@@ -189,7 +198,8 @@ var ScaleMuleServer = class {
189
198
  });
190
199
  }
191
200
  return this.request("POST", "/v1/auth/resend-verification", {
192
- sessionToken: sessionTokenOrEmail
201
+ sessionToken: sessionTokenOrEmail,
202
+ onTokenRotated: options?.onTokenRotated
193
203
  });
194
204
  }
195
205
  };
@@ -200,37 +210,41 @@ var ScaleMuleServer = class {
200
210
  /**
201
211
  * Update user profile
202
212
  */
203
- update: async (sessionToken, data) => {
213
+ update: async (sessionToken, data, options) => {
204
214
  return this.request("PATCH", "/v1/auth/profile", {
205
215
  sessionToken,
206
- body: data
216
+ body: data,
217
+ onTokenRotated: options?.onTokenRotated
207
218
  });
208
219
  },
209
220
  /**
210
221
  * Change password
211
222
  */
212
- changePassword: async (sessionToken, currentPassword, newPassword) => {
223
+ changePassword: async (sessionToken, currentPassword, newPassword, options) => {
213
224
  return this.request("POST", "/v1/auth/change-password", {
214
225
  sessionToken,
215
- body: { current_password: currentPassword, new_password: newPassword }
226
+ body: { current_password: currentPassword, new_password: newPassword },
227
+ onTokenRotated: options?.onTokenRotated
216
228
  });
217
229
  },
218
230
  /**
219
231
  * Change email
220
232
  */
221
- changeEmail: async (sessionToken, newEmail, password) => {
233
+ changeEmail: async (sessionToken, newEmail, password, options) => {
222
234
  return this.request("POST", "/v1/auth/change-email", {
223
235
  sessionToken,
224
- body: { new_email: newEmail, password }
236
+ body: { new_email: newEmail, password },
237
+ onTokenRotated: options?.onTokenRotated
225
238
  });
226
239
  },
227
240
  /**
228
241
  * Delete account
229
242
  */
230
- deleteAccount: async (sessionToken, password) => {
243
+ deleteAccount: async (sessionToken, password, options) => {
231
244
  return this.request("DELETE", "/v1/auth/me", {
232
245
  sessionToken,
233
- body: { password }
246
+ body: { password },
247
+ onTokenRotated: options?.onTokenRotated
234
248
  });
235
249
  }
236
250
  };
@@ -691,6 +705,9 @@ var ScaleMuleServer = class {
691
705
  this.apiKey = config.apiKey;
692
706
  this.gatewayUrl = resolveGatewayUrl(config);
693
707
  this.debug = config.debug || false;
708
+ this.onRefreshStart = config.onRefreshStart;
709
+ this.onRefreshEnd = config.onRefreshEnd;
710
+ this.onAutoRefreshFailed = config.onAutoRefreshFailed;
694
711
  this.money = money.createMoneyClient({
695
712
  apiKey: this.apiKey,
696
713
  gatewayUrl: this.gatewayUrl,
@@ -734,6 +751,10 @@ var ScaleMuleServer = class {
734
751
  headers,
735
752
  body: options.body ? JSON.stringify(options.body) : void 0
736
753
  });
754
+ const rotated = response.headers.get("x-rotated-session-token");
755
+ if (rotated && options.onTokenRotated) {
756
+ options.onTokenRotated(rotated);
757
+ }
737
758
  const text = await response.text();
738
759
  let responseData = null;
739
760
  try {
@@ -745,6 +766,33 @@ var ScaleMuleServer = class {
745
766
  code: `HTTP_${response.status}`,
746
767
  message: responseData?.message || text || response.statusText
747
768
  };
769
+ if (response.status === 401 && options.sessionToken && !options.isAutoRefresh) {
770
+ if (this.debug) console.log("[ScaleMule Server] 401 received, attempting auto-refresh...");
771
+ try {
772
+ this.onRefreshStart?.();
773
+ const refreshData = await this.auth.refresh(options.sessionToken, {
774
+ clientContext: options.clientContext,
775
+ isAutoRefresh: true
776
+ // Prevent infinite loops
777
+ });
778
+ const newToken = refreshData.session_token;
779
+ if (this.debug) console.log("[ScaleMule Server] Auto-refresh succeeded, retrying original request...");
780
+ options.onTokenRotated?.(newToken);
781
+ return this.request(method, path, {
782
+ ...options,
783
+ sessionToken: newToken,
784
+ isAutoRefresh: true
785
+ // Don't refresh again on this retry
786
+ });
787
+ } catch (refreshErr) {
788
+ if (this.debug) console.error("[ScaleMule Server] Auto-refresh failed:", refreshErr);
789
+ const refreshApiError = refreshErr instanceof ScaleMuleApiError ? { code: refreshErr.code, message: refreshErr.message } : { code: "REFRESH_FAILED", message: "Auto-refresh failed" };
790
+ this.onAutoRefreshFailed?.(refreshApiError);
791
+ throw new ScaleMuleApiError(error);
792
+ } finally {
793
+ this.onRefreshEnd?.();
794
+ }
795
+ }
748
796
  throw new ScaleMuleApiError(error);
749
797
  }
750
798
  const data = responseData?.data !== void 0 ? responseData.data : responseData;
@@ -1173,8 +1221,16 @@ function createAuthRoutes(config = {}) {
1173
1221
  // ==================== Logout ====================
1174
1222
  case "logout": {
1175
1223
  const session = await getSession();
1224
+ let rotated = null;
1176
1225
  if (session) {
1177
- await sm.auth.logout(session.sessionToken);
1226
+ try {
1227
+ await sm.auth.logout(session.sessionToken, {
1228
+ onTokenRotated: (newToken) => {
1229
+ rotated = newToken;
1230
+ }
1231
+ });
1232
+ } catch {
1233
+ }
1178
1234
  }
1179
1235
  if (config.onLogout) {
1180
1236
  await config.onLogout();
@@ -1239,6 +1295,7 @@ function createAuthRoutes(config = {}) {
1239
1295
  case "resend-verification": {
1240
1296
  const { email } = body;
1241
1297
  const session = await getSession();
1298
+ let rotated = null;
1242
1299
  if (email) {
1243
1300
  try {
1244
1301
  await sm.auth.resendVerification(email);
@@ -1256,7 +1313,11 @@ function createAuthRoutes(config = {}) {
1256
1313
  return errorResponse("UNAUTHORIZED", "Email or session required", 401);
1257
1314
  }
1258
1315
  try {
1259
- await sm.auth.resendVerification(session.sessionToken);
1316
+ await sm.auth.resendVerification(session.sessionToken, {
1317
+ onTokenRotated: (newToken) => {
1318
+ rotated = newToken;
1319
+ }
1320
+ });
1260
1321
  } catch (err) {
1261
1322
  const apiErr = err instanceof ScaleMuleApiError ? err : null;
1262
1323
  return errorResponse(
@@ -1265,6 +1326,14 @@ function createAuthRoutes(config = {}) {
1265
1326
  400
1266
1327
  );
1267
1328
  }
1329
+ if (rotated) {
1330
+ return withRefreshedSession(
1331
+ rotated,
1332
+ session.userId,
1333
+ { success: true, data: { message: "Verification email sent" } },
1334
+ cookieOptions
1335
+ );
1336
+ }
1268
1337
  return successResponse({ message: "Verification email sent" });
1269
1338
  }
1270
1339
  // ==================== Refresh Session ====================
@@ -1299,11 +1368,17 @@ function createAuthRoutes(config = {}) {
1299
1368
  if (!current_password || !new_password) {
1300
1369
  return errorResponse("VALIDATION_ERROR", "Current and new password required", 400);
1301
1370
  }
1371
+ let rotated = null;
1302
1372
  try {
1303
1373
  await sm.user.changePassword(
1304
1374
  session.sessionToken,
1305
1375
  current_password,
1306
- new_password
1376
+ new_password,
1377
+ {
1378
+ onTokenRotated: (newToken) => {
1379
+ rotated = newToken;
1380
+ }
1381
+ }
1307
1382
  );
1308
1383
  } catch (err) {
1309
1384
  const apiErr = err instanceof ScaleMuleApiError ? err : null;
@@ -1313,6 +1388,14 @@ function createAuthRoutes(config = {}) {
1313
1388
  400
1314
1389
  );
1315
1390
  }
1391
+ if (rotated) {
1392
+ return withRefreshedSession(
1393
+ rotated,
1394
+ session.userId,
1395
+ { success: true, data: { message: "Password changed successfully" } },
1396
+ cookieOptions
1397
+ );
1398
+ }
1316
1399
  return successResponse({ message: "Password changed successfully" });
1317
1400
  }
1318
1401
  // ==================== Switch Account ====================
@@ -1393,14 +1476,27 @@ function createAuthRoutes(config = {}) {
1393
1476
  return withNorm(errorResponse("UNAUTHORIZED", "Authentication required", 401));
1394
1477
  }
1395
1478
  let userData;
1479
+ let rotated = null;
1396
1480
  try {
1397
- userData = await sm.auth.me(session.sessionToken);
1481
+ userData = await sm.auth.me(session.sessionToken, {
1482
+ onTokenRotated: (newToken) => {
1483
+ rotated = newToken;
1484
+ }
1485
+ });
1398
1486
  } catch {
1399
1487
  return withNorm(clearSession(
1400
1488
  { error: { code: "SESSION_EXPIRED", message: "Session expired" } },
1401
1489
  cookieOptions
1402
1490
  ));
1403
1491
  }
1492
+ if (rotated) {
1493
+ return withNorm(withRefreshedSession(
1494
+ rotated,
1495
+ session.userId,
1496
+ { user: userData, sessionToken: rotated, userId: session.userId },
1497
+ cookieOptions
1498
+ ));
1499
+ }
1404
1500
  return withNorm(successResponse({ user: userData, sessionToken: session.sessionToken, userId: session.userId }));
1405
1501
  }
1406
1502
  // ==================== Get Session Status ====================
@@ -1479,8 +1575,13 @@ function createAuthRoutes(config = {}) {
1479
1575
  const body = await request.json().catch(() => ({}));
1480
1576
  const { full_name, avatar_url } = body;
1481
1577
  let updatedUser;
1578
+ let rotated = null;
1482
1579
  try {
1483
- updatedUser = await sm.user.update(session.sessionToken, { full_name, avatar_url });
1580
+ updatedUser = await sm.user.update(session.sessionToken, { full_name, avatar_url }, {
1581
+ onTokenRotated: (newToken) => {
1582
+ rotated = newToken;
1583
+ }
1584
+ });
1484
1585
  } catch (err) {
1485
1586
  const apiErr = err instanceof ScaleMuleApiError ? err : null;
1486
1587
  return errorResponse(
@@ -1489,6 +1590,14 @@ function createAuthRoutes(config = {}) {
1489
1590
  400
1490
1591
  );
1491
1592
  }
1593
+ if (rotated) {
1594
+ return withRefreshedSession(
1595
+ rotated,
1596
+ session.userId,
1597
+ { success: true, data: { user: updatedUser } },
1598
+ cookieOptions
1599
+ );
1600
+ }
1492
1601
  return successResponse({ user: updatedUser });
1493
1602
  }
1494
1603
  default: