formreader-session-timeout 0.2.3 → 0.2.4

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/index.d.ts CHANGED
@@ -17,6 +17,8 @@ interface SessionConfig {
17
17
  refreshEndpoint: string;
18
18
  /** Endpoint to call for logout */
19
19
  logoutEndpoint: string;
20
+ /** Whether to track idle time */
21
+ trackIdleTime?: boolean;
20
22
  /** Whether to show idle warning dialog */
21
23
  showIdleWarning: boolean;
22
24
  /** Time before idle timeout to show warning (milliseconds) */
@@ -27,6 +29,8 @@ interface SessionConfig {
27
29
  onIdle?: () => void;
28
30
  /** Callback when session has expired */
29
31
  onSessionExpired?: () => void;
32
+ /** Callback when user logs out */
33
+ onLogout?: () => void;
30
34
  /** Callback on refresh success */
31
35
  onRefreshSuccess?: () => void;
32
36
  /** Callback on refresh failure */
@@ -41,10 +45,20 @@ interface SessionConfig {
41
45
  data: any;
42
46
  }>;
43
47
  };
44
- /** Custom function to format the refresh request payload. Allows you to define exactly what data is sent to your API. */
45
- refreshPayloadFormatter?: (token: string) => Record<string, any>;
46
- /** Custom function to format the logout request payload. Allows you to define exactly what data is sent to your API. */
48
+ /** Custom function to format the refresh request payload */
49
+ refreshPayloadFormatter?: (token: string, refreshToken: string) => Record<string, any>;
50
+ /** Custom function to format the logout request payload */
47
51
  logoutPayloadFormatter?: (token: string) => Record<string, any>;
52
+ /** Which field in login response contains access token (default: 'access') */
53
+ accessTokenField?: string;
54
+ /** Which field in login response contains refresh token (default: 'refresh') */
55
+ refreshTokenField?: string;
56
+ /** Which field in refresh response contains new access token (default: 'access') */
57
+ refreshAccessTokenField?: string;
58
+ /** Which field in refresh response contains new refresh token (default: 'refresh') */
59
+ refreshRefreshTokenField?: string;
60
+ /** Whether to store refresh token separately (default: true) */
61
+ storeRefreshToken?: boolean;
48
62
  }
49
63
  interface JWTPayload {
50
64
  exp: number;
@@ -77,6 +91,7 @@ declare const DEFAULT_SESSION_CONFIG: SessionConfig;
77
91
  /**
78
92
  * Session Manager Service
79
93
  * Manages token refresh, idle tracking, and session lifecycle
94
+ * Supports configurable token response field mapping
80
95
  */
81
96
 
82
97
  declare class SessionManager {
@@ -90,15 +105,16 @@ declare class SessionManager {
90
105
  private requestDeduplication;
91
106
  constructor(config: Partial<SessionConfig>);
92
107
  /**
93
- * Initialize session management
108
+ * Initialize session management with optional login response
109
+ * Extracts and stores tokens from login response using configured field names
94
110
  */
95
- init(): void;
111
+ init(loginResponse?: any): void;
96
112
  /**
97
113
  * Setup token refresh before expiry
98
114
  */
99
115
  private setupTokenRefresh;
100
116
  /**
101
- * Setup idle activity tracking
117
+ * Setup idle tracking with activity listeners
102
118
  */
103
119
  private setupIdleTracking;
104
120
  /**
@@ -106,27 +122,23 @@ declare class SessionManager {
106
122
  */
107
123
  private setupMaxSessionDuration;
108
124
  /**
109
- * Refresh token
125
+ * Refresh token using stored refresh token
110
126
  */
111
127
  refreshToken(): Promise<boolean>;
112
128
  /**
113
- * Logout and cleanup
129
+ * Manual logout
114
130
  */
115
131
  logout(): Promise<void>;
116
132
  /**
117
- * Extend session (reset idle timer)
133
+ * Check if session is active
118
134
  */
119
- extendSession(): void;
135
+ isActive(): boolean;
120
136
  /**
121
137
  * Get current state
122
138
  */
123
139
  getState(): SessionState;
124
140
  /**
125
- * Get current config
126
- */
127
- getConfig(): SessionConfig;
128
- /**
129
- * Update config
141
+ * Update config at runtime
130
142
  */
131
143
  updateConfig(newConfig: Partial<SessionConfig>): void;
132
144
  /**
@@ -171,48 +183,57 @@ declare function isTokenExpired(token: string, bufferMs?: number): boolean;
171
183
  */
172
184
  declare function getTimeUntilExpiry(token: string): number;
173
185
  /**
174
- * Extract token from storage
186
+ * Extract token from storage (access token)
175
187
  */
176
188
  declare function getStoredToken(): string | null;
189
+ /**
190
+ * Extract refresh token from storage
191
+ */
192
+ declare function getStoredRefreshToken(): string | null;
177
193
  /**
178
194
  * Store token in appropriate storage
179
195
  */
180
196
  declare function storeToken(token: string, persistent?: boolean): void;
181
197
  /**
182
- * Clear stored token
198
+ * Store refresh token in appropriate storage
199
+ */
200
+ declare function storeRefreshToken(token: string, persistent?: boolean): void;
201
+ /**
202
+ * Clear all tokens from storage
183
203
  */
184
204
  declare function clearToken(): void;
185
205
  /**
186
- * Validate token structure and expiry
206
+ * Validate token structure
187
207
  */
188
- declare function validateToken(token: string): {
189
- valid: boolean;
190
- error?: string;
191
- };
208
+ declare function validateToken(token: string): boolean;
192
209
 
193
210
  /**
194
211
  * useSessionTimeout Hook
195
- * React hook for session timeout management in components
212
+ * React hook for managing session timeout
196
213
  */
197
214
 
198
- interface UseSessionTimeoutOptions {
199
- config?: Partial<SessionConfig>;
200
- enabled?: boolean;
201
- onSessionExpiring?: () => void;
202
- onSessionExpired?: () => void;
203
- onIdle?: () => void;
204
- onRefreshSuccess?: () => void;
215
+ interface UseSessionTimeoutOptions extends Partial<SessionConfig> {
216
+ /**
217
+ * Optional login response from your authentication endpoint
218
+ * Used to extract tokens from response if provided
219
+ */
220
+ loginResponse?: any;
221
+ /**
222
+ * Whether to automatically initialize on mount
223
+ */
224
+ autoInit?: boolean;
205
225
  }
206
226
  declare function useSessionTimeout(options?: UseSessionTimeoutOptions): {
207
- sessionState: SessionState;
208
- timeUntilExpiry: any;
227
+ isActive: boolean;
228
+ isIdle: boolean;
229
+ isRefreshing: boolean;
230
+ refreshAttempts: number;
231
+ timeUntilTimeout: number;
209
232
  timeUntilIdle: number;
210
- idleWarningVisible: boolean;
211
- extendSession: () => void;
233
+ error: Error;
212
234
  refreshToken: () => Promise<boolean>;
213
235
  logout: () => Promise<void>;
214
- updateConfig: (newConfig: Partial<SessionConfig>) => void;
215
- manager: SessionManager;
236
+ dismissIdleWarning: () => void;
216
237
  };
217
238
 
218
239
  /**
@@ -221,4 +242,4 @@ declare function useSessionTimeout(options?: UseSessionTimeoutOptions): {
221
242
  */
222
243
  declare function SessionStatus(): react_jsx_runtime.JSX.Element;
223
244
 
224
- export { DEFAULT_SESSION_CONFIG, JWTPayload, RefreshTokenResponse, SessionConfig, SessionManager, SessionState, SessionStatus, TokenInfo, UseSessionTimeoutOptions, clearToken, getSessionManager, getStoredToken, getTimeUntilExpiry, getTokenInfo, isTokenExpired, resetSessionManager, storeToken, useSessionTimeout, validateToken };
245
+ export { DEFAULT_SESSION_CONFIG, JWTPayload, RefreshTokenResponse, SessionConfig, SessionManager, SessionState, SessionStatus, TokenInfo, UseSessionTimeoutOptions, clearToken, getSessionManager, getStoredRefreshToken, getStoredToken, getTimeUntilExpiry, getTokenInfo, isTokenExpired, resetSessionManager, storeRefreshToken, storeToken, useSessionTimeout, validateToken };
package/dist/index.js CHANGED
@@ -24,11 +24,13 @@ __export(session_timeout_exports, {
24
24
  SessionStatus: () => SessionStatus,
25
25
  clearToken: () => clearToken,
26
26
  getSessionManager: () => getSessionManager,
27
+ getStoredRefreshToken: () => getStoredRefreshToken,
27
28
  getStoredToken: () => getStoredToken,
28
29
  getTimeUntilExpiry: () => getTimeUntilExpiry,
29
30
  getTokenInfo: () => getTokenInfo,
30
31
  isTokenExpired: () => isTokenExpired,
31
32
  resetSessionManager: () => resetSessionManager,
33
+ storeRefreshToken: () => storeRefreshToken,
32
34
  storeToken: () => storeToken,
33
35
  useSessionTimeout: () => useSessionTimeout,
34
36
  validateToken: () => validateToken
@@ -47,11 +49,17 @@ var DEFAULT_SESSION_CONFIG = {
47
49
  // 8 hours max
48
50
  refreshEndpoint: "/auth/refresh/",
49
51
  logoutEndpoint: "/auth/logout/",
52
+ trackIdleTime: true,
50
53
  showIdleWarning: true,
51
54
  idleWarningThresholdMs: 2 * 60 * 1e3,
52
55
  // 2 minutes before idle timeout
53
56
  autoRefresh: true,
54
- debug: false
57
+ debug: false,
58
+ accessTokenField: "access",
59
+ refreshTokenField: "refresh",
60
+ refreshAccessTokenField: "access",
61
+ refreshRefreshTokenField: "refresh",
62
+ storeRefreshToken: true
55
63
  };
56
64
 
57
65
  // services/tokenService.ts
@@ -101,34 +109,25 @@ function getTimeUntilExpiry(token) {
101
109
  function getStoredToken() {
102
110
  return sessionStorage.getItem("authToken") || localStorage.getItem("authToken");
103
111
  }
112
+ function getStoredRefreshToken() {
113
+ return sessionStorage.getItem("refreshToken") || localStorage.getItem("refreshToken");
114
+ }
104
115
  function storeToken(token, persistent = false) {
105
- if (persistent) {
106
- localStorage.setItem("authToken", token);
107
- sessionStorage.removeItem("authToken");
108
- } else {
109
- sessionStorage.setItem("authToken", token);
110
- localStorage.removeItem("authToken");
111
- }
116
+ const storage = persistent ? localStorage : sessionStorage;
117
+ storage.setItem("authToken", token);
118
+ }
119
+ function storeRefreshToken(token, persistent = false) {
120
+ const storage = persistent ? localStorage : sessionStorage;
121
+ storage.setItem("refreshToken", token);
112
122
  }
113
123
  function clearToken() {
114
124
  sessionStorage.removeItem("authToken");
115
125
  localStorage.removeItem("authToken");
126
+ sessionStorage.removeItem("refreshToken");
127
+ localStorage.removeItem("refreshToken");
116
128
  }
117
129
  function validateToken(token) {
118
- if (!token || typeof token !== "string") {
119
- return { valid: false, error: "Token is missing or invalid" };
120
- }
121
- const payload = decodeJWT(token);
122
- if (!payload) {
123
- return { valid: false, error: "Failed to decode token" };
124
- }
125
- if (!payload.exp) {
126
- return { valid: false, error: "Token missing expiry time" };
127
- }
128
- if (isTokenExpired(token)) {
129
- return { valid: false, error: "Token has expired" };
130
- }
131
- return { valid: true };
130
+ return decodeJWT(token) !== null;
132
131
  }
133
132
 
134
133
  // services/sessionManager.ts
@@ -149,6 +148,20 @@ function defaultFetchClient() {
149
148
  }
150
149
  };
151
150
  }
151
+ function extractTokenFromResponse(response, fieldName = "access") {
152
+ if (!response)
153
+ return null;
154
+ const parts = fieldName.split(".");
155
+ let value = response;
156
+ for (const part of parts) {
157
+ if (value && typeof value === "object") {
158
+ value = value[part];
159
+ } else {
160
+ return null;
161
+ }
162
+ }
163
+ return typeof value === "string" ? value : null;
164
+ }
152
165
  var SessionManager = class {
153
166
  constructor(config) {
154
167
  this.refreshTimer = null;
@@ -171,17 +184,29 @@ var SessionManager = class {
171
184
  this.log("SessionManager initialized with config:", this.config);
172
185
  }
173
186
  /**
174
- * Initialize session management
187
+ * Initialize session management with optional login response
188
+ * Extracts and stores tokens from login response using configured field names
175
189
  */
176
- init() {
177
- const token = getStoredToken();
190
+ init(loginResponse) {
191
+ const token = loginResponse ? extractTokenFromResponse(loginResponse, this.config.accessTokenField || "access") : getStoredToken();
178
192
  if (!token) {
179
193
  this.log("No token found, session not initialized");
180
194
  return;
181
195
  }
196
+ storeToken(token);
197
+ if (this.config.storeRefreshToken && loginResponse) {
198
+ const refreshToken = extractTokenFromResponse(
199
+ loginResponse,
200
+ this.config.refreshTokenField || "refresh"
201
+ );
202
+ if (refreshToken) {
203
+ storeRefreshToken(refreshToken);
204
+ this.log("Refresh token stored");
205
+ }
206
+ }
182
207
  const validation = validateToken(token);
183
- if (!validation.valid) {
184
- this.log("Invalid token:", validation.error);
208
+ if (!validation) {
209
+ this.log("Invalid token");
185
210
  this.logout();
186
211
  return;
187
212
  }
@@ -201,43 +226,33 @@ var SessionManager = class {
201
226
  return;
202
227
  const timeUntilExpiry = getTimeUntilExpiry(token);
203
228
  const refreshAt = Math.max(0, timeUntilExpiry - this.config.refreshThresholdMs);
204
- this.log(`Token expires in ${timeUntilExpiry}ms, will refresh in ${refreshAt}ms`);
205
229
  if (this.refreshTimer) {
206
230
  clearTimeout(this.refreshTimer);
207
231
  }
208
- if (this.config.autoRefresh && refreshAt > 0) {
209
- this.refreshTimer = setTimeout(() => {
210
- this.log("Scheduled token refresh triggered");
211
- this.refreshToken();
212
- }, refreshAt);
213
- } else if (refreshAt <= 0 && this.config.autoRefresh) {
214
- this.log("Token near expiry, refreshing immediately");
232
+ this.refreshTimer = setTimeout(() => {
233
+ this.log("Token refresh threshold reached, triggering refresh");
215
234
  this.refreshToken();
216
- }
235
+ }, refreshAt);
236
+ this.log(`Token refresh scheduled in ${refreshAt}ms`);
217
237
  }
218
238
  /**
219
- * Setup idle activity tracking
239
+ * Setup idle tracking with activity listeners
220
240
  */
221
241
  setupIdleTracking() {
222
- const events = ["mousedown", "keydown", "scroll", "touchstart", "click"];
242
+ if (!this.config.trackIdleTime)
243
+ return;
223
244
  const handleActivity = () => {
224
245
  this.state.lastActivityTime = Date.now();
225
246
  this.state.isIdle = false;
226
247
  this.emit("activity");
227
- if (this.idleTimer) {
228
- clearTimeout(this.idleTimer);
229
- }
230
- this.idleTimer = setTimeout(() => {
231
- var _a, _b;
232
- this.state.isIdle = true;
233
- this.emit("idle");
234
- this.log("User idle timeout triggered");
235
- (_b = (_a = this.config).onIdle) == null ? void 0 : _b.call(_a);
236
- }, this.config.idleTimeoutMs);
237
248
  };
249
+ const events = ["mousedown", "keydown", "scroll", "touchstart", "click"];
238
250
  events.forEach((event) => {
239
- window.addEventListener(event, handleActivity, { passive: true });
251
+ window.addEventListener(event, handleActivity, true);
240
252
  });
253
+ if (this.idleTimer) {
254
+ clearTimeout(this.idleTimer);
255
+ }
241
256
  this.idleTimer = setTimeout(() => {
242
257
  var _a, _b;
243
258
  this.state.isIdle = true;
@@ -269,7 +284,7 @@ var SessionManager = class {
269
284
  }, this.config.maxSessionDurationMs);
270
285
  }
271
286
  /**
272
- * Refresh token
287
+ * Refresh token using stored refresh token
273
288
  */
274
289
  async refreshToken() {
275
290
  var _a, _b, _c, _d;
@@ -279,23 +294,49 @@ var SessionManager = class {
279
294
  }
280
295
  this.state.isRefreshing = true;
281
296
  this.state.refreshAttempts++;
282
- const token = getStoredToken();
283
- if (!token) {
297
+ const accessToken = getStoredToken();
298
+ const refreshToken = getStoredRefreshToken();
299
+ if (!accessToken && !refreshToken) {
284
300
  this.state.isRefreshing = false;
285
301
  return false;
286
302
  }
287
303
  try {
288
304
  this.log(`Refreshing token (attempt ${this.state.refreshAttempts})`);
289
305
  const client = this.config.httpClient || defaultFetchClient();
290
- const refreshPayload = this.config.refreshPayloadFormatter ? this.config.refreshPayloadFormatter(token) : { token };
291
- const refreshPromise = client.post(this.config.refreshEndpoint, refreshPayload, { headers: { Authorization: `Bearer ${token}` } });
306
+ const refreshPayload = this.config.refreshPayloadFormatter ? this.config.refreshPayloadFormatter(accessToken || "", refreshToken || "") : {
307
+ refresh: refreshToken || accessToken,
308
+ token: accessToken
309
+ };
310
+ const refreshPromise = client.post(
311
+ this.config.refreshEndpoint,
312
+ refreshPayload,
313
+ {
314
+ headers: {
315
+ Authorization: `Bearer ${accessToken || refreshToken}`
316
+ }
317
+ }
318
+ );
292
319
  this.requestDeduplication.set("refresh", refreshPromise);
293
320
  const response = await refreshPromise;
294
- const newToken = response.data.token || response.data.access_token;
295
- if (!newToken) {
296
- throw new Error("No token in refresh response");
321
+ const responseData = response.data;
322
+ const newAccessToken = extractTokenFromResponse(
323
+ responseData,
324
+ this.config.refreshAccessTokenField || "access"
325
+ );
326
+ if (!newAccessToken) {
327
+ throw new Error("No access token in refresh response");
328
+ }
329
+ storeToken(newAccessToken);
330
+ if (this.config.storeRefreshToken) {
331
+ const newRefreshToken = extractTokenFromResponse(
332
+ responseData,
333
+ this.config.refreshRefreshTokenField || "refresh"
334
+ );
335
+ if (newRefreshToken) {
336
+ storeRefreshToken(newRefreshToken);
337
+ this.log("Refresh token updated");
338
+ }
297
339
  }
298
- storeToken(newToken);
299
340
  this.state.isRefreshing = false;
300
341
  this.state.refreshAttempts = 0;
301
342
  this.requestDeduplication.delete("refresh");
@@ -317,41 +358,34 @@ var SessionManager = class {
317
358
  }
318
359
  }
319
360
  /**
320
- * Logout and cleanup
361
+ * Manual logout
321
362
  */
322
363
  async logout() {
323
364
  var _a, _b;
365
+ this.log("Logging out");
324
366
  try {
325
367
  const token = getStoredToken();
326
- if (token) {
368
+ if (token && this.config.logoutEndpoint) {
327
369
  const client = this.config.httpClient || defaultFetchClient();
328
370
  const logoutPayload = this.config.logoutPayloadFormatter ? this.config.logoutPayloadFormatter(token) : { token };
329
- await client.post(this.config.logoutEndpoint, logoutPayload, { headers: { Authorization: `Bearer ${token}` } });
371
+ await client.post(this.config.logoutEndpoint, logoutPayload, {
372
+ headers: { Authorization: `Bearer ${token}` }
373
+ });
330
374
  }
331
375
  } catch (error) {
332
- this.log("Logout API call failed:", error);
376
+ this.log("Logout request failed:", error);
333
377
  }
334
- this.cleanup();
335
- this.state.isActive = false;
336
- this.emit("loggedOut");
337
- (_b = (_a = this.config).onSessionExpired) == null ? void 0 : _b.call(_a);
338
378
  clearToken();
379
+ this.state.isActive = false;
380
+ this.emit("logout");
381
+ (_b = (_a = this.config).onLogout) == null ? void 0 : _b.call(_a);
382
+ this.cleanup();
339
383
  }
340
384
  /**
341
- * Extend session (reset idle timer)
385
+ * Check if session is active
342
386
  */
343
- extendSession() {
344
- this.state.lastActivityTime = Date.now();
345
- this.state.isIdle = false;
346
- if (this.idleTimer) {
347
- clearTimeout(this.idleTimer);
348
- }
349
- this.idleTimer = setTimeout(() => {
350
- this.state.isIdle = true;
351
- this.emit("idle");
352
- }, this.config.idleTimeoutMs);
353
- this.log("Session extended");
354
- this.emit("sessionExtended");
387
+ isActive() {
388
+ return this.state.isActive;
355
389
  }
356
390
  /**
357
391
  * Get current state
@@ -360,13 +394,7 @@ var SessionManager = class {
360
394
  return { ...this.state };
361
395
  }
362
396
  /**
363
- * Get current config
364
- */
365
- getConfig() {
366
- return { ...this.config };
367
- }
368
- /**
369
- * Update config
397
+ * Update config at runtime
370
398
  */
371
399
  updateConfig(newConfig) {
372
400
  this.config = { ...this.config, ...newConfig };
@@ -459,119 +487,97 @@ function resetSessionManager() {
459
487
  // hooks/useSessionTimeout.ts
460
488
  var import_react = require("react");
461
489
  function useSessionTimeout(options = {}) {
462
- const {
463
- config,
464
- enabled = true,
465
- onSessionExpiring,
466
- onSessionExpired,
467
- onIdle,
468
- onRefreshSuccess
469
- } = options;
470
- const [sessionState, setSessionState] = (0, import_react.useState)({
490
+ const [state, setState] = (0, import_react.useState)({
471
491
  isActive: false,
472
492
  isIdle: false,
473
493
  lastActivityTime: Date.now(),
474
494
  refreshAttempts: 0,
475
495
  isRefreshing: false
476
496
  });
477
- const [timeUntilIdle, setTimeUntilIdle] = (0, import_react.useState)(null);
478
- const [idleWarningVisible, setIdleWarningVisible] = (0, import_react.useState)(false);
497
+ const [error, setError] = (0, import_react.useState)(null);
498
+ const [timeUntilTimeout, setTimeUntilTimeout] = (0, import_react.useState)(0);
499
+ const [timeUntilIdle, setTimeUntilIdle] = (0, import_react.useState)(0);
479
500
  const managerRef = (0, import_react.useRef)(null);
480
- const unsubscribeRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
501
+ const updateTimerRef = (0, import_react.useRef)(null);
481
502
  (0, import_react.useEffect)(() => {
482
- if (!enabled)
483
- return;
484
- const manager = getSessionManager({
485
- ...config,
486
- onSessionExpiring,
487
- onSessionExpired,
488
- onIdle,
489
- onRefreshSuccess
490
- });
503
+ const { loginResponse, autoInit, ...config } = options;
504
+ const manager = getSessionManager(config);
491
505
  managerRef.current = manager;
492
- const subscriptions = /* @__PURE__ */ new Map();
493
- subscriptions.set(
494
- "initialized",
495
- manager.on("initialized", () => {
496
- setSessionState(manager.getState());
497
- })
498
- );
499
- subscriptions.set(
500
- "tokenRefreshed",
501
- manager.on("tokenRefreshed", () => {
502
- setSessionState(manager.getState());
503
- })
504
- );
505
- subscriptions.set(
506
- "idle",
507
- manager.on("idle", () => {
508
- setSessionState(manager.getState());
509
- })
510
- );
511
- subscriptions.set(
512
- "activity",
513
- manager.on("activity", () => {
514
- setSessionState(manager.getState());
515
- setIdleWarningVisible(false);
516
- })
517
- );
518
- subscriptions.set(
519
- "idleWarning",
520
- manager.on("idleWarning", (data) => {
521
- setIdleWarningVisible(true);
522
- setTimeUntilIdle(data.timeRemaining);
523
- })
524
- );
525
- subscriptions.set(
526
- "loggedOut",
527
- manager.on("loggedOut", () => {
528
- setSessionState(manager.getState());
529
- })
530
- );
531
- unsubscribeRef.current = subscriptions;
532
- manager.init();
506
+ const unsubscribeInitialized = manager.on("initialized", () => {
507
+ setState(manager.getState());
508
+ });
509
+ const unsubscribeTokenRefreshed = manager.on("tokenRefreshed", () => {
510
+ setState(manager.getState());
511
+ });
512
+ const unsubscribeRefreshFailed = manager.on("refreshFailed", (error2) => {
513
+ setError(error2);
514
+ setState(manager.getState());
515
+ });
516
+ const unsubscribeLogout = manager.on("logout", () => {
517
+ setState(manager.getState());
518
+ });
519
+ const unsubscribeIdle = manager.on("idle", () => {
520
+ setState(manager.getState());
521
+ });
522
+ const unsubscribeIdleWarning = manager.on("idleWarning", (data) => {
523
+ setTimeUntilIdle(data.timeRemaining);
524
+ });
525
+ const unsubscribeActivity = manager.on("activity", () => {
526
+ setTimeUntilIdle(0);
527
+ });
528
+ if (autoInit !== false && (autoInit === true || loginResponse)) {
529
+ manager.init(loginResponse);
530
+ }
533
531
  return () => {
534
- subscriptions.forEach((unsub) => unsub());
535
- unsubscribeRef.current.clear();
532
+ unsubscribeInitialized();
533
+ unsubscribeTokenRefreshed();
534
+ unsubscribeRefreshFailed();
535
+ unsubscribeLogout();
536
+ unsubscribeIdle();
537
+ unsubscribeIdleWarning();
538
+ unsubscribeActivity();
536
539
  };
537
- }, [enabled, config, onSessionExpiring, onSessionExpired, onIdle, onRefreshSuccess]);
540
+ }, [options]);
538
541
  (0, import_react.useEffect)(() => {
539
- if (!enabled || !sessionState.isActive)
542
+ var _a;
543
+ if (!((_a = managerRef.current) == null ? void 0 : _a.isActive())) {
540
544
  return;
541
- const interval = setInterval(() => {
542
- const manager = managerRef.current;
543
- if (manager) {
544
- setSessionState(manager.getState());
545
+ }
546
+ const updateCountdown = () => {
547
+ setState(managerRef.current.getState());
548
+ };
549
+ updateTimerRef.current = setInterval(updateCountdown, 1e3);
550
+ return () => {
551
+ if (updateTimerRef.current) {
552
+ clearInterval(updateTimerRef.current);
545
553
  }
546
- }, 5e3);
547
- return () => clearInterval(interval);
548
- }, [enabled, sessionState.isActive]);
549
- const extendSession = (0, import_react.useCallback)(() => {
550
- var _a;
551
- (_a = managerRef.current) == null ? void 0 : _a.extendSession();
554
+ };
552
555
  }, []);
553
556
  const refreshToken = (0, import_react.useCallback)(async () => {
554
- var _a;
555
- return (_a = managerRef.current) == null ? void 0 : _a.refreshToken();
557
+ if (managerRef.current) {
558
+ return managerRef.current.refreshToken();
559
+ }
560
+ return false;
556
561
  }, []);
557
562
  const logout = (0, import_react.useCallback)(async () => {
558
- var _a;
559
- return (_a = managerRef.current) == null ? void 0 : _a.logout();
563
+ if (managerRef.current) {
564
+ return managerRef.current.logout();
565
+ }
560
566
  }, []);
561
- const updateConfig = (0, import_react.useCallback)((newConfig) => {
562
- var _a;
563
- (_a = managerRef.current) == null ? void 0 : _a.updateConfig(newConfig);
567
+ const dismissIdleWarning = (0, import_react.useCallback)(() => {
568
+ setTimeUntilIdle(0);
564
569
  }, []);
565
570
  return {
566
- sessionState,
567
- timeUntilExpiry: null,
571
+ isActive: state.isActive,
572
+ isIdle: state.isIdle,
573
+ isRefreshing: state.isRefreshing,
574
+ refreshAttempts: state.refreshAttempts,
575
+ timeUntilTimeout,
568
576
  timeUntilIdle,
569
- idleWarningVisible,
570
- extendSession,
577
+ error,
571
578
  refreshToken,
572
579
  logout,
573
- updateConfig,
574
- manager: managerRef.current
580
+ dismissIdleWarning
575
581
  };
576
582
  }
577
583
 
@@ -623,11 +629,13 @@ function SessionStatus() {
623
629
  SessionStatus,
624
630
  clearToken,
625
631
  getSessionManager,
632
+ getStoredRefreshToken,
626
633
  getStoredToken,
627
634
  getTimeUntilExpiry,
628
635
  getTokenInfo,
629
636
  isTokenExpired,
630
637
  resetSessionManager,
638
+ storeRefreshToken,
631
639
  storeToken,
632
640
  useSessionTimeout,
633
641
  validateToken
package/dist/index.mjs CHANGED
@@ -10,11 +10,17 @@ var DEFAULT_SESSION_CONFIG = {
10
10
  // 8 hours max
11
11
  refreshEndpoint: "/auth/refresh/",
12
12
  logoutEndpoint: "/auth/logout/",
13
+ trackIdleTime: true,
13
14
  showIdleWarning: true,
14
15
  idleWarningThresholdMs: 2 * 60 * 1e3,
15
16
  // 2 minutes before idle timeout
16
17
  autoRefresh: true,
17
- debug: false
18
+ debug: false,
19
+ accessTokenField: "access",
20
+ refreshTokenField: "refresh",
21
+ refreshAccessTokenField: "access",
22
+ refreshRefreshTokenField: "refresh",
23
+ storeRefreshToken: true
18
24
  };
19
25
 
20
26
  // services/tokenService.ts
@@ -64,34 +70,25 @@ function getTimeUntilExpiry(token) {
64
70
  function getStoredToken() {
65
71
  return sessionStorage.getItem("authToken") || localStorage.getItem("authToken");
66
72
  }
73
+ function getStoredRefreshToken() {
74
+ return sessionStorage.getItem("refreshToken") || localStorage.getItem("refreshToken");
75
+ }
67
76
  function storeToken(token, persistent = false) {
68
- if (persistent) {
69
- localStorage.setItem("authToken", token);
70
- sessionStorage.removeItem("authToken");
71
- } else {
72
- sessionStorage.setItem("authToken", token);
73
- localStorage.removeItem("authToken");
74
- }
77
+ const storage = persistent ? localStorage : sessionStorage;
78
+ storage.setItem("authToken", token);
79
+ }
80
+ function storeRefreshToken(token, persistent = false) {
81
+ const storage = persistent ? localStorage : sessionStorage;
82
+ storage.setItem("refreshToken", token);
75
83
  }
76
84
  function clearToken() {
77
85
  sessionStorage.removeItem("authToken");
78
86
  localStorage.removeItem("authToken");
87
+ sessionStorage.removeItem("refreshToken");
88
+ localStorage.removeItem("refreshToken");
79
89
  }
80
90
  function validateToken(token) {
81
- if (!token || typeof token !== "string") {
82
- return { valid: false, error: "Token is missing or invalid" };
83
- }
84
- const payload = decodeJWT(token);
85
- if (!payload) {
86
- return { valid: false, error: "Failed to decode token" };
87
- }
88
- if (!payload.exp) {
89
- return { valid: false, error: "Token missing expiry time" };
90
- }
91
- if (isTokenExpired(token)) {
92
- return { valid: false, error: "Token has expired" };
93
- }
94
- return { valid: true };
91
+ return decodeJWT(token) !== null;
95
92
  }
96
93
 
97
94
  // services/sessionManager.ts
@@ -112,6 +109,20 @@ function defaultFetchClient() {
112
109
  }
113
110
  };
114
111
  }
112
+ function extractTokenFromResponse(response, fieldName = "access") {
113
+ if (!response)
114
+ return null;
115
+ const parts = fieldName.split(".");
116
+ let value = response;
117
+ for (const part of parts) {
118
+ if (value && typeof value === "object") {
119
+ value = value[part];
120
+ } else {
121
+ return null;
122
+ }
123
+ }
124
+ return typeof value === "string" ? value : null;
125
+ }
115
126
  var SessionManager = class {
116
127
  constructor(config) {
117
128
  this.refreshTimer = null;
@@ -134,17 +145,29 @@ var SessionManager = class {
134
145
  this.log("SessionManager initialized with config:", this.config);
135
146
  }
136
147
  /**
137
- * Initialize session management
148
+ * Initialize session management with optional login response
149
+ * Extracts and stores tokens from login response using configured field names
138
150
  */
139
- init() {
140
- const token = getStoredToken();
151
+ init(loginResponse) {
152
+ const token = loginResponse ? extractTokenFromResponse(loginResponse, this.config.accessTokenField || "access") : getStoredToken();
141
153
  if (!token) {
142
154
  this.log("No token found, session not initialized");
143
155
  return;
144
156
  }
157
+ storeToken(token);
158
+ if (this.config.storeRefreshToken && loginResponse) {
159
+ const refreshToken = extractTokenFromResponse(
160
+ loginResponse,
161
+ this.config.refreshTokenField || "refresh"
162
+ );
163
+ if (refreshToken) {
164
+ storeRefreshToken(refreshToken);
165
+ this.log("Refresh token stored");
166
+ }
167
+ }
145
168
  const validation = validateToken(token);
146
- if (!validation.valid) {
147
- this.log("Invalid token:", validation.error);
169
+ if (!validation) {
170
+ this.log("Invalid token");
148
171
  this.logout();
149
172
  return;
150
173
  }
@@ -164,43 +187,33 @@ var SessionManager = class {
164
187
  return;
165
188
  const timeUntilExpiry = getTimeUntilExpiry(token);
166
189
  const refreshAt = Math.max(0, timeUntilExpiry - this.config.refreshThresholdMs);
167
- this.log(`Token expires in ${timeUntilExpiry}ms, will refresh in ${refreshAt}ms`);
168
190
  if (this.refreshTimer) {
169
191
  clearTimeout(this.refreshTimer);
170
192
  }
171
- if (this.config.autoRefresh && refreshAt > 0) {
172
- this.refreshTimer = setTimeout(() => {
173
- this.log("Scheduled token refresh triggered");
174
- this.refreshToken();
175
- }, refreshAt);
176
- } else if (refreshAt <= 0 && this.config.autoRefresh) {
177
- this.log("Token near expiry, refreshing immediately");
193
+ this.refreshTimer = setTimeout(() => {
194
+ this.log("Token refresh threshold reached, triggering refresh");
178
195
  this.refreshToken();
179
- }
196
+ }, refreshAt);
197
+ this.log(`Token refresh scheduled in ${refreshAt}ms`);
180
198
  }
181
199
  /**
182
- * Setup idle activity tracking
200
+ * Setup idle tracking with activity listeners
183
201
  */
184
202
  setupIdleTracking() {
185
- const events = ["mousedown", "keydown", "scroll", "touchstart", "click"];
203
+ if (!this.config.trackIdleTime)
204
+ return;
186
205
  const handleActivity = () => {
187
206
  this.state.lastActivityTime = Date.now();
188
207
  this.state.isIdle = false;
189
208
  this.emit("activity");
190
- if (this.idleTimer) {
191
- clearTimeout(this.idleTimer);
192
- }
193
- this.idleTimer = setTimeout(() => {
194
- var _a, _b;
195
- this.state.isIdle = true;
196
- this.emit("idle");
197
- this.log("User idle timeout triggered");
198
- (_b = (_a = this.config).onIdle) == null ? void 0 : _b.call(_a);
199
- }, this.config.idleTimeoutMs);
200
209
  };
210
+ const events = ["mousedown", "keydown", "scroll", "touchstart", "click"];
201
211
  events.forEach((event) => {
202
- window.addEventListener(event, handleActivity, { passive: true });
212
+ window.addEventListener(event, handleActivity, true);
203
213
  });
214
+ if (this.idleTimer) {
215
+ clearTimeout(this.idleTimer);
216
+ }
204
217
  this.idleTimer = setTimeout(() => {
205
218
  var _a, _b;
206
219
  this.state.isIdle = true;
@@ -232,7 +245,7 @@ var SessionManager = class {
232
245
  }, this.config.maxSessionDurationMs);
233
246
  }
234
247
  /**
235
- * Refresh token
248
+ * Refresh token using stored refresh token
236
249
  */
237
250
  async refreshToken() {
238
251
  var _a, _b, _c, _d;
@@ -242,23 +255,49 @@ var SessionManager = class {
242
255
  }
243
256
  this.state.isRefreshing = true;
244
257
  this.state.refreshAttempts++;
245
- const token = getStoredToken();
246
- if (!token) {
258
+ const accessToken = getStoredToken();
259
+ const refreshToken = getStoredRefreshToken();
260
+ if (!accessToken && !refreshToken) {
247
261
  this.state.isRefreshing = false;
248
262
  return false;
249
263
  }
250
264
  try {
251
265
  this.log(`Refreshing token (attempt ${this.state.refreshAttempts})`);
252
266
  const client = this.config.httpClient || defaultFetchClient();
253
- const refreshPayload = this.config.refreshPayloadFormatter ? this.config.refreshPayloadFormatter(token) : { token };
254
- const refreshPromise = client.post(this.config.refreshEndpoint, refreshPayload, { headers: { Authorization: `Bearer ${token}` } });
267
+ const refreshPayload = this.config.refreshPayloadFormatter ? this.config.refreshPayloadFormatter(accessToken || "", refreshToken || "") : {
268
+ refresh: refreshToken || accessToken,
269
+ token: accessToken
270
+ };
271
+ const refreshPromise = client.post(
272
+ this.config.refreshEndpoint,
273
+ refreshPayload,
274
+ {
275
+ headers: {
276
+ Authorization: `Bearer ${accessToken || refreshToken}`
277
+ }
278
+ }
279
+ );
255
280
  this.requestDeduplication.set("refresh", refreshPromise);
256
281
  const response = await refreshPromise;
257
- const newToken = response.data.token || response.data.access_token;
258
- if (!newToken) {
259
- throw new Error("No token in refresh response");
282
+ const responseData = response.data;
283
+ const newAccessToken = extractTokenFromResponse(
284
+ responseData,
285
+ this.config.refreshAccessTokenField || "access"
286
+ );
287
+ if (!newAccessToken) {
288
+ throw new Error("No access token in refresh response");
289
+ }
290
+ storeToken(newAccessToken);
291
+ if (this.config.storeRefreshToken) {
292
+ const newRefreshToken = extractTokenFromResponse(
293
+ responseData,
294
+ this.config.refreshRefreshTokenField || "refresh"
295
+ );
296
+ if (newRefreshToken) {
297
+ storeRefreshToken(newRefreshToken);
298
+ this.log("Refresh token updated");
299
+ }
260
300
  }
261
- storeToken(newToken);
262
301
  this.state.isRefreshing = false;
263
302
  this.state.refreshAttempts = 0;
264
303
  this.requestDeduplication.delete("refresh");
@@ -280,41 +319,34 @@ var SessionManager = class {
280
319
  }
281
320
  }
282
321
  /**
283
- * Logout and cleanup
322
+ * Manual logout
284
323
  */
285
324
  async logout() {
286
325
  var _a, _b;
326
+ this.log("Logging out");
287
327
  try {
288
328
  const token = getStoredToken();
289
- if (token) {
329
+ if (token && this.config.logoutEndpoint) {
290
330
  const client = this.config.httpClient || defaultFetchClient();
291
331
  const logoutPayload = this.config.logoutPayloadFormatter ? this.config.logoutPayloadFormatter(token) : { token };
292
- await client.post(this.config.logoutEndpoint, logoutPayload, { headers: { Authorization: `Bearer ${token}` } });
332
+ await client.post(this.config.logoutEndpoint, logoutPayload, {
333
+ headers: { Authorization: `Bearer ${token}` }
334
+ });
293
335
  }
294
336
  } catch (error) {
295
- this.log("Logout API call failed:", error);
337
+ this.log("Logout request failed:", error);
296
338
  }
297
- this.cleanup();
298
- this.state.isActive = false;
299
- this.emit("loggedOut");
300
- (_b = (_a = this.config).onSessionExpired) == null ? void 0 : _b.call(_a);
301
339
  clearToken();
340
+ this.state.isActive = false;
341
+ this.emit("logout");
342
+ (_b = (_a = this.config).onLogout) == null ? void 0 : _b.call(_a);
343
+ this.cleanup();
302
344
  }
303
345
  /**
304
- * Extend session (reset idle timer)
346
+ * Check if session is active
305
347
  */
306
- extendSession() {
307
- this.state.lastActivityTime = Date.now();
308
- this.state.isIdle = false;
309
- if (this.idleTimer) {
310
- clearTimeout(this.idleTimer);
311
- }
312
- this.idleTimer = setTimeout(() => {
313
- this.state.isIdle = true;
314
- this.emit("idle");
315
- }, this.config.idleTimeoutMs);
316
- this.log("Session extended");
317
- this.emit("sessionExtended");
348
+ isActive() {
349
+ return this.state.isActive;
318
350
  }
319
351
  /**
320
352
  * Get current state
@@ -323,13 +355,7 @@ var SessionManager = class {
323
355
  return { ...this.state };
324
356
  }
325
357
  /**
326
- * Get current config
327
- */
328
- getConfig() {
329
- return { ...this.config };
330
- }
331
- /**
332
- * Update config
358
+ * Update config at runtime
333
359
  */
334
360
  updateConfig(newConfig) {
335
361
  this.config = { ...this.config, ...newConfig };
@@ -422,119 +448,97 @@ function resetSessionManager() {
422
448
  // hooks/useSessionTimeout.ts
423
449
  import { useEffect, useState, useCallback, useRef } from "react";
424
450
  function useSessionTimeout(options = {}) {
425
- const {
426
- config,
427
- enabled = true,
428
- onSessionExpiring,
429
- onSessionExpired,
430
- onIdle,
431
- onRefreshSuccess
432
- } = options;
433
- const [sessionState, setSessionState] = useState({
451
+ const [state, setState] = useState({
434
452
  isActive: false,
435
453
  isIdle: false,
436
454
  lastActivityTime: Date.now(),
437
455
  refreshAttempts: 0,
438
456
  isRefreshing: false
439
457
  });
440
- const [timeUntilIdle, setTimeUntilIdle] = useState(null);
441
- const [idleWarningVisible, setIdleWarningVisible] = useState(false);
458
+ const [error, setError] = useState(null);
459
+ const [timeUntilTimeout, setTimeUntilTimeout] = useState(0);
460
+ const [timeUntilIdle, setTimeUntilIdle] = useState(0);
442
461
  const managerRef = useRef(null);
443
- const unsubscribeRef = useRef(/* @__PURE__ */ new Map());
462
+ const updateTimerRef = useRef(null);
444
463
  useEffect(() => {
445
- if (!enabled)
446
- return;
447
- const manager = getSessionManager({
448
- ...config,
449
- onSessionExpiring,
450
- onSessionExpired,
451
- onIdle,
452
- onRefreshSuccess
453
- });
464
+ const { loginResponse, autoInit, ...config } = options;
465
+ const manager = getSessionManager(config);
454
466
  managerRef.current = manager;
455
- const subscriptions = /* @__PURE__ */ new Map();
456
- subscriptions.set(
457
- "initialized",
458
- manager.on("initialized", () => {
459
- setSessionState(manager.getState());
460
- })
461
- );
462
- subscriptions.set(
463
- "tokenRefreshed",
464
- manager.on("tokenRefreshed", () => {
465
- setSessionState(manager.getState());
466
- })
467
- );
468
- subscriptions.set(
469
- "idle",
470
- manager.on("idle", () => {
471
- setSessionState(manager.getState());
472
- })
473
- );
474
- subscriptions.set(
475
- "activity",
476
- manager.on("activity", () => {
477
- setSessionState(manager.getState());
478
- setIdleWarningVisible(false);
479
- })
480
- );
481
- subscriptions.set(
482
- "idleWarning",
483
- manager.on("idleWarning", (data) => {
484
- setIdleWarningVisible(true);
485
- setTimeUntilIdle(data.timeRemaining);
486
- })
487
- );
488
- subscriptions.set(
489
- "loggedOut",
490
- manager.on("loggedOut", () => {
491
- setSessionState(manager.getState());
492
- })
493
- );
494
- unsubscribeRef.current = subscriptions;
495
- manager.init();
467
+ const unsubscribeInitialized = manager.on("initialized", () => {
468
+ setState(manager.getState());
469
+ });
470
+ const unsubscribeTokenRefreshed = manager.on("tokenRefreshed", () => {
471
+ setState(manager.getState());
472
+ });
473
+ const unsubscribeRefreshFailed = manager.on("refreshFailed", (error2) => {
474
+ setError(error2);
475
+ setState(manager.getState());
476
+ });
477
+ const unsubscribeLogout = manager.on("logout", () => {
478
+ setState(manager.getState());
479
+ });
480
+ const unsubscribeIdle = manager.on("idle", () => {
481
+ setState(manager.getState());
482
+ });
483
+ const unsubscribeIdleWarning = manager.on("idleWarning", (data) => {
484
+ setTimeUntilIdle(data.timeRemaining);
485
+ });
486
+ const unsubscribeActivity = manager.on("activity", () => {
487
+ setTimeUntilIdle(0);
488
+ });
489
+ if (autoInit !== false && (autoInit === true || loginResponse)) {
490
+ manager.init(loginResponse);
491
+ }
496
492
  return () => {
497
- subscriptions.forEach((unsub) => unsub());
498
- unsubscribeRef.current.clear();
493
+ unsubscribeInitialized();
494
+ unsubscribeTokenRefreshed();
495
+ unsubscribeRefreshFailed();
496
+ unsubscribeLogout();
497
+ unsubscribeIdle();
498
+ unsubscribeIdleWarning();
499
+ unsubscribeActivity();
499
500
  };
500
- }, [enabled, config, onSessionExpiring, onSessionExpired, onIdle, onRefreshSuccess]);
501
+ }, [options]);
501
502
  useEffect(() => {
502
- if (!enabled || !sessionState.isActive)
503
+ var _a;
504
+ if (!((_a = managerRef.current) == null ? void 0 : _a.isActive())) {
503
505
  return;
504
- const interval = setInterval(() => {
505
- const manager = managerRef.current;
506
- if (manager) {
507
- setSessionState(manager.getState());
506
+ }
507
+ const updateCountdown = () => {
508
+ setState(managerRef.current.getState());
509
+ };
510
+ updateTimerRef.current = setInterval(updateCountdown, 1e3);
511
+ return () => {
512
+ if (updateTimerRef.current) {
513
+ clearInterval(updateTimerRef.current);
508
514
  }
509
- }, 5e3);
510
- return () => clearInterval(interval);
511
- }, [enabled, sessionState.isActive]);
512
- const extendSession = useCallback(() => {
513
- var _a;
514
- (_a = managerRef.current) == null ? void 0 : _a.extendSession();
515
+ };
515
516
  }, []);
516
517
  const refreshToken = useCallback(async () => {
517
- var _a;
518
- return (_a = managerRef.current) == null ? void 0 : _a.refreshToken();
518
+ if (managerRef.current) {
519
+ return managerRef.current.refreshToken();
520
+ }
521
+ return false;
519
522
  }, []);
520
523
  const logout = useCallback(async () => {
521
- var _a;
522
- return (_a = managerRef.current) == null ? void 0 : _a.logout();
524
+ if (managerRef.current) {
525
+ return managerRef.current.logout();
526
+ }
523
527
  }, []);
524
- const updateConfig = useCallback((newConfig) => {
525
- var _a;
526
- (_a = managerRef.current) == null ? void 0 : _a.updateConfig(newConfig);
528
+ const dismissIdleWarning = useCallback(() => {
529
+ setTimeUntilIdle(0);
527
530
  }, []);
528
531
  return {
529
- sessionState,
530
- timeUntilExpiry: null,
532
+ isActive: state.isActive,
533
+ isIdle: state.isIdle,
534
+ isRefreshing: state.isRefreshing,
535
+ refreshAttempts: state.refreshAttempts,
536
+ timeUntilTimeout,
531
537
  timeUntilIdle,
532
- idleWarningVisible,
533
- extendSession,
538
+ error,
534
539
  refreshToken,
535
540
  logout,
536
- updateConfig,
537
- manager: managerRef.current
541
+ dismissIdleWarning
538
542
  };
539
543
  }
540
544
 
@@ -585,11 +589,13 @@ export {
585
589
  SessionStatus,
586
590
  clearToken,
587
591
  getSessionManager,
592
+ getStoredRefreshToken,
588
593
  getStoredToken,
589
594
  getTimeUntilExpiry,
590
595
  getTokenInfo,
591
596
  isTokenExpired,
592
597
  resetSessionManager,
598
+ storeRefreshToken,
593
599
  storeToken,
594
600
  useSessionTimeout,
595
601
  validateToken
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "formreader-session-timeout",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Session timeout microfrontend: configurable JWT expiry decoding and refresh with idle tracking",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",