@mapnests/gateway-web-sdk 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,7 +43,7 @@ This setup covers bootstrap on app start and three API patterns:
43
43
  import React from 'react';
44
44
  import ReactDOM from 'react-dom/client';
45
45
  import { SessionManager } from 'gateway-web-sdk';
46
- import { API_BASE_URL, BOOTSTRAP_PATH, TOKEN_COOKIE_NAME, REFRESH_INTERVAL, TOKEN_EXPIRY, CREDENTIALS } from './config.js';
46
+ import { API_BASE_URL, BOOTSTRAP_PATH, TOKEN_COOKIE_NAME } from './config.js';
47
47
  import App from './App';
48
48
 
49
49
  const sessionManager = SessionManager.getInstance();
@@ -51,9 +51,6 @@ try {
51
51
  sessionManager.configure({
52
52
  bootstrapUrl: `${API_BASE_URL}${BOOTSTRAP_PATH}`,
53
53
  tokenCookieName: TOKEN_COOKIE_NAME,
54
- refreshInterval: (REFRESH_INTERVAL ? parseInt(REFRESH_INTERVAL) : 25) * 60 * 1000,
55
- tokenExpiry: (TOKEN_EXPIRY ? parseInt(TOKEN_EXPIRY) : 30) * 60 * 1000,
56
- credentials: CREDENTIALS === 'true',
57
54
  });
58
55
  } catch (error) {
59
56
  console.error('Failed to configure session manager:', error);
@@ -100,7 +97,14 @@ async function manual(url, init = {}) {
100
97
  try {
101
98
  const data = await cloned.json();
102
99
  if (data.error_msg === 'INVALID_SESSION') {
103
- if (sm.isRefreshing()) await sm.waitForRefresh(); else await sm.refreshToken();
100
+ if (sm.isRefreshing()) {
101
+ await sm.waitForRefresh();
102
+ } else {
103
+ const existingToken = sm.getToken(sm.config.tokenCookieName);
104
+ if (!existingToken) {
105
+ await sm.refreshToken();
106
+ }
107
+ }
104
108
  opts.headers['cf-session-id'] = sm.getSessionId();
105
109
  if (sm.shouldUseTokenHeader()) {
106
110
  const nt = sm.getToken(sm.config.tokenCookieName);
@@ -215,12 +219,9 @@ import App from './App';
215
219
  const sessionManager = SessionManager.getInstance();
216
220
  sessionManager.configure({
217
221
  bootstrapUrl: 'https://your-api.com/session/bootstrap',
218
- refreshInterval: 25 * 60 * 1000, // 25 minutes
219
- tokenExpiry: 30 * 60 * 1000, // 30 minutes
220
222
  headers: {
221
223
  'X-Custom-Header': 'value',
222
224
  },
223
- credentials: true, // Include cookies in requests
224
225
  });
225
226
 
226
227
  ReactDOM.createRoot(document.getElementById('root')).render(
@@ -245,7 +246,6 @@ export default function RootLayout({ children }) {
245
246
  const sessionManager = SessionManager.getInstance();
246
247
  sessionManager.configure({
247
248
  bootstrapUrl: '/api/session/bootstrap',
248
- refreshInterval: 25 * 60 * 1000,
249
249
  });
250
250
 
251
251
  sessionManager.initialize();
@@ -270,7 +270,6 @@ function MyApp({ Component, pageProps }) {
270
270
  const sessionManager = SessionManager.getInstance();
271
271
  sessionManager.configure({
272
272
  bootstrapUrl: '/api/session/bootstrap',
273
- refreshInterval: 25 * 60 * 1000,
274
273
  });
275
274
 
276
275
  sessionManager.initialize();
@@ -323,14 +322,14 @@ Configure the session manager.
323
322
  sessionManager.configure({
324
323
  bootstrapUrl: '/session/bootstrap', // Required: Bootstrap API endpoint
325
324
  tokenCookieName: 'stoken', // Optional: Custom token cookie name
326
- refreshInterval: 25 * 60 * 1000, // Optional: Refresh interval (ms)
327
- tokenExpiry: 30 * 60 * 1000, // Optional: Token expiry (ms)
328
325
  maxRetries: 3, // Optional: Max retry attempts
329
326
  headers: {}, // Optional: Additional headers
330
- credentials: true, // Optional: Include credentials
327
+ credentials: true, // Optional: Include credentials (default: true)
331
328
  });
332
329
  ```
333
330
 
331
+ Note: `refreshInterval` and `tokenExpiry` are automatically set by the server's bootstrap response (`refresh_time` and `expire` fields). You don't need to configure them manually.
332
+
334
333
  ##### `initialize()`
335
334
 
336
335
  Initialize session by calling bootstrap API.
@@ -406,10 +405,10 @@ const api = setupAxiosInterceptor(axios.create({
406
405
  ## Client-Side Best Practices
407
406
 
408
407
  1. Single Instance: Call configure() once at app startup and reuse the singleton.
409
- 2. Token Timing: Set refreshInterval about 5 minutes before tokenExpiry to avoid race conditions.
408
+ 2. Token Timing: The server controls refresh and expiry timing via bootstrap response.
410
409
  3. Error Handling: Handle errors and provide user feedback for limited mode.
411
410
  4. HTTPS: Use secure, production-grade origins (https) in production environments.
412
- 5. CORS/Credentials: If cross-origin, ensure credentials are enabled in client config and server CORS.
411
+ 5. CORS/Credentials: If cross-origin, ensure credentials are enabled in server CORS (SDK defaults to credentials: true).
413
412
  6. Initialization: Initialize after configure() and before issuing business API calls.
414
413
 
415
414
  ## Troubleshooting
@@ -423,7 +422,7 @@ const api = setupAxiosInterceptor(axios.create({
423
422
  ### Automatic refresh not working
424
423
 
425
424
  - Check browser console for errors
426
- - Verify `refreshInterval` is set correctly
425
+ - Verify server is sending `refresh_time` in bootstrap response
427
426
  - Ensure timer isn't being cleared prematurely
428
427
 
429
428
  ### Multiple initializations
@@ -447,7 +446,6 @@ import type { SessionConfig, SessionState, UseSessionOptions } from 'gateway-web
447
446
 
448
447
  const config: SessionConfig = {
449
448
  bootstrapUrl: '/api/session',
450
- refreshInterval: 25 * 60 * 1000
451
449
  };
452
450
 
453
451
  const manager = SessionManager.getInstance();
package/dist/index.cjs.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react");class t extends Error{constructor(e,i,s={}){super(e),this.name="SessionError",this.code=i,this.details=s,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor),Object.setPrototypeOf(this,t.prototype)}}class i extends t{constructor(e,t){super(e,"CONFIGURATION_ERROR",t),this.name="ConfigurationError"}}class s extends t{constructor(e,t){super(e,"BOOTSTRAP_ERROR",t),this.name="BootstrapError"}}class r extends t{constructor(e,t){super(e,"NETWORK_ERROR",t),this.name="NetworkError"}}class o extends t{constructor(e,t={}){super(e,"SSR_ERROR",t),this.name="SSRError"}}const n={NONE:0,ERROR:1,WARN:2,INFO:3,DEBUG:4};const a=new class{constructor(){this.level=n.WARN}setLevel(e){this.level="string"==typeof e?n[e.toUpperCase()]??n.WARN:e}error(...e){this.level>=n.ERROR&&console.error("[SessionManager]",...e)}warn(...e){this.level>=n.WARN&&console.warn("[SessionManager]",...e)}info(...e){this.level>=n.INFO&&console.info("[SessionManager]",...e)}debug(...e){this.level>=n.DEBUG&&console.debug("[SessionManager]",...e)}};class l{constructor(){if(l.instance)return l.instance;this.config={bootstrapUrl:"/session/bootstrap",refreshInterval:15e5,tokenExpiry:18e5,maxRetries:3,tokenCookieName:"token"},this.state={isInitialized:!1,isLoading:!1,lastRefreshTime:null,error:null,errorCode:null,nextRefreshTime:null,tokenExpiry:null,cfSessionId:null,initializationFailed:!1},this.refreshTimerId=null,this.listeners=new Set,this.initializationPromise=null,l.instance=this}configure(e={}){if(!e.bootstrapUrl||"string"!=typeof e.bootstrapUrl||!e.bootstrapUrl.trim())throw new i("bootstrapUrl is required and must be a non-empty string",{bootstrapUrl:e.bootstrapUrl});if(void 0!==e.refreshInterval){if("number"!=typeof e.refreshInterval||!isFinite(e.refreshInterval))throw new i("refreshInterval must be a finite number",{refreshInterval:e.refreshInterval});if(e.refreshInterval<=0)throw new i("refreshInterval must be positive",{refreshInterval:e.refreshInterval})}if(void 0!==e.tokenExpiry){if("number"!=typeof e.tokenExpiry||!isFinite(e.tokenExpiry))throw new i("tokenExpiry must be a finite number",{tokenExpiry:e.tokenExpiry});if(e.tokenExpiry<=0)throw new i("tokenExpiry must be positive",{tokenExpiry:e.tokenExpiry})}this.config={...this.config,...e,credentials:void 0===e.credentials||e.credentials,logLevel:e.logLevel||"WARN"},a.setLevel(this.config.logLevel),this.config.refreshInterval&&this.config.tokenExpiry&&this.config.refreshInterval>=this.config.tokenExpiry&&a.warn("refreshInterval should be less than tokenExpiry to prevent race conditions")}async initialize(){if("undefined"==typeof window)throw new o("Cannot initialize in non-browser environment (SSR)");if(this.state.initializationFailed)throw a.warn("Initialization previously failed. Reload page to retry."),new s("Initialization failed after max retries. Reload page to retry.");if(this.initializationPromise)return a.debug("Initialization already in progress, waiting..."),this.initializationPromise;const e=this.getToken(this.config.tokenCookieName);if(e&&!this.state.isInitialized){if(a.info("Token found in cookie, skipping bootstrap API call"),this.state.isInitialized=!0,this.state.lastRefreshTime=Date.now(),this.state.nextRefreshTime=Date.now()+this.config.refreshInterval,this.state.tokenExpiry=this.config.tokenExpiry,"undefined"!=typeof sessionStorage){const e=sessionStorage.getItem("cf-session-id");e?this.state.cfSessionId=e:this.setCfSessionId(this.generateSessionId())}else this.setCfSessionId(this.generateSessionId());return this.startTokenRefresh(this.config.refreshInterval),this.notifyListeners(),{token:e}}this.state.isLoading=!0,this.state.error=null,this.notifyListeners(),this.initializationPromise=(async()=>{let e;for(let t=1;t<=this.config.maxRetries;t++)try{const e=this.generateSessionId();this.setCfSessionId(e),a.info(`Calling bootstrap API (attempt ${t}/${this.config.maxRetries}):`,this.config.bootstrapUrl);const i=await fetch(this.config.bootstrapUrl,{method:"GET",credentials:this.config.credentials?"include":"same-origin",headers:{"Content-Type":"application/json","cf-session-id":this.state.cfSessionId,...this.config.headers}});if(!i.ok)throw new s(`Bootstrap failed: ${i.status} ${i.statusText}`,{status:i.status,statusText:i.statusText,url:this.config.bootstrapUrl});let r;try{r=await i.json()}catch(e){throw new s("Bootstrap response is not valid JSON",{originalError:e.message})}a.info("Bootstrap successful");const o=i.headers.get("set-cookie"),n=o&&o.includes(this.config.tokenCookieName),l=r.refresh_time?1e3*r.refresh_time:this.config.refreshInterval,h=r.expire?1e3*r.expire:this.config.tokenExpiry;return r.token&&!n?(a.debug("Server did not set cookie, setting from SDK"),this.setCookie(this.config.tokenCookieName,r.token,h/1e3)):n&&a.debug("Server set cookie, skipping SDK cookie logic"),this.state.isInitialized=!0,this.state.isLoading=!1,this.state.lastRefreshTime=Date.now(),this.state.nextRefreshTime=Date.now()+l,this.state.tokenExpiry=h,this.state.error=null,this.startTokenRefresh(l),this.notifyListeners(),r}catch(i){if(e=i instanceof Error?i:new r("Unknown error occurred",{error:i}),a.error(`Bootstrap attempt ${t} failed:`,e.message),t<this.config.maxRetries){const e=Math.min(1e3*Math.pow(2,t-1),5e3);a.info(`Retrying in ${e}ms...`),await new Promise(t=>setTimeout(t,e))}}throw a.error("All bootstrap attempts failed"),this.state.isLoading=!1,this.state.error=e?.message||"Unknown error",this.state.errorCode=e?.code||"UNKNOWN_ERROR",this.state.initializationFailed=!0,this.notifyListeners(),e})();try{return await this.initializationPromise}finally{this.initializationPromise=null}}isRefreshing(){return null!==this.initializationPromise}async waitForRefresh(){return this.initializationPromise?(a.debug("Waiting for ongoing refresh to complete"),this.initializationPromise):Promise.resolve()}setCfSessionId(e){this.state.cfSessionId=e,"undefined"!=typeof sessionStorage&&sessionStorage.setItem("cf-session-id",e)}generateSessionId(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substr(2,9)}`}getSessionId(){return this.state.cfSessionId}shouldUseTokenHeader(){return"undefined"!=typeof window&&"http:"===window.location.protocol}getToken(e=this.config.tokenCookieName){if("undefined"==typeof document)return null;const t=`; ${document.cookie}`.split(`; ${e}=`);if(2===t.length)try{return decodeURIComponent(t.pop().split(";").shift())}catch(e){return a.error("Failed to decode cookie value:",e instanceof Error?e.message:String(e)),null}return null}setCookie(e,t,i){if("undefined"==typeof document)return void a.warn("Cannot set cookie in non-browser environment");const s=e.replace(/[^a-zA-Z0-9_-]/g,"");if(!s)return void a.error("Invalid cookie name provided");const r=new Date(Date.now()+1e3*i).toUTCString(),o="undefined"!=typeof window&&"https:"===window.location.protocol,n=o?"; Secure":"",l=encodeURIComponent(t);document.cookie=`${s}=${l}; path=/; expires=${r}; SameSite=Lax${n}`,a.debug(`Cookie set: ${s}, expires in ${i}s${o?" (Secure)":""} [WARNING: Not HttpOnly]`)}startTokenRefresh(e=this.config.refreshInterval){this.stopTokenRefresh(),a.info(`Scheduling token refresh in ${e/1e3} seconds`),this.refreshTimerId=setTimeout(async()=>{a.info("Auto-refreshing token...");try{await this.refreshToken()}catch(e){const t=e instanceof Error?e.message:String(e),i=e?.code||"UNKNOWN_ERROR";a.error("Auto-refresh failed:",t),this.state.error=t,this.state.errorCode=i,this.notifyListeners()}},e)}stopTokenRefresh(){this.refreshTimerId&&(clearTimeout(this.refreshTimerId),this.refreshTimerId=null,a.debug("Token refresh timer stopped"))}async refreshToken(){return a.info("Manual token refresh triggered"),this.state.initializationFailed=!1,this.initialize()}getSessionStatus(){return{isInitialized:this.state.isInitialized,isLoading:this.state.isLoading,lastRefreshTime:this.state.lastRefreshTime,nextRefreshTime:this.state.nextRefreshTime,tokenExpiry:this.state.tokenExpiry,error:this.state.error,errorCode:this.state.errorCode,initializationFailed:this.state.initializationFailed,timeUntilRefresh:this.state.nextRefreshTime?Math.max(0,this.state.nextRefreshTime-Date.now()):null}}subscribe(e){if("function"!=typeof e)throw new TypeError("Listener must be a function");return this.listeners.add(e),()=>{this.listeners.delete(e)}}notifyListeners(){const e=this.getSessionStatus();Array.from(this.listeners).forEach(t=>{try{t(e)}catch(e){const t=e instanceof Error?e.message:String(e);a.error("Listener error:",t)}})}destroy(){this.stopTokenRefresh(),this.listeners.clear(),this.state={isInitialized:!1,isLoading:!1,lastRefreshTime:null,error:null,errorCode:null,nextRefreshTime:null,tokenExpiry:null,cfSessionId:null,initializationFailed:!1},"undefined"!=typeof sessionStorage&&sessionStorage.removeItem("cf-session-id"),a.info("Destroyed")}static getInstance(){return l.instance||(l.instance=new l),l.instance}}exports.BootstrapError=s,exports.ConfigurationError=i,exports.LOG_LEVELS=n,exports.NetworkError=r,exports.SSRError=o,exports.SessionError=t,exports.SessionManager=l,exports.default=l,exports.fetchInterceptor=async function(e,t={}){const i=l.getInstance();if(t.headers={...t.headers,"cf-session-id":i.getSessionId()},i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(t.headers[i.config.tokenCookieName]=e)}let s=await fetch(e,t);if(401===s.status){const r=s.clone();try{if("INVALID_SESSION"===(await r.json()).error_msg){if(i.isRefreshing()?await i.waitForRefresh():await i.refreshToken(),t.headers["cf-session-id"]=i.getSessionId(),i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(t.headers[i.config.tokenCookieName]=e)}s=await fetch(e,t)}}catch(e){}}return s},exports.logger=a,exports.setupAxiosInterceptor=function(e){const t=l.getInstance();return e.interceptors.request.use(e=>{if(e.headers["cf-session-id"]=t.getSessionId(),t.shouldUseTokenHeader()){const i=t.getToken(t.config.tokenCookieName);i&&(e.headers[t.config.tokenCookieName]=i)}return e},e=>Promise.reject(e)),e.interceptors.response.use(e=>e,async i=>{const s=i.config;if(401===i.response?.status&&!s._retry&&"INVALID_SESSION"===i.response?.data?.error_msg){if(s._retry=!0,t.isRefreshing()?await t.waitForRefresh():await t.refreshToken(),s.headers["cf-session-id"]=t.getSessionId(),t.shouldUseTokenHeader()){const e=t.getToken(t.config.tokenCookieName);e&&(s.headers[t.config.tokenCookieName]=e)}return e(s)}return Promise.reject(i)}),e},exports.useSession=function(t={}){const{autoInitialize:i=!0}=t,s=l.getInstance(),[r,o]=e.useState(()=>s.getSessionStatus());e.useEffect(()=>s.subscribe(e=>{o(e)}),[]),e.useEffect(()=>{!i||r.isInitialized||r.isLoading||r.initializationFailed||s.initialize().catch(e=>{a.error("Auto-initialization failed:",e)})},[i,r.isInitialized,r.isLoading,r.initializationFailed]);const n=e.useCallback(async()=>{try{await s.refreshToken()}catch(e){throw a.error("Manual refresh failed:",e),e}},[]),h=e.useCallback(async()=>{try{await s.initialize()}catch(e){throw a.error("Manual initialization failed:",e),e}},[]);return{isInitialized:r.isInitialized,isLoading:r.isLoading,error:r.error,lastRefreshTime:r.lastRefreshTime,nextRefreshTime:r.nextRefreshTime,timeUntilRefresh:r.timeUntilRefresh,initializationFailed:r.initializationFailed,refresh:n,initialize:h}};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react");class t extends Error{constructor(e,i,s={}){super(e),this.name="SessionError",this.code=i,this.details=s,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor),Object.setPrototypeOf(this,t.prototype)}}class i extends t{constructor(e,t){super(e,"CONFIGURATION_ERROR",t),this.name="ConfigurationError"}}class s extends t{constructor(e,t){super(e,"BOOTSTRAP_ERROR",t),this.name="BootstrapError"}}class r extends t{constructor(e,t){super(e,"NETWORK_ERROR",t),this.name="NetworkError"}}class o extends t{constructor(e,t={}){super(e,"SSR_ERROR",t),this.name="SSRError"}}const n={NONE:0,ERROR:1,WARN:2,INFO:3,DEBUG:4};const a=new class{constructor(){this.level=n.WARN}setLevel(e){this.level="string"==typeof e?n[e.toUpperCase()]??n.WARN:e}error(...e){this.level>=n.ERROR&&console.error("[SessionManager]",...e)}warn(...e){this.level>=n.WARN&&console.warn("[SessionManager]",...e)}info(...e){this.level>=n.INFO&&console.info("[SessionManager]",...e)}debug(...e){this.level>=n.DEBUG&&console.debug("[SessionManager]",...e)}};class l{constructor(){if(l.instance)return l.instance;this.config={bootstrapUrl:"/session/bootstrap",refreshInterval:15e5,tokenExpiry:18e5,maxRetries:3,tokenCookieName:"token"},this.state={isInitialized:!1,isLoading:!1,lastRefreshTime:null,error:null,errorCode:null,nextRefreshTime:null,tokenExpiry:null,cfSessionId:null,initializationFailed:!1},this.refreshTimerId=null,this.listeners=new Set,this.initializationPromise=null,l.instance=this}configure(e={}){if(!e.bootstrapUrl||"string"!=typeof e.bootstrapUrl||!e.bootstrapUrl.trim())throw new i("bootstrapUrl is required and must be a non-empty string",{bootstrapUrl:e.bootstrapUrl});if(void 0!==e.refreshInterval){if("number"!=typeof e.refreshInterval||!isFinite(e.refreshInterval))throw new i("refreshInterval must be a finite number",{refreshInterval:e.refreshInterval});if(e.refreshInterval<=0)throw new i("refreshInterval must be positive",{refreshInterval:e.refreshInterval})}if(void 0!==e.tokenExpiry){if("number"!=typeof e.tokenExpiry||!isFinite(e.tokenExpiry))throw new i("tokenExpiry must be a finite number",{tokenExpiry:e.tokenExpiry});if(e.tokenExpiry<=0)throw new i("tokenExpiry must be positive",{tokenExpiry:e.tokenExpiry})}this.config={...this.config,...e,credentials:void 0===e.credentials||e.credentials,logLevel:e.logLevel||"WARN"},a.setLevel(this.config.logLevel),this.config.refreshInterval&&this.config.tokenExpiry&&this.config.refreshInterval>=this.config.tokenExpiry&&a.warn("refreshInterval should be less than tokenExpiry to prevent race conditions")}async initialize(e=!1){if("undefined"==typeof window)throw new o("Cannot initialize in non-browser environment (SSR)");if(this.state.initializationFailed)throw a.warn("Initialization previously failed. Reload page to retry."),new s("Initialization failed after max retries. Reload page to retry.");if(this.initializationPromise)return a.debug("Initialization already in progress, waiting..."),this.initializationPromise;if(!e){if("true"===this.getCookie("session_initialized")){a.info("Session already initialized, skipping bootstrap API call"),this.state.isInitialized=!0,this.state.lastRefreshTime=Date.now(),this.state.nextRefreshTime=Date.now()+this.config.refreshInterval,this.state.tokenExpiry=this.config.tokenExpiry;const e=this.getCookie("cf-session-id");return e?this.state.cfSessionId=e:this.setCfSessionId(this.generateSessionId()),this.startTokenRefresh(this.config.refreshInterval),this.notifyListeners(),{token:this.getToken(this.config.tokenCookieName)}}}this.state.isLoading=!0,this.state.error=null,this.notifyListeners(),this.initializationPromise=(async()=>{let e;for(let t=1;t<=this.config.maxRetries;t++)try{const e=this.generateSessionId();this.setCfSessionId(e),a.info(`Calling bootstrap API (attempt ${t}/${this.config.maxRetries}):`,this.config.bootstrapUrl);const i=await fetch(this.config.bootstrapUrl,{method:"GET",credentials:this.config.credentials?"include":"same-origin",headers:{"Content-Type":"application/json","cf-session-id":this.state.cfSessionId,...this.config.headers}});if(!i.ok)throw new s(`Bootstrap failed: ${i.status} ${i.statusText}`,{status:i.status,statusText:i.statusText,url:this.config.bootstrapUrl});let r;try{r=await i.json()}catch(e){throw new s("Bootstrap response is not valid JSON",{originalError:e.message})}a.info("Bootstrap successful");const o=i.headers.get("set-cookie"),n=o&&o.includes(this.config.tokenCookieName),l=r.refresh_time?1e3*r.refresh_time:this.config.refreshInterval,h=r.expire?1e3*r.expire:this.config.tokenExpiry;return r.token&&!n?(a.debug("Server did not set cookie, setting from SDK"),this.setCookie(this.config.tokenCookieName,r.token,h/1e3)):n&&a.debug("Server set cookie, skipping SDK cookie logic"),this.state.isInitialized=!0,this.state.isLoading=!1,this.state.lastRefreshTime=Date.now(),this.state.nextRefreshTime=Date.now()+l,this.state.tokenExpiry=h,this.state.error=null,this.setCookie("session_initialized","true",h/1e3),this.startTokenRefresh(l),this.notifyListeners(),r}catch(i){if(e=i instanceof Error?i:new r("Unknown error occurred",{error:i}),a.error(`Bootstrap attempt ${t} failed:`,e.message),t<this.config.maxRetries){const e=Math.min(1e3*Math.pow(2,t-1),5e3);a.info(`Retrying in ${e}ms...`),await new Promise(t=>setTimeout(t,e))}}throw a.error("All bootstrap attempts failed"),this.state.isLoading=!1,this.state.error=e?.message||"Unknown error",this.state.errorCode=e?.code||"UNKNOWN_ERROR",this.state.initializationFailed=!0,this.notifyListeners(),e})();try{return await this.initializationPromise}finally{this.initializationPromise=null}}isRefreshing(){return null!==this.initializationPromise}async waitForRefresh(){return this.initializationPromise?(a.debug("Waiting for ongoing refresh to complete"),this.initializationPromise):Promise.resolve()}setCfSessionId(e){this.state.cfSessionId=e;const t=this.config.tokenExpiry/1e3;this.setCookie("cf-session-id",e,t)}generateSessionId(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substr(2,9)}`}getSessionId(){return this.state.cfSessionId}shouldUseTokenHeader(){return"undefined"!=typeof window&&"http:"===window.location.protocol}getCookie(e){if("undefined"==typeof document)return null;const t=`; ${document.cookie}`.split(`; ${e}=`);if(2===t.length)try{return decodeURIComponent(t.pop().split(";").shift())}catch(e){return a.error("Failed to decode cookie value:",e instanceof Error?e.message:String(e)),null}return null}getToken(e=this.config.tokenCookieName){return this.getCookie(e)}setCookie(e,t,i){if("undefined"==typeof document)return void a.warn("Cannot set cookie in non-browser environment");const s=e.replace(/[^a-zA-Z0-9_-]/g,"");if(!s)return void a.error("Invalid cookie name provided");const r=new Date(Date.now()+1e3*i).toUTCString(),o="undefined"!=typeof window&&"https:"===window.location.protocol,n=o?"; Secure":"",l=encodeURIComponent(t);document.cookie=`${s}=${l}; path=/; expires=${r}; SameSite=Lax${n}`,a.debug(`Cookie set: ${s}, expires in ${i}s${o?" (Secure)":""} [WARNING: Not HttpOnly]`)}startTokenRefresh(e=this.config.refreshInterval){this.stopTokenRefresh(),a.info(`Scheduling token refresh in ${e/1e3} seconds`),this.refreshTimerId=setTimeout(async()=>{a.info("Auto-refreshing token...");try{await this.refreshToken()}catch(e){const t=e instanceof Error?e.message:String(e),i=e?.code||"UNKNOWN_ERROR";a.error("Auto-refresh failed:",t),this.state.error=t,this.state.errorCode=i,this.notifyListeners()}},e)}stopTokenRefresh(){this.refreshTimerId&&(clearTimeout(this.refreshTimerId),this.refreshTimerId=null,a.debug("Token refresh timer stopped"))}async refreshToken(){return a.info("Manual token refresh triggered"),this.state.initializationFailed=!1,this.initialize(!0)}getSessionStatus(){return{isInitialized:this.state.isInitialized,isLoading:this.state.isLoading,lastRefreshTime:this.state.lastRefreshTime,nextRefreshTime:this.state.nextRefreshTime,tokenExpiry:this.state.tokenExpiry,error:this.state.error,errorCode:this.state.errorCode,initializationFailed:this.state.initializationFailed,timeUntilRefresh:this.state.nextRefreshTime?Math.max(0,this.state.nextRefreshTime-Date.now()):null}}subscribe(e){if("function"!=typeof e)throw new TypeError("Listener must be a function");return this.listeners.add(e),()=>{this.listeners.delete(e)}}notifyListeners(){const e=this.getSessionStatus();Array.from(this.listeners).forEach(t=>{try{t(e)}catch(e){const t=e instanceof Error?e.message:String(e);a.error("Listener error:",t)}})}destroy(){this.stopTokenRefresh(),this.listeners.clear(),this.state={isInitialized:!1,isLoading:!1,lastRefreshTime:null,error:null,errorCode:null,nextRefreshTime:null,tokenExpiry:null,cfSessionId:null,initializationFailed:!1},this.setCookie("cf-session-id","",-1),this.setCookie("session_initialized","",-1),a.info("Destroyed")}static getInstance(){return l.instance||(l.instance=new l),l.instance}}exports.BootstrapError=s,exports.ConfigurationError=i,exports.LOG_LEVELS=n,exports.NetworkError=r,exports.SSRError=o,exports.SessionError=t,exports.SessionManager=l,exports.default=l,exports.fetchInterceptor=async function(e,t={}){const i=l.getInstance();if(t.credentials=t.credentials||"include",t.headers={...t.headers,"cf-session-id":i.getSessionId()},i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(t.headers[i.config.tokenCookieName]=e)}let s=await fetch(e,t);if(401===s.status){const r=s.clone();try{if("INVALID_SESSION"===(await r.json()).error_msg){if(i.isRefreshing())await i.waitForRefresh();else{i.getToken(i.config.tokenCookieName)||await i.refreshToken()}if(t.headers["cf-session-id"]=i.getSessionId(),i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(t.headers[i.config.tokenCookieName]=e)}s=await fetch(e,t)}}catch(e){}}return s},exports.logger=a,exports.setupAxiosInterceptor=function(e){const t=l.getInstance();return e.interceptors.request.use(e=>{if(e.headers["cf-session-id"]=t.getSessionId(),t.shouldUseTokenHeader()){const i=t.getToken(t.config.tokenCookieName);i&&(e.headers[t.config.tokenCookieName]=i)}return e},e=>Promise.reject(e)),e.interceptors.response.use(e=>e,async i=>{const s=i.config;if(401===i.response?.status&&!s._retry&&"INVALID_SESSION"===i.response?.data?.error_msg){if(s._retry=!0,t.isRefreshing())await t.waitForRefresh();else{t.getToken(t.config.tokenCookieName)||await t.refreshToken()}if(s.headers["cf-session-id"]=t.getSessionId(),t.shouldUseTokenHeader()){const e=t.getToken(t.config.tokenCookieName);e&&(s.headers[t.config.tokenCookieName]=e)}return e(s)}return Promise.reject(i)}),e},exports.useSession=function(t={}){const{autoInitialize:i=!0}=t,s=l.getInstance(),[r,o]=e.useState(()=>s.getSessionStatus());e.useEffect(()=>s.subscribe(e=>{o(e)}),[]),e.useEffect(()=>{!i||r.isInitialized||r.isLoading||r.initializationFailed||s.initialize().catch(e=>{a.error("Auto-initialization failed:",e)})},[i,r.isInitialized,r.isLoading,r.initializationFailed]);const n=e.useCallback(async()=>{try{await s.refreshToken()}catch(e){throw a.error("Manual refresh failed:",e),e}},[]),h=e.useCallback(async()=>{try{await s.initialize()}catch(e){throw a.error("Manual initialization failed:",e),e}},[]);return{isInitialized:r.isInitialized,isLoading:r.isLoading,error:r.error,lastRefreshTime:r.lastRefreshTime,nextRefreshTime:r.nextRefreshTime,timeUntilRefresh:r.timeUntilRefresh,initializationFailed:r.initializationFailed,refresh:n,initialize:h}};
2
2
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/errors.js","../src/logger.js","../src/SessionManager.js","../src/interceptors.js","../src/hooks/useSession.js"],"sourcesContent":["/**\n * Custom error classes for SessionManager\n */\n\nexport class SessionError extends Error {\n constructor(message, code, details = {}) {\n super(message);\n this.name = 'SessionError';\n this.code = code;\n this.details = details;\n \n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n \n // Set the prototype explicitly for proper instanceof checks\n Object.setPrototypeOf(this, SessionError.prototype);\n }\n}\n\nexport class ConfigurationError extends SessionError {\n constructor(message, details) {\n super(message, 'CONFIGURATION_ERROR', details);\n this.name = 'ConfigurationError';\n }\n}\n\nexport class BootstrapError extends SessionError {\n constructor(message, details) {\n super(message, 'BOOTSTRAP_ERROR', details);\n this.name = 'BootstrapError';\n }\n}\n\nexport class NetworkError extends SessionError {\n constructor(message, details) {\n super(message, 'NETWORK_ERROR', details);\n this.name = 'NetworkError';\n }\n}\n\nexport class SSRError extends SessionError {\n constructor(message, details = {}) {\n super(message, 'SSR_ERROR', details);\n this.name = 'SSRError';\n }\n}\n","/**\n * Logger utility with configurable log levels\n */\n\nconst LOG_LEVELS = {\n NONE: 0,\n ERROR: 1,\n WARN: 2,\n INFO: 3,\n DEBUG: 4\n};\n\nclass Logger {\n constructor() {\n this.level = LOG_LEVELS.WARN; // Default to WARN in production\n }\n\n setLevel(level) {\n if (typeof level === 'string') {\n this.level = LOG_LEVELS[level.toUpperCase()] ?? LOG_LEVELS.WARN;\n } else {\n this.level = level;\n }\n }\n\n error(...args) {\n if (this.level >= LOG_LEVELS.ERROR) {\n console.error('[SessionManager]', ...args);\n }\n }\n\n warn(...args) {\n if (this.level >= LOG_LEVELS.WARN) {\n console.warn('[SessionManager]', ...args);\n }\n }\n\n info(...args) {\n if (this.level >= LOG_LEVELS.INFO) {\n console.info('[SessionManager]', ...args);\n }\n }\n\n debug(...args) {\n if (this.level >= LOG_LEVELS.DEBUG) {\n console.debug('[SessionManager]', ...args);\n }\n }\n}\n\nexport const logger = new Logger();\nexport { LOG_LEVELS };\n","import {BootstrapError, ConfigurationError, NetworkError, SSRError} from './errors.js';\nimport {logger} from './logger.js';\n\n/**\n * SessionManager - Core session token management class\n * Handles session bootstrap, automatic token refresh, and lifecycle management\n */\nclass SessionManager {\n constructor() {\n if (SessionManager.instance) {\n return SessionManager.instance;\n }\n\n this.config = {\n bootstrapUrl: '/session/bootstrap',\n refreshInterval: 25 * 60 * 1000, // 25 minutes in milliseconds\n tokenExpiry: 30 * 60 * 1000, // 30 minutes in milliseconds\n maxRetries: 3,\n tokenCookieName: 'token', // Cookie name to read token from\n };\n\n this.state = {\n isInitialized: false,\n isLoading: false,\n lastRefreshTime: null,\n error: null,\n errorCode: null,\n nextRefreshTime: null,\n tokenExpiry: null,\n cfSessionId: null,\n initializationFailed: false,\n };\n\n this.refreshTimerId = null;\n this.listeners = new Set();\n this.initializationPromise = null;\n\n SessionManager.instance = this;\n }\n\n /**\n * Configure the session manager\n * @param {Object} config - Configuration options\n * @param {string} config.bootstrapUrl - URL for bootstrap API endpoint\n * @param {number} config.refreshInterval - Interval for token refresh in milliseconds (default: 25 mins)\n * @param {number} config.tokenExpiry - Token expiry time in milliseconds (default: 30 mins)\n * @param {number} config.maxRetries - Maximum retry attempts on failure (default: 3)\n * @param {Object} config.headers - Additional headers for API calls\n * @param {boolean} config.credentials - Include credentials in requests (default: true)\n * @param {string} config.tokenCookieName - Cookie name to read token from (default: 'token')\n */\n configure(config = {}) {\n if (!config.bootstrapUrl || typeof config.bootstrapUrl !== 'string' || !config.bootstrapUrl.trim()) {\n throw new ConfigurationError('bootstrapUrl is required and must be a non-empty string', { bootstrapUrl: config.bootstrapUrl });\n }\n \n if (config.refreshInterval !== undefined) {\n if (typeof config.refreshInterval !== 'number' || !isFinite(config.refreshInterval)) {\n throw new ConfigurationError('refreshInterval must be a finite number', { refreshInterval: config.refreshInterval });\n }\n if (config.refreshInterval <= 0) {\n throw new ConfigurationError('refreshInterval must be positive', { refreshInterval: config.refreshInterval });\n }\n }\n \n if (config.tokenExpiry !== undefined) {\n if (typeof config.tokenExpiry !== 'number' || !isFinite(config.tokenExpiry)) {\n throw new ConfigurationError('tokenExpiry must be a finite number', { tokenExpiry: config.tokenExpiry });\n }\n if (config.tokenExpiry <= 0) {\n throw new ConfigurationError('tokenExpiry must be positive', { tokenExpiry: config.tokenExpiry });\n }\n }\n\n this.config = {\n ...this.config,\n ...config,\n credentials: config.credentials !== undefined ? config.credentials : true,\n logLevel: config.logLevel || 'WARN'\n };\n\n logger.setLevel(this.config.logLevel);\n\n if (this.config.refreshInterval && this.config.tokenExpiry && \n this.config.refreshInterval >= this.config.tokenExpiry) {\n logger.warn('refreshInterval should be less than tokenExpiry to prevent race conditions');\n }\n }\n\n /**\n * Initialize session by calling bootstrap API\n * @returns {Promise<Object>} Bootstrap response\n */\n async initialize() {\n if (typeof window === 'undefined') {\n throw new SSRError('Cannot initialize in non-browser environment (SSR)');\n }\n \n if (this.state.initializationFailed) {\n logger.warn('Initialization previously failed. Reload page to retry.');\n throw new BootstrapError('Initialization failed after max retries. Reload page to retry.');\n }\n \n // Return existing promise if refresh already in progress\n if (this.initializationPromise) {\n logger.debug('Initialization already in progress, waiting...');\n return this.initializationPromise;\n }\n\n // Check if token already exists in cookie\n const existingToken = this.getToken(this.config.tokenCookieName);\n if (existingToken && !this.state.isInitialized) {\n logger.info('Token found in cookie, skipping bootstrap API call');\n this.state.isInitialized = true;\n this.state.lastRefreshTime = Date.now();\n this.state.nextRefreshTime = Date.now() + this.config.refreshInterval;\n this.state.tokenExpiry = this.config.tokenExpiry;\n \n // Restore or generate cf-session-id\n if (typeof sessionStorage !== 'undefined') {\n const storedSessionId = sessionStorage.getItem('cf-session-id');\n if (storedSessionId) {\n this.state.cfSessionId = storedSessionId;\n } else {\n this.setCfSessionId(this.generateSessionId());\n }\n } else {\n this.setCfSessionId(this.generateSessionId());\n }\n \n this.startTokenRefresh(this.config.refreshInterval);\n this.notifyListeners();\n return { token: existingToken };\n }\n\n this.state.isLoading = true;\n this.state.error = null;\n this.notifyListeners();\n\n this.initializationPromise = (async () => {\n let lastError;\n \n for (let attempt = 1; attempt <= this.config.maxRetries; attempt++) {\n try {\n // Generate new cf-session-id for each bootstrap call\n const newSessionId = this.generateSessionId();\n this.setCfSessionId(newSessionId);\n\n logger.info(`Calling bootstrap API (attempt ${attempt}/${this.config.maxRetries}):`, this.config.bootstrapUrl);\n\n const response = await fetch(this.config.bootstrapUrl, {\n method: 'GET',\n credentials: this.config.credentials ? 'include' : 'same-origin',\n headers: {\n 'Content-Type': 'application/json',\n 'cf-session-id': this.state.cfSessionId,\n ...this.config.headers,\n },\n });\n\n if (!response.ok) {\n throw new BootstrapError(`Bootstrap failed: ${response.status} ${response.statusText}`, {\n status: response.status,\n statusText: response.statusText,\n url: this.config.bootstrapUrl\n });\n }\n\n let data;\n try {\n data = await response.json();\n } catch (jsonError) {\n throw new BootstrapError('Bootstrap response is not valid JSON', { originalError: jsonError.message });\n }\n logger.info('Bootstrap successful');\n\n // Check if server set a cookie\n const setCookieHeader = response.headers.get('set-cookie');\n const serverSetCookie = setCookieHeader && setCookieHeader.includes(this.config.tokenCookieName);\n\n // Use refresh_time from response (in seconds) or fall back to config\n const refreshInterval = data.refresh_time \n ? data.refresh_time * 1000 \n : this.config.refreshInterval;\n\n // Calculate token expiry: refresh_time + window_time (both in seconds)\n const tokenExpiry = data.expire\n ? data.expire * 1000\n : this.config.tokenExpiry;\n\n if (data.token && !serverSetCookie) {\n logger.debug('Server did not set cookie, setting from SDK');\n this.setCookie(this.config.tokenCookieName, data.token, tokenExpiry / 1000);\n } else if (serverSetCookie) {\n logger.debug('Server set cookie, skipping SDK cookie logic');\n }\n\n this.state.isInitialized = true;\n this.state.isLoading = false;\n this.state.lastRefreshTime = Date.now();\n this.state.nextRefreshTime = Date.now() + refreshInterval;\n this.state.tokenExpiry = tokenExpiry;\n this.state.error = null;\n\n // Start automatic refresh timer with dynamic interval\n this.startTokenRefresh(refreshInterval);\n\n this.notifyListeners();\n return data;\n } catch (error) {\n lastError = error instanceof Error ? error : new NetworkError('Unknown error occurred', { error });\n logger.error(`Bootstrap attempt ${attempt} failed:`, lastError.message);\n \n if (attempt < this.config.maxRetries) {\n const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);\n logger.info(`Retrying in ${delay}ms...`);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n \n logger.error('All bootstrap attempts failed');\n this.state.isLoading = false;\n this.state.error = lastError?.message || 'Unknown error';\n this.state.errorCode = lastError?.code || 'UNKNOWN_ERROR';\n this.state.initializationFailed = true;\n this.notifyListeners();\n throw lastError;\n })();\n\n try {\n return await this.initializationPromise;\n } finally {\n this.initializationPromise = null;\n }\n }\n\n /**\n * Check if token refresh is currently in progress\n * @returns {boolean} True if refresh is in progress\n */\n isRefreshing() {\n return this.initializationPromise !== null;\n }\n\n /**\n * Wait for ongoing refresh to complete\n * @returns {Promise<Object>} Bootstrap response from ongoing refresh\n */\n async waitForRefresh() {\n if (this.initializationPromise) {\n logger.debug('Waiting for ongoing refresh to complete');\n return this.initializationPromise;\n }\n return Promise.resolve();\n }\n\n /**\n * Set cf-session-id\n * @param {string} sessionId - Session ID\n */\n setCfSessionId(sessionId) {\n this.state.cfSessionId = sessionId;\n if (typeof sessionStorage !== 'undefined') {\n sessionStorage.setItem('cf-session-id', sessionId);\n }\n }\n\n /**\n * Generate unique session ID\n * @returns {string} Generated session ID\n */\n generateSessionId() {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }\n\n /**\n * Get cf-session-id for requests\n * @returns {string} Current cf-session-id\n */\n getSessionId() {\n return this.state.cfSessionId;\n }\n\n /**\n * Check if token should be added to headers (HTTP or cross-origin)\n * @returns {boolean} True if token should be added to headers\n */\n shouldUseTokenHeader() {\n if (typeof window === 'undefined') return false;\n return window.location.protocol === 'http:';\n }\n\n /**\n * Get token from cookie\n * @param {string} name - Cookie name (default: configured tokenCookieName)\n * @returns {string|null} Token value or null\n */\n getToken(name = this.config.tokenCookieName) {\n if (typeof document === 'undefined') return null;\n const value = `; ${document.cookie}`;\n const parts = value.split(`; ${name}=`);\n if (parts.length === 2) {\n try {\n return decodeURIComponent(parts.pop().split(';').shift());\n } catch (error) {\n logger.error('Failed to decode cookie value:', error instanceof Error ? error.message : String(error));\n return null;\n }\n }\n return null;\n }\n\n /**\n * Set cookie from client-side (WARNING: Not HttpOnly, less secure than server-set cookies)\n * @param {string} name - Cookie name\n * @param {string} value - Cookie value\n * @param {number} maxAge - Max age in seconds\n */\n setCookie(name, value, maxAge) {\n if (typeof document === 'undefined') {\n logger.warn('Cannot set cookie in non-browser environment');\n return;\n }\n \n // Sanitize cookie name to prevent XSS\n const sanitizedName = name.replace(/[^a-zA-Z0-9_-]/g, '');\n if (!sanitizedName) {\n logger.error('Invalid cookie name provided');\n return;\n }\n \n const expires = new Date(Date.now() + maxAge * 1000).toUTCString();\n const isSecure = typeof window !== 'undefined' && window.location.protocol === 'https:';\n const secureFlag = isSecure ? '; Secure' : '';\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${sanitizedName}=${encodedValue}; path=/; expires=${expires}; SameSite=Lax${secureFlag}`;\n logger.debug(`Cookie set: ${sanitizedName}, expires in ${maxAge}s${isSecure ? ' (Secure)' : ''} [WARNING: Not HttpOnly]`);\n }\n\n /**\n * Start automatic token refresh timer\n * @param {number} interval - Refresh interval in milliseconds\n */\n startTokenRefresh(interval = this.config.refreshInterval) {\n this.stopTokenRefresh();\n logger.info(`Scheduling token refresh in ${interval / 1000} seconds`);\n\n this.refreshTimerId = setTimeout(async () => {\n logger.info('Auto-refreshing token...');\n try {\n await this.refreshToken();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n const errorCode = error?.code || 'UNKNOWN_ERROR';\n logger.error('Auto-refresh failed:', errorMessage);\n this.state.error = errorMessage;\n this.state.errorCode = errorCode;\n this.notifyListeners();\n }\n }, interval);\n }\n\n /**\n * Stop automatic token refresh timer\n */\n stopTokenRefresh() {\n if (this.refreshTimerId) {\n clearTimeout(this.refreshTimerId);\n this.refreshTimerId = null;\n logger.debug('Token refresh timer stopped');\n }\n }\n\n /**\n * Manually refresh the session token\n * @returns {Promise<Object>} Bootstrap response\n */\n async refreshToken() {\n logger.info('Manual token refresh triggered');\n this.state.initializationFailed = false;\n return this.initialize();\n }\n\n /**\n * Get current session status\n * @returns {Object} Current session state\n */\n getSessionStatus() {\n return {\n isInitialized: this.state.isInitialized,\n isLoading: this.state.isLoading,\n lastRefreshTime: this.state.lastRefreshTime,\n nextRefreshTime: this.state.nextRefreshTime,\n tokenExpiry: this.state.tokenExpiry,\n error: this.state.error,\n errorCode: this.state.errorCode,\n initializationFailed: this.state.initializationFailed,\n timeUntilRefresh: this.state.nextRefreshTime\n ? Math.max(0, this.state.nextRefreshTime - Date.now())\n : null,\n };\n }\n\n /**\n * Subscribe to session state changes\n * @param {Function} listener - Callback function to be called on state changes\n * @returns {Function} Unsubscribe function\n */\n subscribe(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('Listener must be a function');\n }\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Notify all listeners of state changes\n */\n notifyListeners() {\n const status = this.getSessionStatus();\n // Use Array.from to create a snapshot, preventing issues if listeners modify the Set\n Array.from(this.listeners).forEach(listener => {\n try {\n listener(status);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('Listener error:', errorMessage);\n }\n });\n }\n\n /**\n * Clean up and reset the session manager\n */\n destroy() {\n this.stopTokenRefresh();\n this.listeners.clear();\n this.state = {\n isInitialized: false,\n isLoading: false,\n lastRefreshTime: null,\n error: null,\n errorCode: null,\n nextRefreshTime: null,\n tokenExpiry: null,\n cfSessionId: null,\n initializationFailed: false,\n };\n if (typeof sessionStorage !== 'undefined') {\n sessionStorage.removeItem('cf-session-id');\n }\n logger.info('Destroyed');\n }\n\n /**\n * Get the singleton instance\n * @returns {SessionManager} Singleton instance\n */\n static getInstance() {\n if (!SessionManager.instance) {\n SessionManager.instance = new SessionManager();\n }\n return SessionManager.instance;\n }\n}\n\nexport default SessionManager;\n","import SessionManager from './SessionManager.js';\nimport { logger } from './logger.js';\n\n/**\n * Fetch interceptor with automatic token refresh on 401\n * @param {string} url - Request URL\n * @param {Object} options - Fetch options\n * @returns {Promise<Response>} Fetch response\n */\nexport async function fetchInterceptor(url, options = {}) {\n const sessionManager = SessionManager.getInstance();\n \n // Add headers\n options.headers = {\n ...options.headers,\n 'cf-session-id': sessionManager.getSessionId(),\n };\n \n // Add token to header if HTTP (not HTTPS)\n if (sessionManager.shouldUseTokenHeader()) {\n const token = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (token) {\n options.headers[sessionManager.config.tokenCookieName] = token;\n }\n }\n \n let response = await fetch(url, options);\n \n // Retry once if unauthorized with INVALID_SESSION\n if (response.status === 401) {\n const clonedResponse = response.clone();\n try {\n const data = await clonedResponse.json();\n if (data.error_msg === 'INVALID_SESSION') {\n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n await sessionManager.refreshToken();\n }\n options.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const newToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (newToken) {\n options.headers[sessionManager.config.tokenCookieName] = newToken;\n }\n }\n response = await fetch(url, options);\n }\n } catch (e) {\n // Not JSON or parsing failed, return original response\n }\n }\n \n return response;\n}\n\n/**\n * Axios interceptor with automatic token refresh on 401\n * @param {Object} axiosInstance - Axios instance\n */\nexport function setupAxiosInterceptor(axiosInstance) {\n const sessionManager = SessionManager.getInstance();\n \n // Request interceptor to add cf-session-id and token\n axiosInstance.interceptors.request.use(\n (config) => {\n config.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const token = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (token) {\n config.headers[sessionManager.config.tokenCookieName] = token;\n }\n }\n return config;\n },\n (error) => Promise.reject(error)\n );\n \n // Response interceptor for token refresh\n axiosInstance.interceptors.response.use(\n (response) => response,\n async (error) => {\n const originalRequest = error.config;\n \n // Retry once if unauthorized with INVALID_SESSION\n if (error.response?.status === 401 && !originalRequest._retry) {\n if (error.response?.data?.error_msg === 'INVALID_SESSION') {\n originalRequest._retry = true;\n \n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n await sessionManager.refreshToken();\n }\n \n originalRequest.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const newToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (newToken) {\n originalRequest.headers[sessionManager.config.tokenCookieName] = newToken;\n }\n }\n return axiosInstance(originalRequest);\n }\n }\n \n return Promise.reject(error);\n }\n );\n \n return axiosInstance;\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport SessionManager from '../SessionManager.js';\nimport { logger } from '../logger.js';\n\n/**\n * React hook for session management\n * Provides session state and controls for React components\n * \n * @param {Object} options - Configuration options\n * @param {boolean} options.autoInitialize - Automatically initialize on mount (default: true)\n * @returns {Object} Session state and controls\n */\nexport function useSession(options = {}) {\n const { autoInitialize = true } = options;\n\n const sessionManager = SessionManager.getInstance();\n\n const [sessionState, setSessionState] = useState(() =>\n sessionManager.getSessionStatus()\n );\n\n // Subscribe to session state changes\n useEffect(() => {\n const unsubscribe = sessionManager.subscribe((newState) => {\n setSessionState(newState);\n });\n\n return unsubscribe;\n }, []); // sessionManager is a singleton, no need to include in deps\n\n // Auto-initialize if enabled\n useEffect(() => {\n if (autoInitialize && !sessionState.isInitialized && !sessionState.isLoading && !sessionState.initializationFailed) {\n sessionManager.initialize().catch(error => {\n logger.error('Auto-initialization failed:', error);\n });\n }\n }, [autoInitialize, sessionState.isInitialized, sessionState.isLoading, sessionState.initializationFailed]);\n\n // Manual refresh function\n const refresh = useCallback(async () => {\n try {\n await sessionManager.refreshToken();\n } catch (error) {\n logger.error('Manual refresh failed:', error);\n throw error;\n }\n }, []);\n\n // Initialize function (for manual control)\n const initialize = useCallback(async () => {\n try {\n await sessionManager.initialize();\n } catch (error) {\n logger.error('Manual initialization failed:', error);\n throw error;\n }\n }, []);\n\n return {\n // State\n isInitialized: sessionState.isInitialized,\n isLoading: sessionState.isLoading,\n error: sessionState.error,\n lastRefreshTime: sessionState.lastRefreshTime,\n nextRefreshTime: sessionState.nextRefreshTime,\n timeUntilRefresh: sessionState.timeUntilRefresh,\n initializationFailed: sessionState.initializationFailed,\n\n // Actions\n refresh,\n initialize,\n };\n}\n\nexport default useSession;\n"],"names":["SessionError","Error","constructor","message","code","details","super","this","name","captureStackTrace","Object","setPrototypeOf","prototype","ConfigurationError","BootstrapError","NetworkError","SSRError","LOG_LEVELS","NONE","ERROR","WARN","INFO","DEBUG","logger","level","setLevel","toUpperCase","error","args","console","warn","info","debug","SessionManager","instance","config","bootstrapUrl","refreshInterval","tokenExpiry","maxRetries","tokenCookieName","state","isInitialized","isLoading","lastRefreshTime","errorCode","nextRefreshTime","cfSessionId","initializationFailed","refreshTimerId","listeners","Set","initializationPromise","configure","trim","undefined","isFinite","credentials","logLevel","initialize","window","existingToken","getToken","Date","now","sessionStorage","storedSessionId","getItem","setCfSessionId","generateSessionId","startTokenRefresh","notifyListeners","token","lastError","attempt","newSessionId","response","fetch","method","headers","ok","status","statusText","url","data","json","jsonError","originalError","setCookieHeader","get","serverSetCookie","includes","refresh_time","expire","setCookie","delay","Math","min","pow","Promise","resolve","setTimeout","isRefreshing","waitForRefresh","sessionId","setItem","crypto","randomUUID","random","toString","substr","getSessionId","shouldUseTokenHeader","location","protocol","document","parts","cookie","split","length","decodeURIComponent","pop","shift","String","value","maxAge","sanitizedName","replace","expires","toUTCString","isSecure","secureFlag","encodedValue","encodeURIComponent","interval","stopTokenRefresh","async","refreshToken","errorMessage","clearTimeout","getSessionStatus","timeUntilRefresh","max","subscribe","listener","TypeError","add","delete","Array","from","forEach","destroy","clear","removeItem","getInstance","options","sessionManager","clonedResponse","clone","error_msg","newToken","e","axiosInstance","interceptors","request","use","reject","originalRequest","_retry","autoInitialize","sessionState","setSessionState","useState","useEffect","newState","catch","refresh","useCallback"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/errors.js","../src/logger.js","../src/SessionManager.js","../src/interceptors.js","../src/hooks/useSession.js"],"sourcesContent":["/**\n * Custom error classes for SessionManager\n */\n\nexport class SessionError extends Error {\n constructor(message, code, details = {}) {\n super(message);\n this.name = 'SessionError';\n this.code = code;\n this.details = details;\n \n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n \n // Set the prototype explicitly for proper instanceof checks\n Object.setPrototypeOf(this, SessionError.prototype);\n }\n}\n\nexport class ConfigurationError extends SessionError {\n constructor(message, details) {\n super(message, 'CONFIGURATION_ERROR', details);\n this.name = 'ConfigurationError';\n }\n}\n\nexport class BootstrapError extends SessionError {\n constructor(message, details) {\n super(message, 'BOOTSTRAP_ERROR', details);\n this.name = 'BootstrapError';\n }\n}\n\nexport class NetworkError extends SessionError {\n constructor(message, details) {\n super(message, 'NETWORK_ERROR', details);\n this.name = 'NetworkError';\n }\n}\n\nexport class SSRError extends SessionError {\n constructor(message, details = {}) {\n super(message, 'SSR_ERROR', details);\n this.name = 'SSRError';\n }\n}\n","/**\n * Logger utility with configurable log levels\n */\n\nconst LOG_LEVELS = {\n NONE: 0,\n ERROR: 1,\n WARN: 2,\n INFO: 3,\n DEBUG: 4\n};\n\nclass Logger {\n constructor() {\n this.level = LOG_LEVELS.WARN; // Default to WARN in production\n }\n\n setLevel(level) {\n if (typeof level === 'string') {\n this.level = LOG_LEVELS[level.toUpperCase()] ?? LOG_LEVELS.WARN;\n } else {\n this.level = level;\n }\n }\n\n error(...args) {\n if (this.level >= LOG_LEVELS.ERROR) {\n console.error('[SessionManager]', ...args);\n }\n }\n\n warn(...args) {\n if (this.level >= LOG_LEVELS.WARN) {\n console.warn('[SessionManager]', ...args);\n }\n }\n\n info(...args) {\n if (this.level >= LOG_LEVELS.INFO) {\n console.info('[SessionManager]', ...args);\n }\n }\n\n debug(...args) {\n if (this.level >= LOG_LEVELS.DEBUG) {\n console.debug('[SessionManager]', ...args);\n }\n }\n}\n\nexport const logger = new Logger();\nexport { LOG_LEVELS };\n","import {BootstrapError, ConfigurationError, NetworkError, SSRError} from './errors.js';\nimport {logger} from './logger.js';\n\n/**\n * SessionManager - Core session token management class\n * Handles session bootstrap, automatic token refresh, and lifecycle management\n */\nclass SessionManager {\n constructor() {\n if (SessionManager.instance) {\n return SessionManager.instance;\n }\n\n this.config = {\n bootstrapUrl: '/session/bootstrap',\n refreshInterval: 25 * 60 * 1000, // 25 minutes in milliseconds\n tokenExpiry: 30 * 60 * 1000, // 30 minutes in milliseconds\n maxRetries: 3,\n tokenCookieName: 'token', // Cookie name to read token from\n };\n\n this.state = {\n isInitialized: false,\n isLoading: false,\n lastRefreshTime: null,\n error: null,\n errorCode: null,\n nextRefreshTime: null,\n tokenExpiry: null,\n cfSessionId: null,\n initializationFailed: false,\n };\n\n this.refreshTimerId = null;\n this.listeners = new Set();\n this.initializationPromise = null;\n\n SessionManager.instance = this;\n }\n\n /**\n * Configure the session manager\n * @param {Object} config - Configuration options\n * @param {string} config.bootstrapUrl - URL for bootstrap API endpoint\n * @param {number} config.refreshInterval - Interval for token refresh in milliseconds (default: 25 mins)\n * @param {number} config.tokenExpiry - Token expiry time in milliseconds (default: 30 mins)\n * @param {number} config.maxRetries - Maximum retry attempts on failure (default: 3)\n * @param {Object} config.headers - Additional headers for API calls\n * @param {boolean} config.credentials - Include credentials in requests (default: true)\n * @param {string} config.tokenCookieName - Cookie name to read token from (default: 'token')\n */\n configure(config = {}) {\n if (!config.bootstrapUrl || typeof config.bootstrapUrl !== 'string' || !config.bootstrapUrl.trim()) {\n throw new ConfigurationError('bootstrapUrl is required and must be a non-empty string', { bootstrapUrl: config.bootstrapUrl });\n }\n \n if (config.refreshInterval !== undefined) {\n if (typeof config.refreshInterval !== 'number' || !isFinite(config.refreshInterval)) {\n throw new ConfigurationError('refreshInterval must be a finite number', { refreshInterval: config.refreshInterval });\n }\n if (config.refreshInterval <= 0) {\n throw new ConfigurationError('refreshInterval must be positive', { refreshInterval: config.refreshInterval });\n }\n }\n \n if (config.tokenExpiry !== undefined) {\n if (typeof config.tokenExpiry !== 'number' || !isFinite(config.tokenExpiry)) {\n throw new ConfigurationError('tokenExpiry must be a finite number', { tokenExpiry: config.tokenExpiry });\n }\n if (config.tokenExpiry <= 0) {\n throw new ConfigurationError('tokenExpiry must be positive', { tokenExpiry: config.tokenExpiry });\n }\n }\n\n this.config = {\n ...this.config,\n ...config,\n credentials: config.credentials !== undefined ? config.credentials : true,\n logLevel: config.logLevel || 'WARN'\n };\n\n logger.setLevel(this.config.logLevel);\n\n if (this.config.refreshInterval && this.config.tokenExpiry && \n this.config.refreshInterval >= this.config.tokenExpiry) {\n logger.warn('refreshInterval should be less than tokenExpiry to prevent race conditions');\n }\n }\n\n /**\n * Initialize session by calling bootstrap API\n * @param {boolean} skipCookieCheck - Skip session_initialized cookie check (for auto-refresh)\n * @returns {Promise<Object>} Bootstrap response\n */\n async initialize(skipCookieCheck = false) {\n if (typeof window === 'undefined') {\n throw new SSRError('Cannot initialize in non-browser environment (SSR)');\n }\n \n if (this.state.initializationFailed) {\n logger.warn('Initialization previously failed. Reload page to retry.');\n throw new BootstrapError('Initialization failed after max retries. Reload page to retry.');\n }\n \n // Return existing promise if refresh already in progress\n if (this.initializationPromise) {\n logger.debug('Initialization already in progress, waiting...');\n return this.initializationPromise;\n }\n\n // Check if session is already initialized via cookie (only on page load)\n if (!skipCookieCheck) {\n const sessionInitialized = this.getCookie('session_initialized');\n if (sessionInitialized === 'true') {\n logger.info('Session already initialized, skipping bootstrap API call');\n this.state.isInitialized = true;\n this.state.lastRefreshTime = Date.now();\n this.state.nextRefreshTime = Date.now() + this.config.refreshInterval;\n this.state.tokenExpiry = this.config.tokenExpiry;\n \n // Restore cf-session-id from cookie\n const storedSessionId = this.getCookie('cf-session-id');\n if (storedSessionId) {\n this.state.cfSessionId = storedSessionId;\n } else {\n this.setCfSessionId(this.generateSessionId());\n }\n \n this.startTokenRefresh(this.config.refreshInterval);\n this.notifyListeners();\n return { token: this.getToken(this.config.tokenCookieName) };\n }\n }\n\n this.state.isLoading = true;\n this.state.error = null;\n this.notifyListeners();\n\n this.initializationPromise = (async () => {\n let lastError;\n \n for (let attempt = 1; attempt <= this.config.maxRetries; attempt++) {\n try {\n // Generate new cf-session-id for each bootstrap call\n const newSessionId = this.generateSessionId();\n this.setCfSessionId(newSessionId);\n\n logger.info(`Calling bootstrap API (attempt ${attempt}/${this.config.maxRetries}):`, this.config.bootstrapUrl);\n\n const response = await fetch(this.config.bootstrapUrl, {\n method: 'GET',\n credentials: this.config.credentials ? 'include' : 'same-origin',\n headers: {\n 'Content-Type': 'application/json',\n 'cf-session-id': this.state.cfSessionId,\n ...this.config.headers,\n },\n });\n\n if (!response.ok) {\n throw new BootstrapError(`Bootstrap failed: ${response.status} ${response.statusText}`, {\n status: response.status,\n statusText: response.statusText,\n url: this.config.bootstrapUrl\n });\n }\n\n let data;\n try {\n data = await response.json();\n } catch (jsonError) {\n throw new BootstrapError('Bootstrap response is not valid JSON', { originalError: jsonError.message });\n }\n logger.info('Bootstrap successful');\n\n // Check if server set a cookie\n const setCookieHeader = response.headers.get('set-cookie');\n const serverSetCookie = setCookieHeader && setCookieHeader.includes(this.config.tokenCookieName);\n\n // Use refresh_time from response (in seconds) or fall back to config\n const refreshInterval = data.refresh_time \n ? data.refresh_time * 1000 \n : this.config.refreshInterval;\n\n // Calculate token expiry: refresh_time + window_time (both in seconds)\n const tokenExpiry = data.expire\n ? data.expire * 1000\n : this.config.tokenExpiry;\n\n if (data.token && !serverSetCookie) {\n logger.debug('Server did not set cookie, setting from SDK');\n this.setCookie(this.config.tokenCookieName, data.token, tokenExpiry / 1000);\n } else if (serverSetCookie) {\n logger.debug('Server set cookie, skipping SDK cookie logic');\n }\n\n this.state.isInitialized = true;\n this.state.isLoading = false;\n this.state.lastRefreshTime = Date.now();\n this.state.nextRefreshTime = Date.now() + refreshInterval;\n this.state.tokenExpiry = tokenExpiry;\n this.state.error = null;\n\n // Set session_initialized cookie (expires at token expiry time)\n this.setCookie('session_initialized', 'true', tokenExpiry / 1000);\n\n // Start automatic refresh timer with dynamic interval\n this.startTokenRefresh(refreshInterval);\n\n this.notifyListeners();\n return data;\n } catch (error) {\n lastError = error instanceof Error ? error : new NetworkError('Unknown error occurred', { error });\n logger.error(`Bootstrap attempt ${attempt} failed:`, lastError.message);\n \n if (attempt < this.config.maxRetries) {\n const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);\n logger.info(`Retrying in ${delay}ms...`);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n \n logger.error('All bootstrap attempts failed');\n this.state.isLoading = false;\n this.state.error = lastError?.message || 'Unknown error';\n this.state.errorCode = lastError?.code || 'UNKNOWN_ERROR';\n this.state.initializationFailed = true;\n this.notifyListeners();\n throw lastError;\n })();\n\n try {\n return await this.initializationPromise;\n } finally {\n this.initializationPromise = null;\n }\n }\n\n /**\n * Check if token refresh is currently in progress\n * @returns {boolean} True if refresh is in progress\n */\n isRefreshing() {\n return this.initializationPromise !== null;\n }\n\n /**\n * Wait for ongoing refresh to complete\n * @returns {Promise<Object>} Bootstrap response from ongoing refresh\n */\n async waitForRefresh() {\n if (this.initializationPromise) {\n logger.debug('Waiting for ongoing refresh to complete');\n return this.initializationPromise;\n }\n return Promise.resolve();\n }\n\n /**\n * Set cf-session-id in cookie\n * @param {string} sessionId - Session ID\n */\n setCfSessionId(sessionId) {\n this.state.cfSessionId = sessionId;\n const maxAge = this.config.tokenExpiry / 1000;\n this.setCookie('cf-session-id', sessionId, maxAge);\n }\n\n /**\n * Generate unique session ID\n * @returns {string} Generated session ID\n */\n generateSessionId() {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }\n\n /**\n * Get cf-session-id for requests\n * @returns {string} Current cf-session-id\n */\n getSessionId() {\n return this.state.cfSessionId;\n }\n\n /**\n * Check if token should be added to headers (HTTP or cross-origin)\n * @returns {boolean} True if token should be added to headers\n */\n shouldUseTokenHeader() {\n if (typeof window === 'undefined') return false;\n return window.location.protocol === 'http:';\n }\n\n /**\n * Get cookie value\n * @param {string} name - Cookie name\n * @returns {string|null} Cookie value or null\n */\n getCookie(name) {\n if (typeof document === 'undefined') return null;\n const value = `; ${document.cookie}`;\n const parts = value.split(`; ${name}=`);\n if (parts.length === 2) {\n try {\n return decodeURIComponent(parts.pop().split(';').shift());\n } catch (error) {\n logger.error('Failed to decode cookie value:', error instanceof Error ? error.message : String(error));\n return null;\n }\n }\n return null;\n }\n\n /**\n * Get token from cookie\n * @param {string} name - Cookie name (default: configured tokenCookieName)\n * @returns {string|null} Token value or null\n */\n getToken(name = this.config.tokenCookieName) {\n return this.getCookie(name);\n }\n\n /**\n * Set cookie from client-side (WARNING: Not HttpOnly, less secure than server-set cookies)\n * @param {string} name - Cookie name\n * @param {string} value - Cookie value\n * @param {number} maxAge - Max age in seconds\n */\n setCookie(name, value, maxAge) {\n if (typeof document === 'undefined') {\n logger.warn('Cannot set cookie in non-browser environment');\n return;\n }\n \n // Sanitize cookie name to prevent XSS\n const sanitizedName = name.replace(/[^a-zA-Z0-9_-]/g, '');\n if (!sanitizedName) {\n logger.error('Invalid cookie name provided');\n return;\n }\n \n const expires = new Date(Date.now() + maxAge * 1000).toUTCString();\n const isSecure = typeof window !== 'undefined' && window.location.protocol === 'https:';\n const secureFlag = isSecure ? '; Secure' : '';\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${sanitizedName}=${encodedValue}; path=/; expires=${expires}; SameSite=Lax${secureFlag}`;\n logger.debug(`Cookie set: ${sanitizedName}, expires in ${maxAge}s${isSecure ? ' (Secure)' : ''} [WARNING: Not HttpOnly]`);\n }\n\n /**\n * Start automatic token refresh timer\n * @param {number} interval - Refresh interval in milliseconds\n */\n startTokenRefresh(interval = this.config.refreshInterval) {\n this.stopTokenRefresh();\n logger.info(`Scheduling token refresh in ${interval / 1000} seconds`);\n\n this.refreshTimerId = setTimeout(async () => {\n logger.info('Auto-refreshing token...');\n try {\n await this.refreshToken();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n const errorCode = error?.code || 'UNKNOWN_ERROR';\n logger.error('Auto-refresh failed:', errorMessage);\n this.state.error = errorMessage;\n this.state.errorCode = errorCode;\n this.notifyListeners();\n }\n }, interval);\n }\n\n /**\n * Stop automatic token refresh timer\n */\n stopTokenRefresh() {\n if (this.refreshTimerId) {\n clearTimeout(this.refreshTimerId);\n this.refreshTimerId = null;\n logger.debug('Token refresh timer stopped');\n }\n }\n\n /**\n * Manually refresh the session token\n * @returns {Promise<Object>} Bootstrap response\n */\n async refreshToken() {\n logger.info('Manual token refresh triggered');\n this.state.initializationFailed = false;\n return this.initialize(true);\n }\n\n /**\n * Get current session status\n * @returns {Object} Current session state\n */\n getSessionStatus() {\n return {\n isInitialized: this.state.isInitialized,\n isLoading: this.state.isLoading,\n lastRefreshTime: this.state.lastRefreshTime,\n nextRefreshTime: this.state.nextRefreshTime,\n tokenExpiry: this.state.tokenExpiry,\n error: this.state.error,\n errorCode: this.state.errorCode,\n initializationFailed: this.state.initializationFailed,\n timeUntilRefresh: this.state.nextRefreshTime\n ? Math.max(0, this.state.nextRefreshTime - Date.now())\n : null,\n };\n }\n\n /**\n * Subscribe to session state changes\n * @param {Function} listener - Callback function to be called on state changes\n * @returns {Function} Unsubscribe function\n */\n subscribe(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('Listener must be a function');\n }\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Notify all listeners of state changes\n */\n notifyListeners() {\n const status = this.getSessionStatus();\n // Use Array.from to create a snapshot, preventing issues if listeners modify the Set\n Array.from(this.listeners).forEach(listener => {\n try {\n listener(status);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('Listener error:', errorMessage);\n }\n });\n }\n\n /**\n * Clean up and reset the session manager\n */\n destroy() {\n this.stopTokenRefresh();\n this.listeners.clear();\n this.state = {\n isInitialized: false,\n isLoading: false,\n lastRefreshTime: null,\n error: null,\n errorCode: null,\n nextRefreshTime: null,\n tokenExpiry: null,\n cfSessionId: null,\n initializationFailed: false,\n };\n // Clear cookies\n this.setCookie('cf-session-id', '', -1);\n this.setCookie('session_initialized', '', -1);\n logger.info('Destroyed');\n }\n\n /**\n * Get the singleton instance\n * @returns {SessionManager} Singleton instance\n */\n static getInstance() {\n if (!SessionManager.instance) {\n SessionManager.instance = new SessionManager();\n }\n return SessionManager.instance;\n }\n}\n\nexport default SessionManager;\n","import SessionManager from './SessionManager.js';\n\n/**\n * Fetch interceptor with automatic token refresh on 401\n * @param {string} url - Request URL\n * @param {Object} options - Fetch options\n * @returns {Promise<Response>} Fetch response\n */\nexport async function fetchInterceptor(url, options = {}) {\n const sessionManager = SessionManager.getInstance();\n \n // Ensure credentials are included to send cookies\n options.credentials = options.credentials || 'include';\n \n // Add headers\n options.headers = {\n ...options.headers,\n 'cf-session-id': sessionManager.getSessionId(),\n };\n \n // Add token to header if HTTP (not HTTPS)\n if (sessionManager.shouldUseTokenHeader()) {\n const token = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (token) {\n options.headers[sessionManager.config.tokenCookieName] = token;\n }\n }\n \n let response = await fetch(url, options);\n \n // Retry once if unauthorized with INVALID_SESSION\n if (response.status === 401) {\n const clonedResponse = response.clone();\n try {\n const data = await clonedResponse.json();\n if (data.error_msg === 'INVALID_SESSION') {\n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n const existingToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (!existingToken) {\n await sessionManager.refreshToken();\n }\n }\n options.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const newToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (newToken) {\n options.headers[sessionManager.config.tokenCookieName] = newToken;\n }\n }\n response = await fetch(url, options);\n }\n } catch (e) {\n // Not JSON or parsing failed, return original response\n }\n }\n \n return response;\n}\n\n/**\n * Axios interceptor with automatic token refresh on 401 and INVALID_SESSION\n * @param {Object} axiosInstance - Axios instance\n */\nexport function setupAxiosInterceptor(axiosInstance) {\n const sessionManager = SessionManager.getInstance();\n \n // Request interceptor to add cf-session-id and token\n axiosInstance.interceptors.request.use(\n (config) => {\n config.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const token = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (token) {\n config.headers[sessionManager.config.tokenCookieName] = token;\n }\n }\n return config;\n },\n (error) => Promise.reject(error)\n );\n \n // Response interceptor for token refresh\n axiosInstance.interceptors.response.use(\n (response) => response,\n async (error) => {\n const originalRequest = error.config;\n \n // Retry once if unauthorized with INVALID_SESSION\n if (error.response?.status === 401 && !originalRequest._retry) {\n if (error.response?.data?.error_msg === 'INVALID_SESSION') {\n originalRequest._retry = true;\n \n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n const existingToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (!existingToken) {\n await sessionManager.refreshToken();\n }\n }\n \n originalRequest.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const newToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (newToken) {\n originalRequest.headers[sessionManager.config.tokenCookieName] = newToken;\n }\n }\n return axiosInstance(originalRequest);\n }\n }\n \n return Promise.reject(error);\n }\n );\n \n return axiosInstance;\n}\n","import { useState, useEffect, useCallback } from 'react';\nimport SessionManager from '../SessionManager.js';\nimport { logger } from '../logger.js';\n\n/**\n * React hook for session management\n * Provides session state and controls for React components\n * \n * @param {Object} options - Configuration options\n * @param {boolean} options.autoInitialize - Automatically initialize on mount (default: true)\n * @returns {Object} Session state and controls\n */\nexport function useSession(options = {}) {\n const { autoInitialize = true } = options;\n\n const sessionManager = SessionManager.getInstance();\n\n const [sessionState, setSessionState] = useState(() =>\n sessionManager.getSessionStatus()\n );\n\n // Subscribe to session state changes\n useEffect(() => {\n const unsubscribe = sessionManager.subscribe((newState) => {\n setSessionState(newState);\n });\n\n return unsubscribe;\n }, []); // sessionManager is a singleton, no need to include in deps\n\n // Auto-initialize if enabled\n useEffect(() => {\n if (autoInitialize && !sessionState.isInitialized && !sessionState.isLoading && !sessionState.initializationFailed) {\n sessionManager.initialize().catch(error => {\n logger.error('Auto-initialization failed:', error);\n });\n }\n }, [autoInitialize, sessionState.isInitialized, sessionState.isLoading, sessionState.initializationFailed]);\n\n // Manual refresh function\n const refresh = useCallback(async () => {\n try {\n await sessionManager.refreshToken();\n } catch (error) {\n logger.error('Manual refresh failed:', error);\n throw error;\n }\n }, []);\n\n // Initialize function (for manual control)\n const initialize = useCallback(async () => {\n try {\n await sessionManager.initialize();\n } catch (error) {\n logger.error('Manual initialization failed:', error);\n throw error;\n }\n }, []);\n\n return {\n // State\n isInitialized: sessionState.isInitialized,\n isLoading: sessionState.isLoading,\n error: sessionState.error,\n lastRefreshTime: sessionState.lastRefreshTime,\n nextRefreshTime: sessionState.nextRefreshTime,\n timeUntilRefresh: sessionState.timeUntilRefresh,\n initializationFailed: sessionState.initializationFailed,\n\n // Actions\n refresh,\n initialize,\n };\n}\n\nexport default useSession;\n"],"names":["SessionError","Error","constructor","message","code","details","super","this","name","captureStackTrace","Object","setPrototypeOf","prototype","ConfigurationError","BootstrapError","NetworkError","SSRError","LOG_LEVELS","NONE","ERROR","WARN","INFO","DEBUG","logger","level","setLevel","toUpperCase","error","args","console","warn","info","debug","SessionManager","instance","config","bootstrapUrl","refreshInterval","tokenExpiry","maxRetries","tokenCookieName","state","isInitialized","isLoading","lastRefreshTime","errorCode","nextRefreshTime","cfSessionId","initializationFailed","refreshTimerId","listeners","Set","initializationPromise","configure","trim","undefined","isFinite","credentials","logLevel","initialize","skipCookieCheck","window","getCookie","Date","now","storedSessionId","setCfSessionId","generateSessionId","startTokenRefresh","notifyListeners","token","getToken","lastError","attempt","newSessionId","response","fetch","method","headers","ok","status","statusText","url","data","json","jsonError","originalError","setCookieHeader","get","serverSetCookie","includes","refresh_time","expire","setCookie","delay","Math","min","pow","Promise","resolve","setTimeout","isRefreshing","waitForRefresh","sessionId","maxAge","crypto","randomUUID","random","toString","substr","getSessionId","shouldUseTokenHeader","location","protocol","document","parts","cookie","split","length","decodeURIComponent","pop","shift","String","value","sanitizedName","replace","expires","toUTCString","isSecure","secureFlag","encodedValue","encodeURIComponent","interval","stopTokenRefresh","async","refreshToken","errorMessage","clearTimeout","getSessionStatus","timeUntilRefresh","max","subscribe","listener","TypeError","add","delete","Array","from","forEach","destroy","clear","getInstance","options","sessionManager","clonedResponse","clone","error_msg","newToken","e","axiosInstance","interceptors","request","use","reject","originalRequest","_retry","autoInitialize","sessionState","setSessionState","useState","useEffect","newState","catch","refresh","useCallback"],"mappings":"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"}
package/dist/index.esm.js CHANGED
@@ -1,2 +1,2 @@
1
- import{useState as e,useEffect as t,useCallback as i}from"react";class s extends Error{constructor(e,t,i={}){super(e),this.name="SessionError",this.code=t,this.details=i,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor),Object.setPrototypeOf(this,s.prototype)}}class r extends s{constructor(e,t){super(e,"CONFIGURATION_ERROR",t),this.name="ConfigurationError"}}class n extends s{constructor(e,t){super(e,"BOOTSTRAP_ERROR",t),this.name="BootstrapError"}}class o extends s{constructor(e,t){super(e,"NETWORK_ERROR",t),this.name="NetworkError"}}class a extends s{constructor(e,t={}){super(e,"SSR_ERROR",t),this.name="SSRError"}}const h={NONE:0,ERROR:1,WARN:2,INFO:3,DEBUG:4};const l=new class{constructor(){this.level=h.WARN}setLevel(e){this.level="string"==typeof e?h[e.toUpperCase()]??h.WARN:e}error(...e){this.level>=h.ERROR&&console.error("[SessionManager]",...e)}warn(...e){this.level>=h.WARN&&console.warn("[SessionManager]",...e)}info(...e){this.level>=h.INFO&&console.info("[SessionManager]",...e)}debug(...e){this.level>=h.DEBUG&&console.debug("[SessionManager]",...e)}};class f{constructor(){if(f.instance)return f.instance;this.config={bootstrapUrl:"/session/bootstrap",refreshInterval:15e5,tokenExpiry:18e5,maxRetries:3,tokenCookieName:"token"},this.state={isInitialized:!1,isLoading:!1,lastRefreshTime:null,error:null,errorCode:null,nextRefreshTime:null,tokenExpiry:null,cfSessionId:null,initializationFailed:!1},this.refreshTimerId=null,this.listeners=new Set,this.initializationPromise=null,f.instance=this}configure(e={}){if(!e.bootstrapUrl||"string"!=typeof e.bootstrapUrl||!e.bootstrapUrl.trim())throw new r("bootstrapUrl is required and must be a non-empty string",{bootstrapUrl:e.bootstrapUrl});if(void 0!==e.refreshInterval){if("number"!=typeof e.refreshInterval||!isFinite(e.refreshInterval))throw new r("refreshInterval must be a finite number",{refreshInterval:e.refreshInterval});if(e.refreshInterval<=0)throw new r("refreshInterval must be positive",{refreshInterval:e.refreshInterval})}if(void 0!==e.tokenExpiry){if("number"!=typeof e.tokenExpiry||!isFinite(e.tokenExpiry))throw new r("tokenExpiry must be a finite number",{tokenExpiry:e.tokenExpiry});if(e.tokenExpiry<=0)throw new r("tokenExpiry must be positive",{tokenExpiry:e.tokenExpiry})}this.config={...this.config,...e,credentials:void 0===e.credentials||e.credentials,logLevel:e.logLevel||"WARN"},l.setLevel(this.config.logLevel),this.config.refreshInterval&&this.config.tokenExpiry&&this.config.refreshInterval>=this.config.tokenExpiry&&l.warn("refreshInterval should be less than tokenExpiry to prevent race conditions")}async initialize(){if("undefined"==typeof window)throw new a("Cannot initialize in non-browser environment (SSR)");if(this.state.initializationFailed)throw l.warn("Initialization previously failed. Reload page to retry."),new n("Initialization failed after max retries. Reload page to retry.");if(this.initializationPromise)return l.debug("Initialization already in progress, waiting..."),this.initializationPromise;const e=this.getToken(this.config.tokenCookieName);if(e&&!this.state.isInitialized){if(l.info("Token found in cookie, skipping bootstrap API call"),this.state.isInitialized=!0,this.state.lastRefreshTime=Date.now(),this.state.nextRefreshTime=Date.now()+this.config.refreshInterval,this.state.tokenExpiry=this.config.tokenExpiry,"undefined"!=typeof sessionStorage){const e=sessionStorage.getItem("cf-session-id");e?this.state.cfSessionId=e:this.setCfSessionId(this.generateSessionId())}else this.setCfSessionId(this.generateSessionId());return this.startTokenRefresh(this.config.refreshInterval),this.notifyListeners(),{token:e}}this.state.isLoading=!0,this.state.error=null,this.notifyListeners(),this.initializationPromise=(async()=>{let e;for(let t=1;t<=this.config.maxRetries;t++)try{const e=this.generateSessionId();this.setCfSessionId(e),l.info(`Calling bootstrap API (attempt ${t}/${this.config.maxRetries}):`,this.config.bootstrapUrl);const i=await fetch(this.config.bootstrapUrl,{method:"GET",credentials:this.config.credentials?"include":"same-origin",headers:{"Content-Type":"application/json","cf-session-id":this.state.cfSessionId,...this.config.headers}});if(!i.ok)throw new n(`Bootstrap failed: ${i.status} ${i.statusText}`,{status:i.status,statusText:i.statusText,url:this.config.bootstrapUrl});let s;try{s=await i.json()}catch(e){throw new n("Bootstrap response is not valid JSON",{originalError:e.message})}l.info("Bootstrap successful");const r=i.headers.get("set-cookie"),o=r&&r.includes(this.config.tokenCookieName),a=s.refresh_time?1e3*s.refresh_time:this.config.refreshInterval,h=s.expire?1e3*s.expire:this.config.tokenExpiry;return s.token&&!o?(l.debug("Server did not set cookie, setting from SDK"),this.setCookie(this.config.tokenCookieName,s.token,h/1e3)):o&&l.debug("Server set cookie, skipping SDK cookie logic"),this.state.isInitialized=!0,this.state.isLoading=!1,this.state.lastRefreshTime=Date.now(),this.state.nextRefreshTime=Date.now()+a,this.state.tokenExpiry=h,this.state.error=null,this.startTokenRefresh(a),this.notifyListeners(),s}catch(i){if(e=i instanceof Error?i:new o("Unknown error occurred",{error:i}),l.error(`Bootstrap attempt ${t} failed:`,e.message),t<this.config.maxRetries){const e=Math.min(1e3*Math.pow(2,t-1),5e3);l.info(`Retrying in ${e}ms...`),await new Promise(t=>setTimeout(t,e))}}throw l.error("All bootstrap attempts failed"),this.state.isLoading=!1,this.state.error=e?.message||"Unknown error",this.state.errorCode=e?.code||"UNKNOWN_ERROR",this.state.initializationFailed=!0,this.notifyListeners(),e})();try{return await this.initializationPromise}finally{this.initializationPromise=null}}isRefreshing(){return null!==this.initializationPromise}async waitForRefresh(){return this.initializationPromise?(l.debug("Waiting for ongoing refresh to complete"),this.initializationPromise):Promise.resolve()}setCfSessionId(e){this.state.cfSessionId=e,"undefined"!=typeof sessionStorage&&sessionStorage.setItem("cf-session-id",e)}generateSessionId(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substr(2,9)}`}getSessionId(){return this.state.cfSessionId}shouldUseTokenHeader(){return"undefined"!=typeof window&&"http:"===window.location.protocol}getToken(e=this.config.tokenCookieName){if("undefined"==typeof document)return null;const t=`; ${document.cookie}`.split(`; ${e}=`);if(2===t.length)try{return decodeURIComponent(t.pop().split(";").shift())}catch(e){return l.error("Failed to decode cookie value:",e instanceof Error?e.message:String(e)),null}return null}setCookie(e,t,i){if("undefined"==typeof document)return void l.warn("Cannot set cookie in non-browser environment");const s=e.replace(/[^a-zA-Z0-9_-]/g,"");if(!s)return void l.error("Invalid cookie name provided");const r=new Date(Date.now()+1e3*i).toUTCString(),n="undefined"!=typeof window&&"https:"===window.location.protocol,o=n?"; Secure":"",a=encodeURIComponent(t);document.cookie=`${s}=${a}; path=/; expires=${r}; SameSite=Lax${o}`,l.debug(`Cookie set: ${s}, expires in ${i}s${n?" (Secure)":""} [WARNING: Not HttpOnly]`)}startTokenRefresh(e=this.config.refreshInterval){this.stopTokenRefresh(),l.info(`Scheduling token refresh in ${e/1e3} seconds`),this.refreshTimerId=setTimeout(async()=>{l.info("Auto-refreshing token...");try{await this.refreshToken()}catch(e){const t=e instanceof Error?e.message:String(e),i=e?.code||"UNKNOWN_ERROR";l.error("Auto-refresh failed:",t),this.state.error=t,this.state.errorCode=i,this.notifyListeners()}},e)}stopTokenRefresh(){this.refreshTimerId&&(clearTimeout(this.refreshTimerId),this.refreshTimerId=null,l.debug("Token refresh timer stopped"))}async refreshToken(){return l.info("Manual token refresh triggered"),this.state.initializationFailed=!1,this.initialize()}getSessionStatus(){return{isInitialized:this.state.isInitialized,isLoading:this.state.isLoading,lastRefreshTime:this.state.lastRefreshTime,nextRefreshTime:this.state.nextRefreshTime,tokenExpiry:this.state.tokenExpiry,error:this.state.error,errorCode:this.state.errorCode,initializationFailed:this.state.initializationFailed,timeUntilRefresh:this.state.nextRefreshTime?Math.max(0,this.state.nextRefreshTime-Date.now()):null}}subscribe(e){if("function"!=typeof e)throw new TypeError("Listener must be a function");return this.listeners.add(e),()=>{this.listeners.delete(e)}}notifyListeners(){const e=this.getSessionStatus();Array.from(this.listeners).forEach(t=>{try{t(e)}catch(e){const t=e instanceof Error?e.message:String(e);l.error("Listener error:",t)}})}destroy(){this.stopTokenRefresh(),this.listeners.clear(),this.state={isInitialized:!1,isLoading:!1,lastRefreshTime:null,error:null,errorCode:null,nextRefreshTime:null,tokenExpiry:null,cfSessionId:null,initializationFailed:!1},"undefined"!=typeof sessionStorage&&sessionStorage.removeItem("cf-session-id"),l.info("Destroyed")}static getInstance(){return f.instance||(f.instance=new f),f.instance}}function c(s={}){const{autoInitialize:r=!0}=s,n=f.getInstance(),[o,a]=e(()=>n.getSessionStatus());t(()=>n.subscribe(e=>{a(e)}),[]),t(()=>{!r||o.isInitialized||o.isLoading||o.initializationFailed||n.initialize().catch(e=>{l.error("Auto-initialization failed:",e)})},[r,o.isInitialized,o.isLoading,o.initializationFailed]);const h=i(async()=>{try{await n.refreshToken()}catch(e){throw l.error("Manual refresh failed:",e),e}},[]),c=i(async()=>{try{await n.initialize()}catch(e){throw l.error("Manual initialization failed:",e),e}},[]);return{isInitialized:o.isInitialized,isLoading:o.isLoading,error:o.error,lastRefreshTime:o.lastRefreshTime,nextRefreshTime:o.nextRefreshTime,timeUntilRefresh:o.timeUntilRefresh,initializationFailed:o.initializationFailed,refresh:h,initialize:c}}async function d(e,t={}){const i=f.getInstance();if(t.headers={...t.headers,"cf-session-id":i.getSessionId()},i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(t.headers[i.config.tokenCookieName]=e)}let s=await fetch(e,t);if(401===s.status){const r=s.clone();try{if("INVALID_SESSION"===(await r.json()).error_msg){if(i.isRefreshing()?await i.waitForRefresh():await i.refreshToken(),t.headers["cf-session-id"]=i.getSessionId(),i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(t.headers[i.config.tokenCookieName]=e)}s=await fetch(e,t)}}catch(e){}}return s}function u(e){const t=f.getInstance();return e.interceptors.request.use(e=>{if(e.headers["cf-session-id"]=t.getSessionId(),t.shouldUseTokenHeader()){const i=t.getToken(t.config.tokenCookieName);i&&(e.headers[t.config.tokenCookieName]=i)}return e},e=>Promise.reject(e)),e.interceptors.response.use(e=>e,async i=>{const s=i.config;if(401===i.response?.status&&!s._retry&&"INVALID_SESSION"===i.response?.data?.error_msg){if(s._retry=!0,t.isRefreshing()?await t.waitForRefresh():await t.refreshToken(),s.headers["cf-session-id"]=t.getSessionId(),t.shouldUseTokenHeader()){const e=t.getToken(t.config.tokenCookieName);e&&(s.headers[t.config.tokenCookieName]=e)}return e(s)}return Promise.reject(i)}),e}export{n as BootstrapError,r as ConfigurationError,h as LOG_LEVELS,o as NetworkError,a as SSRError,s as SessionError,f as SessionManager,f as default,d as fetchInterceptor,l as logger,u as setupAxiosInterceptor,c as useSession};
1
+ import{useState as e,useEffect as t,useCallback as i}from"react";class s extends Error{constructor(e,t,i={}){super(e),this.name="SessionError",this.code=t,this.details=i,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor),Object.setPrototypeOf(this,s.prototype)}}class r extends s{constructor(e,t){super(e,"CONFIGURATION_ERROR",t),this.name="ConfigurationError"}}class n extends s{constructor(e,t){super(e,"BOOTSTRAP_ERROR",t),this.name="BootstrapError"}}class o extends s{constructor(e,t){super(e,"NETWORK_ERROR",t),this.name="NetworkError"}}class a extends s{constructor(e,t={}){super(e,"SSR_ERROR",t),this.name="SSRError"}}const l={NONE:0,ERROR:1,WARN:2,INFO:3,DEBUG:4};const h=new class{constructor(){this.level=l.WARN}setLevel(e){this.level="string"==typeof e?l[e.toUpperCase()]??l.WARN:e}error(...e){this.level>=l.ERROR&&console.error("[SessionManager]",...e)}warn(...e){this.level>=l.WARN&&console.warn("[SessionManager]",...e)}info(...e){this.level>=l.INFO&&console.info("[SessionManager]",...e)}debug(...e){this.level>=l.DEBUG&&console.debug("[SessionManager]",...e)}};class f{constructor(){if(f.instance)return f.instance;this.config={bootstrapUrl:"/session/bootstrap",refreshInterval:15e5,tokenExpiry:18e5,maxRetries:3,tokenCookieName:"token"},this.state={isInitialized:!1,isLoading:!1,lastRefreshTime:null,error:null,errorCode:null,nextRefreshTime:null,tokenExpiry:null,cfSessionId:null,initializationFailed:!1},this.refreshTimerId=null,this.listeners=new Set,this.initializationPromise=null,f.instance=this}configure(e={}){if(!e.bootstrapUrl||"string"!=typeof e.bootstrapUrl||!e.bootstrapUrl.trim())throw new r("bootstrapUrl is required and must be a non-empty string",{bootstrapUrl:e.bootstrapUrl});if(void 0!==e.refreshInterval){if("number"!=typeof e.refreshInterval||!isFinite(e.refreshInterval))throw new r("refreshInterval must be a finite number",{refreshInterval:e.refreshInterval});if(e.refreshInterval<=0)throw new r("refreshInterval must be positive",{refreshInterval:e.refreshInterval})}if(void 0!==e.tokenExpiry){if("number"!=typeof e.tokenExpiry||!isFinite(e.tokenExpiry))throw new r("tokenExpiry must be a finite number",{tokenExpiry:e.tokenExpiry});if(e.tokenExpiry<=0)throw new r("tokenExpiry must be positive",{tokenExpiry:e.tokenExpiry})}this.config={...this.config,...e,credentials:void 0===e.credentials||e.credentials,logLevel:e.logLevel||"WARN"},h.setLevel(this.config.logLevel),this.config.refreshInterval&&this.config.tokenExpiry&&this.config.refreshInterval>=this.config.tokenExpiry&&h.warn("refreshInterval should be less than tokenExpiry to prevent race conditions")}async initialize(e=!1){if("undefined"==typeof window)throw new a("Cannot initialize in non-browser environment (SSR)");if(this.state.initializationFailed)throw h.warn("Initialization previously failed. Reload page to retry."),new n("Initialization failed after max retries. Reload page to retry.");if(this.initializationPromise)return h.debug("Initialization already in progress, waiting..."),this.initializationPromise;if(!e){if("true"===this.getCookie("session_initialized")){h.info("Session already initialized, skipping bootstrap API call"),this.state.isInitialized=!0,this.state.lastRefreshTime=Date.now(),this.state.nextRefreshTime=Date.now()+this.config.refreshInterval,this.state.tokenExpiry=this.config.tokenExpiry;const e=this.getCookie("cf-session-id");return e?this.state.cfSessionId=e:this.setCfSessionId(this.generateSessionId()),this.startTokenRefresh(this.config.refreshInterval),this.notifyListeners(),{token:this.getToken(this.config.tokenCookieName)}}}this.state.isLoading=!0,this.state.error=null,this.notifyListeners(),this.initializationPromise=(async()=>{let e;for(let t=1;t<=this.config.maxRetries;t++)try{const e=this.generateSessionId();this.setCfSessionId(e),h.info(`Calling bootstrap API (attempt ${t}/${this.config.maxRetries}):`,this.config.bootstrapUrl);const i=await fetch(this.config.bootstrapUrl,{method:"GET",credentials:this.config.credentials?"include":"same-origin",headers:{"Content-Type":"application/json","cf-session-id":this.state.cfSessionId,...this.config.headers}});if(!i.ok)throw new n(`Bootstrap failed: ${i.status} ${i.statusText}`,{status:i.status,statusText:i.statusText,url:this.config.bootstrapUrl});let s;try{s=await i.json()}catch(e){throw new n("Bootstrap response is not valid JSON",{originalError:e.message})}h.info("Bootstrap successful");const r=i.headers.get("set-cookie"),o=r&&r.includes(this.config.tokenCookieName),a=s.refresh_time?1e3*s.refresh_time:this.config.refreshInterval,l=s.expire?1e3*s.expire:this.config.tokenExpiry;return s.token&&!o?(h.debug("Server did not set cookie, setting from SDK"),this.setCookie(this.config.tokenCookieName,s.token,l/1e3)):o&&h.debug("Server set cookie, skipping SDK cookie logic"),this.state.isInitialized=!0,this.state.isLoading=!1,this.state.lastRefreshTime=Date.now(),this.state.nextRefreshTime=Date.now()+a,this.state.tokenExpiry=l,this.state.error=null,this.setCookie("session_initialized","true",l/1e3),this.startTokenRefresh(a),this.notifyListeners(),s}catch(i){if(e=i instanceof Error?i:new o("Unknown error occurred",{error:i}),h.error(`Bootstrap attempt ${t} failed:`,e.message),t<this.config.maxRetries){const e=Math.min(1e3*Math.pow(2,t-1),5e3);h.info(`Retrying in ${e}ms...`),await new Promise(t=>setTimeout(t,e))}}throw h.error("All bootstrap attempts failed"),this.state.isLoading=!1,this.state.error=e?.message||"Unknown error",this.state.errorCode=e?.code||"UNKNOWN_ERROR",this.state.initializationFailed=!0,this.notifyListeners(),e})();try{return await this.initializationPromise}finally{this.initializationPromise=null}}isRefreshing(){return null!==this.initializationPromise}async waitForRefresh(){return this.initializationPromise?(h.debug("Waiting for ongoing refresh to complete"),this.initializationPromise):Promise.resolve()}setCfSessionId(e){this.state.cfSessionId=e;const t=this.config.tokenExpiry/1e3;this.setCookie("cf-session-id",e,t)}generateSessionId(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).substr(2,9)}`}getSessionId(){return this.state.cfSessionId}shouldUseTokenHeader(){return"undefined"!=typeof window&&"http:"===window.location.protocol}getCookie(e){if("undefined"==typeof document)return null;const t=`; ${document.cookie}`.split(`; ${e}=`);if(2===t.length)try{return decodeURIComponent(t.pop().split(";").shift())}catch(e){return h.error("Failed to decode cookie value:",e instanceof Error?e.message:String(e)),null}return null}getToken(e=this.config.tokenCookieName){return this.getCookie(e)}setCookie(e,t,i){if("undefined"==typeof document)return void h.warn("Cannot set cookie in non-browser environment");const s=e.replace(/[^a-zA-Z0-9_-]/g,"");if(!s)return void h.error("Invalid cookie name provided");const r=new Date(Date.now()+1e3*i).toUTCString(),n="undefined"!=typeof window&&"https:"===window.location.protocol,o=n?"; Secure":"",a=encodeURIComponent(t);document.cookie=`${s}=${a}; path=/; expires=${r}; SameSite=Lax${o}`,h.debug(`Cookie set: ${s}, expires in ${i}s${n?" (Secure)":""} [WARNING: Not HttpOnly]`)}startTokenRefresh(e=this.config.refreshInterval){this.stopTokenRefresh(),h.info(`Scheduling token refresh in ${e/1e3} seconds`),this.refreshTimerId=setTimeout(async()=>{h.info("Auto-refreshing token...");try{await this.refreshToken()}catch(e){const t=e instanceof Error?e.message:String(e),i=e?.code||"UNKNOWN_ERROR";h.error("Auto-refresh failed:",t),this.state.error=t,this.state.errorCode=i,this.notifyListeners()}},e)}stopTokenRefresh(){this.refreshTimerId&&(clearTimeout(this.refreshTimerId),this.refreshTimerId=null,h.debug("Token refresh timer stopped"))}async refreshToken(){return h.info("Manual token refresh triggered"),this.state.initializationFailed=!1,this.initialize(!0)}getSessionStatus(){return{isInitialized:this.state.isInitialized,isLoading:this.state.isLoading,lastRefreshTime:this.state.lastRefreshTime,nextRefreshTime:this.state.nextRefreshTime,tokenExpiry:this.state.tokenExpiry,error:this.state.error,errorCode:this.state.errorCode,initializationFailed:this.state.initializationFailed,timeUntilRefresh:this.state.nextRefreshTime?Math.max(0,this.state.nextRefreshTime-Date.now()):null}}subscribe(e){if("function"!=typeof e)throw new TypeError("Listener must be a function");return this.listeners.add(e),()=>{this.listeners.delete(e)}}notifyListeners(){const e=this.getSessionStatus();Array.from(this.listeners).forEach(t=>{try{t(e)}catch(e){const t=e instanceof Error?e.message:String(e);h.error("Listener error:",t)}})}destroy(){this.stopTokenRefresh(),this.listeners.clear(),this.state={isInitialized:!1,isLoading:!1,lastRefreshTime:null,error:null,errorCode:null,nextRefreshTime:null,tokenExpiry:null,cfSessionId:null,initializationFailed:!1},this.setCookie("cf-session-id","",-1),this.setCookie("session_initialized","",-1),h.info("Destroyed")}static getInstance(){return f.instance||(f.instance=new f),f.instance}}function c(s={}){const{autoInitialize:r=!0}=s,n=f.getInstance(),[o,a]=e(()=>n.getSessionStatus());t(()=>n.subscribe(e=>{a(e)}),[]),t(()=>{!r||o.isInitialized||o.isLoading||o.initializationFailed||n.initialize().catch(e=>{h.error("Auto-initialization failed:",e)})},[r,o.isInitialized,o.isLoading,o.initializationFailed]);const l=i(async()=>{try{await n.refreshToken()}catch(e){throw h.error("Manual refresh failed:",e),e}},[]),c=i(async()=>{try{await n.initialize()}catch(e){throw h.error("Manual initialization failed:",e),e}},[]);return{isInitialized:o.isInitialized,isLoading:o.isLoading,error:o.error,lastRefreshTime:o.lastRefreshTime,nextRefreshTime:o.nextRefreshTime,timeUntilRefresh:o.timeUntilRefresh,initializationFailed:o.initializationFailed,refresh:l,initialize:c}}async function d(e,t={}){const i=f.getInstance();if(t.credentials=t.credentials||"include",t.headers={...t.headers,"cf-session-id":i.getSessionId()},i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(t.headers[i.config.tokenCookieName]=e)}let s=await fetch(e,t);if(401===s.status){const r=s.clone();try{if("INVALID_SESSION"===(await r.json()).error_msg){if(i.isRefreshing())await i.waitForRefresh();else{i.getToken(i.config.tokenCookieName)||await i.refreshToken()}if(t.headers["cf-session-id"]=i.getSessionId(),i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(t.headers[i.config.tokenCookieName]=e)}s=await fetch(e,t)}}catch(e){}}return s}function u(e){const t=f.getInstance();return e.interceptors.request.use(e=>{if(e.headers["cf-session-id"]=t.getSessionId(),t.shouldUseTokenHeader()){const i=t.getToken(t.config.tokenCookieName);i&&(e.headers[t.config.tokenCookieName]=i)}return e},e=>Promise.reject(e)),e.interceptors.response.use(e=>e,async i=>{const s=i.config;if(401===i.response?.status&&!s._retry&&"INVALID_SESSION"===i.response?.data?.error_msg){if(s._retry=!0,t.isRefreshing())await t.waitForRefresh();else{t.getToken(t.config.tokenCookieName)||await t.refreshToken()}if(s.headers["cf-session-id"]=t.getSessionId(),t.shouldUseTokenHeader()){const e=t.getToken(t.config.tokenCookieName);e&&(s.headers[t.config.tokenCookieName]=e)}return e(s)}return Promise.reject(i)}),e}export{n as BootstrapError,r as ConfigurationError,l as LOG_LEVELS,o as NetworkError,a as SSRError,s as SessionError,f as SessionManager,f as default,d as fetchInterceptor,h as logger,u as setupAxiosInterceptor,c as useSession};
2
2
  //# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/errors.js","../src/logger.js","../src/SessionManager.js","../src/hooks/useSession.js","../src/interceptors.js"],"sourcesContent":["/**\n * Custom error classes for SessionManager\n */\n\nexport class SessionError extends Error {\n constructor(message, code, details = {}) {\n super(message);\n this.name = 'SessionError';\n this.code = code;\n this.details = details;\n \n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n \n // Set the prototype explicitly for proper instanceof checks\n Object.setPrototypeOf(this, SessionError.prototype);\n }\n}\n\nexport class ConfigurationError extends SessionError {\n constructor(message, details) {\n super(message, 'CONFIGURATION_ERROR', details);\n this.name = 'ConfigurationError';\n }\n}\n\nexport class BootstrapError extends SessionError {\n constructor(message, details) {\n super(message, 'BOOTSTRAP_ERROR', details);\n this.name = 'BootstrapError';\n }\n}\n\nexport class NetworkError extends SessionError {\n constructor(message, details) {\n super(message, 'NETWORK_ERROR', details);\n this.name = 'NetworkError';\n }\n}\n\nexport class SSRError extends SessionError {\n constructor(message, details = {}) {\n super(message, 'SSR_ERROR', details);\n this.name = 'SSRError';\n }\n}\n","/**\n * Logger utility with configurable log levels\n */\n\nconst LOG_LEVELS = {\n NONE: 0,\n ERROR: 1,\n WARN: 2,\n INFO: 3,\n DEBUG: 4\n};\n\nclass Logger {\n constructor() {\n this.level = LOG_LEVELS.WARN; // Default to WARN in production\n }\n\n setLevel(level) {\n if (typeof level === 'string') {\n this.level = LOG_LEVELS[level.toUpperCase()] ?? LOG_LEVELS.WARN;\n } else {\n this.level = level;\n }\n }\n\n error(...args) {\n if (this.level >= LOG_LEVELS.ERROR) {\n console.error('[SessionManager]', ...args);\n }\n }\n\n warn(...args) {\n if (this.level >= LOG_LEVELS.WARN) {\n console.warn('[SessionManager]', ...args);\n }\n }\n\n info(...args) {\n if (this.level >= LOG_LEVELS.INFO) {\n console.info('[SessionManager]', ...args);\n }\n }\n\n debug(...args) {\n if (this.level >= LOG_LEVELS.DEBUG) {\n console.debug('[SessionManager]', ...args);\n }\n }\n}\n\nexport const logger = new Logger();\nexport { LOG_LEVELS };\n","import {BootstrapError, ConfigurationError, NetworkError, SSRError} from './errors.js';\nimport {logger} from './logger.js';\n\n/**\n * SessionManager - Core session token management class\n * Handles session bootstrap, automatic token refresh, and lifecycle management\n */\nclass SessionManager {\n constructor() {\n if (SessionManager.instance) {\n return SessionManager.instance;\n }\n\n this.config = {\n bootstrapUrl: '/session/bootstrap',\n refreshInterval: 25 * 60 * 1000, // 25 minutes in milliseconds\n tokenExpiry: 30 * 60 * 1000, // 30 minutes in milliseconds\n maxRetries: 3,\n tokenCookieName: 'token', // Cookie name to read token from\n };\n\n this.state = {\n isInitialized: false,\n isLoading: false,\n lastRefreshTime: null,\n error: null,\n errorCode: null,\n nextRefreshTime: null,\n tokenExpiry: null,\n cfSessionId: null,\n initializationFailed: false,\n };\n\n this.refreshTimerId = null;\n this.listeners = new Set();\n this.initializationPromise = null;\n\n SessionManager.instance = this;\n }\n\n /**\n * Configure the session manager\n * @param {Object} config - Configuration options\n * @param {string} config.bootstrapUrl - URL for bootstrap API endpoint\n * @param {number} config.refreshInterval - Interval for token refresh in milliseconds (default: 25 mins)\n * @param {number} config.tokenExpiry - Token expiry time in milliseconds (default: 30 mins)\n * @param {number} config.maxRetries - Maximum retry attempts on failure (default: 3)\n * @param {Object} config.headers - Additional headers for API calls\n * @param {boolean} config.credentials - Include credentials in requests (default: true)\n * @param {string} config.tokenCookieName - Cookie name to read token from (default: 'token')\n */\n configure(config = {}) {\n if (!config.bootstrapUrl || typeof config.bootstrapUrl !== 'string' || !config.bootstrapUrl.trim()) {\n throw new ConfigurationError('bootstrapUrl is required and must be a non-empty string', { bootstrapUrl: config.bootstrapUrl });\n }\n \n if (config.refreshInterval !== undefined) {\n if (typeof config.refreshInterval !== 'number' || !isFinite(config.refreshInterval)) {\n throw new ConfigurationError('refreshInterval must be a finite number', { refreshInterval: config.refreshInterval });\n }\n if (config.refreshInterval <= 0) {\n throw new ConfigurationError('refreshInterval must be positive', { refreshInterval: config.refreshInterval });\n }\n }\n \n if (config.tokenExpiry !== undefined) {\n if (typeof config.tokenExpiry !== 'number' || !isFinite(config.tokenExpiry)) {\n throw new ConfigurationError('tokenExpiry must be a finite number', { tokenExpiry: config.tokenExpiry });\n }\n if (config.tokenExpiry <= 0) {\n throw new ConfigurationError('tokenExpiry must be positive', { tokenExpiry: config.tokenExpiry });\n }\n }\n\n this.config = {\n ...this.config,\n ...config,\n credentials: config.credentials !== undefined ? config.credentials : true,\n logLevel: config.logLevel || 'WARN'\n };\n\n logger.setLevel(this.config.logLevel);\n\n if (this.config.refreshInterval && this.config.tokenExpiry && \n this.config.refreshInterval >= this.config.tokenExpiry) {\n logger.warn('refreshInterval should be less than tokenExpiry to prevent race conditions');\n }\n }\n\n /**\n * Initialize session by calling bootstrap API\n * @returns {Promise<Object>} Bootstrap response\n */\n async initialize() {\n if (typeof window === 'undefined') {\n throw new SSRError('Cannot initialize in non-browser environment (SSR)');\n }\n \n if (this.state.initializationFailed) {\n logger.warn('Initialization previously failed. Reload page to retry.');\n throw new BootstrapError('Initialization failed after max retries. Reload page to retry.');\n }\n \n // Return existing promise if refresh already in progress\n if (this.initializationPromise) {\n logger.debug('Initialization already in progress, waiting...');\n return this.initializationPromise;\n }\n\n // Check if token already exists in cookie\n const existingToken = this.getToken(this.config.tokenCookieName);\n if (existingToken && !this.state.isInitialized) {\n logger.info('Token found in cookie, skipping bootstrap API call');\n this.state.isInitialized = true;\n this.state.lastRefreshTime = Date.now();\n this.state.nextRefreshTime = Date.now() + this.config.refreshInterval;\n this.state.tokenExpiry = this.config.tokenExpiry;\n \n // Restore or generate cf-session-id\n if (typeof sessionStorage !== 'undefined') {\n const storedSessionId = sessionStorage.getItem('cf-session-id');\n if (storedSessionId) {\n this.state.cfSessionId = storedSessionId;\n } else {\n this.setCfSessionId(this.generateSessionId());\n }\n } else {\n this.setCfSessionId(this.generateSessionId());\n }\n \n this.startTokenRefresh(this.config.refreshInterval);\n this.notifyListeners();\n return { token: existingToken };\n }\n\n this.state.isLoading = true;\n this.state.error = null;\n this.notifyListeners();\n\n this.initializationPromise = (async () => {\n let lastError;\n \n for (let attempt = 1; attempt <= this.config.maxRetries; attempt++) {\n try {\n // Generate new cf-session-id for each bootstrap call\n const newSessionId = this.generateSessionId();\n this.setCfSessionId(newSessionId);\n\n logger.info(`Calling bootstrap API (attempt ${attempt}/${this.config.maxRetries}):`, this.config.bootstrapUrl);\n\n const response = await fetch(this.config.bootstrapUrl, {\n method: 'GET',\n credentials: this.config.credentials ? 'include' : 'same-origin',\n headers: {\n 'Content-Type': 'application/json',\n 'cf-session-id': this.state.cfSessionId,\n ...this.config.headers,\n },\n });\n\n if (!response.ok) {\n throw new BootstrapError(`Bootstrap failed: ${response.status} ${response.statusText}`, {\n status: response.status,\n statusText: response.statusText,\n url: this.config.bootstrapUrl\n });\n }\n\n let data;\n try {\n data = await response.json();\n } catch (jsonError) {\n throw new BootstrapError('Bootstrap response is not valid JSON', { originalError: jsonError.message });\n }\n logger.info('Bootstrap successful');\n\n // Check if server set a cookie\n const setCookieHeader = response.headers.get('set-cookie');\n const serverSetCookie = setCookieHeader && setCookieHeader.includes(this.config.tokenCookieName);\n\n // Use refresh_time from response (in seconds) or fall back to config\n const refreshInterval = data.refresh_time \n ? data.refresh_time * 1000 \n : this.config.refreshInterval;\n\n // Calculate token expiry: refresh_time + window_time (both in seconds)\n const tokenExpiry = data.expire\n ? data.expire * 1000\n : this.config.tokenExpiry;\n\n if (data.token && !serverSetCookie) {\n logger.debug('Server did not set cookie, setting from SDK');\n this.setCookie(this.config.tokenCookieName, data.token, tokenExpiry / 1000);\n } else if (serverSetCookie) {\n logger.debug('Server set cookie, skipping SDK cookie logic');\n }\n\n this.state.isInitialized = true;\n this.state.isLoading = false;\n this.state.lastRefreshTime = Date.now();\n this.state.nextRefreshTime = Date.now() + refreshInterval;\n this.state.tokenExpiry = tokenExpiry;\n this.state.error = null;\n\n // Start automatic refresh timer with dynamic interval\n this.startTokenRefresh(refreshInterval);\n\n this.notifyListeners();\n return data;\n } catch (error) {\n lastError = error instanceof Error ? error : new NetworkError('Unknown error occurred', { error });\n logger.error(`Bootstrap attempt ${attempt} failed:`, lastError.message);\n \n if (attempt < this.config.maxRetries) {\n const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);\n logger.info(`Retrying in ${delay}ms...`);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n \n logger.error('All bootstrap attempts failed');\n this.state.isLoading = false;\n this.state.error = lastError?.message || 'Unknown error';\n this.state.errorCode = lastError?.code || 'UNKNOWN_ERROR';\n this.state.initializationFailed = true;\n this.notifyListeners();\n throw lastError;\n })();\n\n try {\n return await this.initializationPromise;\n } finally {\n this.initializationPromise = null;\n }\n }\n\n /**\n * Check if token refresh is currently in progress\n * @returns {boolean} True if refresh is in progress\n */\n isRefreshing() {\n return this.initializationPromise !== null;\n }\n\n /**\n * Wait for ongoing refresh to complete\n * @returns {Promise<Object>} Bootstrap response from ongoing refresh\n */\n async waitForRefresh() {\n if (this.initializationPromise) {\n logger.debug('Waiting for ongoing refresh to complete');\n return this.initializationPromise;\n }\n return Promise.resolve();\n }\n\n /**\n * Set cf-session-id\n * @param {string} sessionId - Session ID\n */\n setCfSessionId(sessionId) {\n this.state.cfSessionId = sessionId;\n if (typeof sessionStorage !== 'undefined') {\n sessionStorage.setItem('cf-session-id', sessionId);\n }\n }\n\n /**\n * Generate unique session ID\n * @returns {string} Generated session ID\n */\n generateSessionId() {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }\n\n /**\n * Get cf-session-id for requests\n * @returns {string} Current cf-session-id\n */\n getSessionId() {\n return this.state.cfSessionId;\n }\n\n /**\n * Check if token should be added to headers (HTTP or cross-origin)\n * @returns {boolean} True if token should be added to headers\n */\n shouldUseTokenHeader() {\n if (typeof window === 'undefined') return false;\n return window.location.protocol === 'http:';\n }\n\n /**\n * Get token from cookie\n * @param {string} name - Cookie name (default: configured tokenCookieName)\n * @returns {string|null} Token value or null\n */\n getToken(name = this.config.tokenCookieName) {\n if (typeof document === 'undefined') return null;\n const value = `; ${document.cookie}`;\n const parts = value.split(`; ${name}=`);\n if (parts.length === 2) {\n try {\n return decodeURIComponent(parts.pop().split(';').shift());\n } catch (error) {\n logger.error('Failed to decode cookie value:', error instanceof Error ? error.message : String(error));\n return null;\n }\n }\n return null;\n }\n\n /**\n * Set cookie from client-side (WARNING: Not HttpOnly, less secure than server-set cookies)\n * @param {string} name - Cookie name\n * @param {string} value - Cookie value\n * @param {number} maxAge - Max age in seconds\n */\n setCookie(name, value, maxAge) {\n if (typeof document === 'undefined') {\n logger.warn('Cannot set cookie in non-browser environment');\n return;\n }\n \n // Sanitize cookie name to prevent XSS\n const sanitizedName = name.replace(/[^a-zA-Z0-9_-]/g, '');\n if (!sanitizedName) {\n logger.error('Invalid cookie name provided');\n return;\n }\n \n const expires = new Date(Date.now() + maxAge * 1000).toUTCString();\n const isSecure = typeof window !== 'undefined' && window.location.protocol === 'https:';\n const secureFlag = isSecure ? '; Secure' : '';\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${sanitizedName}=${encodedValue}; path=/; expires=${expires}; SameSite=Lax${secureFlag}`;\n logger.debug(`Cookie set: ${sanitizedName}, expires in ${maxAge}s${isSecure ? ' (Secure)' : ''} [WARNING: Not HttpOnly]`);\n }\n\n /**\n * Start automatic token refresh timer\n * @param {number} interval - Refresh interval in milliseconds\n */\n startTokenRefresh(interval = this.config.refreshInterval) {\n this.stopTokenRefresh();\n logger.info(`Scheduling token refresh in ${interval / 1000} seconds`);\n\n this.refreshTimerId = setTimeout(async () => {\n logger.info('Auto-refreshing token...');\n try {\n await this.refreshToken();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n const errorCode = error?.code || 'UNKNOWN_ERROR';\n logger.error('Auto-refresh failed:', errorMessage);\n this.state.error = errorMessage;\n this.state.errorCode = errorCode;\n this.notifyListeners();\n }\n }, interval);\n }\n\n /**\n * Stop automatic token refresh timer\n */\n stopTokenRefresh() {\n if (this.refreshTimerId) {\n clearTimeout(this.refreshTimerId);\n this.refreshTimerId = null;\n logger.debug('Token refresh timer stopped');\n }\n }\n\n /**\n * Manually refresh the session token\n * @returns {Promise<Object>} Bootstrap response\n */\n async refreshToken() {\n logger.info('Manual token refresh triggered');\n this.state.initializationFailed = false;\n return this.initialize();\n }\n\n /**\n * Get current session status\n * @returns {Object} Current session state\n */\n getSessionStatus() {\n return {\n isInitialized: this.state.isInitialized,\n isLoading: this.state.isLoading,\n lastRefreshTime: this.state.lastRefreshTime,\n nextRefreshTime: this.state.nextRefreshTime,\n tokenExpiry: this.state.tokenExpiry,\n error: this.state.error,\n errorCode: this.state.errorCode,\n initializationFailed: this.state.initializationFailed,\n timeUntilRefresh: this.state.nextRefreshTime\n ? Math.max(0, this.state.nextRefreshTime - Date.now())\n : null,\n };\n }\n\n /**\n * Subscribe to session state changes\n * @param {Function} listener - Callback function to be called on state changes\n * @returns {Function} Unsubscribe function\n */\n subscribe(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('Listener must be a function');\n }\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Notify all listeners of state changes\n */\n notifyListeners() {\n const status = this.getSessionStatus();\n // Use Array.from to create a snapshot, preventing issues if listeners modify the Set\n Array.from(this.listeners).forEach(listener => {\n try {\n listener(status);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('Listener error:', errorMessage);\n }\n });\n }\n\n /**\n * Clean up and reset the session manager\n */\n destroy() {\n this.stopTokenRefresh();\n this.listeners.clear();\n this.state = {\n isInitialized: false,\n isLoading: false,\n lastRefreshTime: null,\n error: null,\n errorCode: null,\n nextRefreshTime: null,\n tokenExpiry: null,\n cfSessionId: null,\n initializationFailed: false,\n };\n if (typeof sessionStorage !== 'undefined') {\n sessionStorage.removeItem('cf-session-id');\n }\n logger.info('Destroyed');\n }\n\n /**\n * Get the singleton instance\n * @returns {SessionManager} Singleton instance\n */\n static getInstance() {\n if (!SessionManager.instance) {\n SessionManager.instance = new SessionManager();\n }\n return SessionManager.instance;\n }\n}\n\nexport default SessionManager;\n","import { useState, useEffect, useCallback } from 'react';\nimport SessionManager from '../SessionManager.js';\nimport { logger } from '../logger.js';\n\n/**\n * React hook for session management\n * Provides session state and controls for React components\n * \n * @param {Object} options - Configuration options\n * @param {boolean} options.autoInitialize - Automatically initialize on mount (default: true)\n * @returns {Object} Session state and controls\n */\nexport function useSession(options = {}) {\n const { autoInitialize = true } = options;\n\n const sessionManager = SessionManager.getInstance();\n\n const [sessionState, setSessionState] = useState(() =>\n sessionManager.getSessionStatus()\n );\n\n // Subscribe to session state changes\n useEffect(() => {\n const unsubscribe = sessionManager.subscribe((newState) => {\n setSessionState(newState);\n });\n\n return unsubscribe;\n }, []); // sessionManager is a singleton, no need to include in deps\n\n // Auto-initialize if enabled\n useEffect(() => {\n if (autoInitialize && !sessionState.isInitialized && !sessionState.isLoading && !sessionState.initializationFailed) {\n sessionManager.initialize().catch(error => {\n logger.error('Auto-initialization failed:', error);\n });\n }\n }, [autoInitialize, sessionState.isInitialized, sessionState.isLoading, sessionState.initializationFailed]);\n\n // Manual refresh function\n const refresh = useCallback(async () => {\n try {\n await sessionManager.refreshToken();\n } catch (error) {\n logger.error('Manual refresh failed:', error);\n throw error;\n }\n }, []);\n\n // Initialize function (for manual control)\n const initialize = useCallback(async () => {\n try {\n await sessionManager.initialize();\n } catch (error) {\n logger.error('Manual initialization failed:', error);\n throw error;\n }\n }, []);\n\n return {\n // State\n isInitialized: sessionState.isInitialized,\n isLoading: sessionState.isLoading,\n error: sessionState.error,\n lastRefreshTime: sessionState.lastRefreshTime,\n nextRefreshTime: sessionState.nextRefreshTime,\n timeUntilRefresh: sessionState.timeUntilRefresh,\n initializationFailed: sessionState.initializationFailed,\n\n // Actions\n refresh,\n initialize,\n };\n}\n\nexport default useSession;\n","import SessionManager from './SessionManager.js';\nimport { logger } from './logger.js';\n\n/**\n * Fetch interceptor with automatic token refresh on 401\n * @param {string} url - Request URL\n * @param {Object} options - Fetch options\n * @returns {Promise<Response>} Fetch response\n */\nexport async function fetchInterceptor(url, options = {}) {\n const sessionManager = SessionManager.getInstance();\n \n // Add headers\n options.headers = {\n ...options.headers,\n 'cf-session-id': sessionManager.getSessionId(),\n };\n \n // Add token to header if HTTP (not HTTPS)\n if (sessionManager.shouldUseTokenHeader()) {\n const token = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (token) {\n options.headers[sessionManager.config.tokenCookieName] = token;\n }\n }\n \n let response = await fetch(url, options);\n \n // Retry once if unauthorized with INVALID_SESSION\n if (response.status === 401) {\n const clonedResponse = response.clone();\n try {\n const data = await clonedResponse.json();\n if (data.error_msg === 'INVALID_SESSION') {\n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n await sessionManager.refreshToken();\n }\n options.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const newToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (newToken) {\n options.headers[sessionManager.config.tokenCookieName] = newToken;\n }\n }\n response = await fetch(url, options);\n }\n } catch (e) {\n // Not JSON or parsing failed, return original response\n }\n }\n \n return response;\n}\n\n/**\n * Axios interceptor with automatic token refresh on 401\n * @param {Object} axiosInstance - Axios instance\n */\nexport function setupAxiosInterceptor(axiosInstance) {\n const sessionManager = SessionManager.getInstance();\n \n // Request interceptor to add cf-session-id and token\n axiosInstance.interceptors.request.use(\n (config) => {\n config.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const token = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (token) {\n config.headers[sessionManager.config.tokenCookieName] = token;\n }\n }\n return config;\n },\n (error) => Promise.reject(error)\n );\n \n // Response interceptor for token refresh\n axiosInstance.interceptors.response.use(\n (response) => response,\n async (error) => {\n const originalRequest = error.config;\n \n // Retry once if unauthorized with INVALID_SESSION\n if (error.response?.status === 401 && !originalRequest._retry) {\n if (error.response?.data?.error_msg === 'INVALID_SESSION') {\n originalRequest._retry = true;\n \n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n await sessionManager.refreshToken();\n }\n \n originalRequest.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const newToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (newToken) {\n originalRequest.headers[sessionManager.config.tokenCookieName] = newToken;\n }\n }\n return axiosInstance(originalRequest);\n }\n }\n \n return Promise.reject(error);\n }\n );\n \n return axiosInstance;\n}\n"],"names":["SessionError","Error","constructor","message","code","details","super","this","name","captureStackTrace","Object","setPrototypeOf","prototype","ConfigurationError","BootstrapError","NetworkError","SSRError","LOG_LEVELS","NONE","ERROR","WARN","INFO","DEBUG","logger","level","setLevel","toUpperCase","error","args","console","warn","info","debug","SessionManager","instance","config","bootstrapUrl","refreshInterval","tokenExpiry","maxRetries","tokenCookieName","state","isInitialized","isLoading","lastRefreshTime","errorCode","nextRefreshTime","cfSessionId","initializationFailed","refreshTimerId","listeners","Set","initializationPromise","configure","trim","undefined","isFinite","credentials","logLevel","initialize","window","existingToken","getToken","Date","now","sessionStorage","storedSessionId","getItem","setCfSessionId","generateSessionId","startTokenRefresh","notifyListeners","token","lastError","attempt","newSessionId","response","fetch","method","headers","ok","status","statusText","url","data","json","jsonError","originalError","setCookieHeader","get","serverSetCookie","includes","refresh_time","expire","setCookie","delay","Math","min","pow","Promise","resolve","setTimeout","isRefreshing","waitForRefresh","sessionId","setItem","crypto","randomUUID","random","toString","substr","getSessionId","shouldUseTokenHeader","location","protocol","document","parts","cookie","split","length","decodeURIComponent","pop","shift","String","value","maxAge","sanitizedName","replace","expires","toUTCString","isSecure","secureFlag","encodedValue","encodeURIComponent","interval","stopTokenRefresh","async","refreshToken","errorMessage","clearTimeout","getSessionStatus","timeUntilRefresh","max","subscribe","listener","TypeError","add","delete","Array","from","forEach","destroy","clear","removeItem","getInstance","useSession","options","autoInitialize","sessionManager","sessionState","setSessionState","useState","useEffect","newState","catch","refresh","useCallback","fetchInterceptor","clonedResponse","clone","error_msg","newToken","e","setupAxiosInterceptor","axiosInstance","interceptors","request","use","reject","originalRequest","_retry"],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/errors.js","../src/logger.js","../src/SessionManager.js","../src/hooks/useSession.js","../src/interceptors.js"],"sourcesContent":["/**\n * Custom error classes for SessionManager\n */\n\nexport class SessionError extends Error {\n constructor(message, code, details = {}) {\n super(message);\n this.name = 'SessionError';\n this.code = code;\n this.details = details;\n \n // Maintains proper stack trace for where error was thrown\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n \n // Set the prototype explicitly for proper instanceof checks\n Object.setPrototypeOf(this, SessionError.prototype);\n }\n}\n\nexport class ConfigurationError extends SessionError {\n constructor(message, details) {\n super(message, 'CONFIGURATION_ERROR', details);\n this.name = 'ConfigurationError';\n }\n}\n\nexport class BootstrapError extends SessionError {\n constructor(message, details) {\n super(message, 'BOOTSTRAP_ERROR', details);\n this.name = 'BootstrapError';\n }\n}\n\nexport class NetworkError extends SessionError {\n constructor(message, details) {\n super(message, 'NETWORK_ERROR', details);\n this.name = 'NetworkError';\n }\n}\n\nexport class SSRError extends SessionError {\n constructor(message, details = {}) {\n super(message, 'SSR_ERROR', details);\n this.name = 'SSRError';\n }\n}\n","/**\n * Logger utility with configurable log levels\n */\n\nconst LOG_LEVELS = {\n NONE: 0,\n ERROR: 1,\n WARN: 2,\n INFO: 3,\n DEBUG: 4\n};\n\nclass Logger {\n constructor() {\n this.level = LOG_LEVELS.WARN; // Default to WARN in production\n }\n\n setLevel(level) {\n if (typeof level === 'string') {\n this.level = LOG_LEVELS[level.toUpperCase()] ?? LOG_LEVELS.WARN;\n } else {\n this.level = level;\n }\n }\n\n error(...args) {\n if (this.level >= LOG_LEVELS.ERROR) {\n console.error('[SessionManager]', ...args);\n }\n }\n\n warn(...args) {\n if (this.level >= LOG_LEVELS.WARN) {\n console.warn('[SessionManager]', ...args);\n }\n }\n\n info(...args) {\n if (this.level >= LOG_LEVELS.INFO) {\n console.info('[SessionManager]', ...args);\n }\n }\n\n debug(...args) {\n if (this.level >= LOG_LEVELS.DEBUG) {\n console.debug('[SessionManager]', ...args);\n }\n }\n}\n\nexport const logger = new Logger();\nexport { LOG_LEVELS };\n","import {BootstrapError, ConfigurationError, NetworkError, SSRError} from './errors.js';\nimport {logger} from './logger.js';\n\n/**\n * SessionManager - Core session token management class\n * Handles session bootstrap, automatic token refresh, and lifecycle management\n */\nclass SessionManager {\n constructor() {\n if (SessionManager.instance) {\n return SessionManager.instance;\n }\n\n this.config = {\n bootstrapUrl: '/session/bootstrap',\n refreshInterval: 25 * 60 * 1000, // 25 minutes in milliseconds\n tokenExpiry: 30 * 60 * 1000, // 30 minutes in milliseconds\n maxRetries: 3,\n tokenCookieName: 'token', // Cookie name to read token from\n };\n\n this.state = {\n isInitialized: false,\n isLoading: false,\n lastRefreshTime: null,\n error: null,\n errorCode: null,\n nextRefreshTime: null,\n tokenExpiry: null,\n cfSessionId: null,\n initializationFailed: false,\n };\n\n this.refreshTimerId = null;\n this.listeners = new Set();\n this.initializationPromise = null;\n\n SessionManager.instance = this;\n }\n\n /**\n * Configure the session manager\n * @param {Object} config - Configuration options\n * @param {string} config.bootstrapUrl - URL for bootstrap API endpoint\n * @param {number} config.refreshInterval - Interval for token refresh in milliseconds (default: 25 mins)\n * @param {number} config.tokenExpiry - Token expiry time in milliseconds (default: 30 mins)\n * @param {number} config.maxRetries - Maximum retry attempts on failure (default: 3)\n * @param {Object} config.headers - Additional headers for API calls\n * @param {boolean} config.credentials - Include credentials in requests (default: true)\n * @param {string} config.tokenCookieName - Cookie name to read token from (default: 'token')\n */\n configure(config = {}) {\n if (!config.bootstrapUrl || typeof config.bootstrapUrl !== 'string' || !config.bootstrapUrl.trim()) {\n throw new ConfigurationError('bootstrapUrl is required and must be a non-empty string', { bootstrapUrl: config.bootstrapUrl });\n }\n \n if (config.refreshInterval !== undefined) {\n if (typeof config.refreshInterval !== 'number' || !isFinite(config.refreshInterval)) {\n throw new ConfigurationError('refreshInterval must be a finite number', { refreshInterval: config.refreshInterval });\n }\n if (config.refreshInterval <= 0) {\n throw new ConfigurationError('refreshInterval must be positive', { refreshInterval: config.refreshInterval });\n }\n }\n \n if (config.tokenExpiry !== undefined) {\n if (typeof config.tokenExpiry !== 'number' || !isFinite(config.tokenExpiry)) {\n throw new ConfigurationError('tokenExpiry must be a finite number', { tokenExpiry: config.tokenExpiry });\n }\n if (config.tokenExpiry <= 0) {\n throw new ConfigurationError('tokenExpiry must be positive', { tokenExpiry: config.tokenExpiry });\n }\n }\n\n this.config = {\n ...this.config,\n ...config,\n credentials: config.credentials !== undefined ? config.credentials : true,\n logLevel: config.logLevel || 'WARN'\n };\n\n logger.setLevel(this.config.logLevel);\n\n if (this.config.refreshInterval && this.config.tokenExpiry && \n this.config.refreshInterval >= this.config.tokenExpiry) {\n logger.warn('refreshInterval should be less than tokenExpiry to prevent race conditions');\n }\n }\n\n /**\n * Initialize session by calling bootstrap API\n * @param {boolean} skipCookieCheck - Skip session_initialized cookie check (for auto-refresh)\n * @returns {Promise<Object>} Bootstrap response\n */\n async initialize(skipCookieCheck = false) {\n if (typeof window === 'undefined') {\n throw new SSRError('Cannot initialize in non-browser environment (SSR)');\n }\n \n if (this.state.initializationFailed) {\n logger.warn('Initialization previously failed. Reload page to retry.');\n throw new BootstrapError('Initialization failed after max retries. Reload page to retry.');\n }\n \n // Return existing promise if refresh already in progress\n if (this.initializationPromise) {\n logger.debug('Initialization already in progress, waiting...');\n return this.initializationPromise;\n }\n\n // Check if session is already initialized via cookie (only on page load)\n if (!skipCookieCheck) {\n const sessionInitialized = this.getCookie('session_initialized');\n if (sessionInitialized === 'true') {\n logger.info('Session already initialized, skipping bootstrap API call');\n this.state.isInitialized = true;\n this.state.lastRefreshTime = Date.now();\n this.state.nextRefreshTime = Date.now() + this.config.refreshInterval;\n this.state.tokenExpiry = this.config.tokenExpiry;\n \n // Restore cf-session-id from cookie\n const storedSessionId = this.getCookie('cf-session-id');\n if (storedSessionId) {\n this.state.cfSessionId = storedSessionId;\n } else {\n this.setCfSessionId(this.generateSessionId());\n }\n \n this.startTokenRefresh(this.config.refreshInterval);\n this.notifyListeners();\n return { token: this.getToken(this.config.tokenCookieName) };\n }\n }\n\n this.state.isLoading = true;\n this.state.error = null;\n this.notifyListeners();\n\n this.initializationPromise = (async () => {\n let lastError;\n \n for (let attempt = 1; attempt <= this.config.maxRetries; attempt++) {\n try {\n // Generate new cf-session-id for each bootstrap call\n const newSessionId = this.generateSessionId();\n this.setCfSessionId(newSessionId);\n\n logger.info(`Calling bootstrap API (attempt ${attempt}/${this.config.maxRetries}):`, this.config.bootstrapUrl);\n\n const response = await fetch(this.config.bootstrapUrl, {\n method: 'GET',\n credentials: this.config.credentials ? 'include' : 'same-origin',\n headers: {\n 'Content-Type': 'application/json',\n 'cf-session-id': this.state.cfSessionId,\n ...this.config.headers,\n },\n });\n\n if (!response.ok) {\n throw new BootstrapError(`Bootstrap failed: ${response.status} ${response.statusText}`, {\n status: response.status,\n statusText: response.statusText,\n url: this.config.bootstrapUrl\n });\n }\n\n let data;\n try {\n data = await response.json();\n } catch (jsonError) {\n throw new BootstrapError('Bootstrap response is not valid JSON', { originalError: jsonError.message });\n }\n logger.info('Bootstrap successful');\n\n // Check if server set a cookie\n const setCookieHeader = response.headers.get('set-cookie');\n const serverSetCookie = setCookieHeader && setCookieHeader.includes(this.config.tokenCookieName);\n\n // Use refresh_time from response (in seconds) or fall back to config\n const refreshInterval = data.refresh_time \n ? data.refresh_time * 1000 \n : this.config.refreshInterval;\n\n // Calculate token expiry: refresh_time + window_time (both in seconds)\n const tokenExpiry = data.expire\n ? data.expire * 1000\n : this.config.tokenExpiry;\n\n if (data.token && !serverSetCookie) {\n logger.debug('Server did not set cookie, setting from SDK');\n this.setCookie(this.config.tokenCookieName, data.token, tokenExpiry / 1000);\n } else if (serverSetCookie) {\n logger.debug('Server set cookie, skipping SDK cookie logic');\n }\n\n this.state.isInitialized = true;\n this.state.isLoading = false;\n this.state.lastRefreshTime = Date.now();\n this.state.nextRefreshTime = Date.now() + refreshInterval;\n this.state.tokenExpiry = tokenExpiry;\n this.state.error = null;\n\n // Set session_initialized cookie (expires at token expiry time)\n this.setCookie('session_initialized', 'true', tokenExpiry / 1000);\n\n // Start automatic refresh timer with dynamic interval\n this.startTokenRefresh(refreshInterval);\n\n this.notifyListeners();\n return data;\n } catch (error) {\n lastError = error instanceof Error ? error : new NetworkError('Unknown error occurred', { error });\n logger.error(`Bootstrap attempt ${attempt} failed:`, lastError.message);\n \n if (attempt < this.config.maxRetries) {\n const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);\n logger.info(`Retrying in ${delay}ms...`);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n \n logger.error('All bootstrap attempts failed');\n this.state.isLoading = false;\n this.state.error = lastError?.message || 'Unknown error';\n this.state.errorCode = lastError?.code || 'UNKNOWN_ERROR';\n this.state.initializationFailed = true;\n this.notifyListeners();\n throw lastError;\n })();\n\n try {\n return await this.initializationPromise;\n } finally {\n this.initializationPromise = null;\n }\n }\n\n /**\n * Check if token refresh is currently in progress\n * @returns {boolean} True if refresh is in progress\n */\n isRefreshing() {\n return this.initializationPromise !== null;\n }\n\n /**\n * Wait for ongoing refresh to complete\n * @returns {Promise<Object>} Bootstrap response from ongoing refresh\n */\n async waitForRefresh() {\n if (this.initializationPromise) {\n logger.debug('Waiting for ongoing refresh to complete');\n return this.initializationPromise;\n }\n return Promise.resolve();\n }\n\n /**\n * Set cf-session-id in cookie\n * @param {string} sessionId - Session ID\n */\n setCfSessionId(sessionId) {\n this.state.cfSessionId = sessionId;\n const maxAge = this.config.tokenExpiry / 1000;\n this.setCookie('cf-session-id', sessionId, maxAge);\n }\n\n /**\n * Generate unique session ID\n * @returns {string} Generated session ID\n */\n generateSessionId() {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }\n\n /**\n * Get cf-session-id for requests\n * @returns {string} Current cf-session-id\n */\n getSessionId() {\n return this.state.cfSessionId;\n }\n\n /**\n * Check if token should be added to headers (HTTP or cross-origin)\n * @returns {boolean} True if token should be added to headers\n */\n shouldUseTokenHeader() {\n if (typeof window === 'undefined') return false;\n return window.location.protocol === 'http:';\n }\n\n /**\n * Get cookie value\n * @param {string} name - Cookie name\n * @returns {string|null} Cookie value or null\n */\n getCookie(name) {\n if (typeof document === 'undefined') return null;\n const value = `; ${document.cookie}`;\n const parts = value.split(`; ${name}=`);\n if (parts.length === 2) {\n try {\n return decodeURIComponent(parts.pop().split(';').shift());\n } catch (error) {\n logger.error('Failed to decode cookie value:', error instanceof Error ? error.message : String(error));\n return null;\n }\n }\n return null;\n }\n\n /**\n * Get token from cookie\n * @param {string} name - Cookie name (default: configured tokenCookieName)\n * @returns {string|null} Token value or null\n */\n getToken(name = this.config.tokenCookieName) {\n return this.getCookie(name);\n }\n\n /**\n * Set cookie from client-side (WARNING: Not HttpOnly, less secure than server-set cookies)\n * @param {string} name - Cookie name\n * @param {string} value - Cookie value\n * @param {number} maxAge - Max age in seconds\n */\n setCookie(name, value, maxAge) {\n if (typeof document === 'undefined') {\n logger.warn('Cannot set cookie in non-browser environment');\n return;\n }\n \n // Sanitize cookie name to prevent XSS\n const sanitizedName = name.replace(/[^a-zA-Z0-9_-]/g, '');\n if (!sanitizedName) {\n logger.error('Invalid cookie name provided');\n return;\n }\n \n const expires = new Date(Date.now() + maxAge * 1000).toUTCString();\n const isSecure = typeof window !== 'undefined' && window.location.protocol === 'https:';\n const secureFlag = isSecure ? '; Secure' : '';\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${sanitizedName}=${encodedValue}; path=/; expires=${expires}; SameSite=Lax${secureFlag}`;\n logger.debug(`Cookie set: ${sanitizedName}, expires in ${maxAge}s${isSecure ? ' (Secure)' : ''} [WARNING: Not HttpOnly]`);\n }\n\n /**\n * Start automatic token refresh timer\n * @param {number} interval - Refresh interval in milliseconds\n */\n startTokenRefresh(interval = this.config.refreshInterval) {\n this.stopTokenRefresh();\n logger.info(`Scheduling token refresh in ${interval / 1000} seconds`);\n\n this.refreshTimerId = setTimeout(async () => {\n logger.info('Auto-refreshing token...');\n try {\n await this.refreshToken();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n const errorCode = error?.code || 'UNKNOWN_ERROR';\n logger.error('Auto-refresh failed:', errorMessage);\n this.state.error = errorMessage;\n this.state.errorCode = errorCode;\n this.notifyListeners();\n }\n }, interval);\n }\n\n /**\n * Stop automatic token refresh timer\n */\n stopTokenRefresh() {\n if (this.refreshTimerId) {\n clearTimeout(this.refreshTimerId);\n this.refreshTimerId = null;\n logger.debug('Token refresh timer stopped');\n }\n }\n\n /**\n * Manually refresh the session token\n * @returns {Promise<Object>} Bootstrap response\n */\n async refreshToken() {\n logger.info('Manual token refresh triggered');\n this.state.initializationFailed = false;\n return this.initialize(true);\n }\n\n /**\n * Get current session status\n * @returns {Object} Current session state\n */\n getSessionStatus() {\n return {\n isInitialized: this.state.isInitialized,\n isLoading: this.state.isLoading,\n lastRefreshTime: this.state.lastRefreshTime,\n nextRefreshTime: this.state.nextRefreshTime,\n tokenExpiry: this.state.tokenExpiry,\n error: this.state.error,\n errorCode: this.state.errorCode,\n initializationFailed: this.state.initializationFailed,\n timeUntilRefresh: this.state.nextRefreshTime\n ? Math.max(0, this.state.nextRefreshTime - Date.now())\n : null,\n };\n }\n\n /**\n * Subscribe to session state changes\n * @param {Function} listener - Callback function to be called on state changes\n * @returns {Function} Unsubscribe function\n */\n subscribe(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('Listener must be a function');\n }\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Notify all listeners of state changes\n */\n notifyListeners() {\n const status = this.getSessionStatus();\n // Use Array.from to create a snapshot, preventing issues if listeners modify the Set\n Array.from(this.listeners).forEach(listener => {\n try {\n listener(status);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('Listener error:', errorMessage);\n }\n });\n }\n\n /**\n * Clean up and reset the session manager\n */\n destroy() {\n this.stopTokenRefresh();\n this.listeners.clear();\n this.state = {\n isInitialized: false,\n isLoading: false,\n lastRefreshTime: null,\n error: null,\n errorCode: null,\n nextRefreshTime: null,\n tokenExpiry: null,\n cfSessionId: null,\n initializationFailed: false,\n };\n // Clear cookies\n this.setCookie('cf-session-id', '', -1);\n this.setCookie('session_initialized', '', -1);\n logger.info('Destroyed');\n }\n\n /**\n * Get the singleton instance\n * @returns {SessionManager} Singleton instance\n */\n static getInstance() {\n if (!SessionManager.instance) {\n SessionManager.instance = new SessionManager();\n }\n return SessionManager.instance;\n }\n}\n\nexport default SessionManager;\n","import { useState, useEffect, useCallback } from 'react';\nimport SessionManager from '../SessionManager.js';\nimport { logger } from '../logger.js';\n\n/**\n * React hook for session management\n * Provides session state and controls for React components\n * \n * @param {Object} options - Configuration options\n * @param {boolean} options.autoInitialize - Automatically initialize on mount (default: true)\n * @returns {Object} Session state and controls\n */\nexport function useSession(options = {}) {\n const { autoInitialize = true } = options;\n\n const sessionManager = SessionManager.getInstance();\n\n const [sessionState, setSessionState] = useState(() =>\n sessionManager.getSessionStatus()\n );\n\n // Subscribe to session state changes\n useEffect(() => {\n const unsubscribe = sessionManager.subscribe((newState) => {\n setSessionState(newState);\n });\n\n return unsubscribe;\n }, []); // sessionManager is a singleton, no need to include in deps\n\n // Auto-initialize if enabled\n useEffect(() => {\n if (autoInitialize && !sessionState.isInitialized && !sessionState.isLoading && !sessionState.initializationFailed) {\n sessionManager.initialize().catch(error => {\n logger.error('Auto-initialization failed:', error);\n });\n }\n }, [autoInitialize, sessionState.isInitialized, sessionState.isLoading, sessionState.initializationFailed]);\n\n // Manual refresh function\n const refresh = useCallback(async () => {\n try {\n await sessionManager.refreshToken();\n } catch (error) {\n logger.error('Manual refresh failed:', error);\n throw error;\n }\n }, []);\n\n // Initialize function (for manual control)\n const initialize = useCallback(async () => {\n try {\n await sessionManager.initialize();\n } catch (error) {\n logger.error('Manual initialization failed:', error);\n throw error;\n }\n }, []);\n\n return {\n // State\n isInitialized: sessionState.isInitialized,\n isLoading: sessionState.isLoading,\n error: sessionState.error,\n lastRefreshTime: sessionState.lastRefreshTime,\n nextRefreshTime: sessionState.nextRefreshTime,\n timeUntilRefresh: sessionState.timeUntilRefresh,\n initializationFailed: sessionState.initializationFailed,\n\n // Actions\n refresh,\n initialize,\n };\n}\n\nexport default useSession;\n","import SessionManager from './SessionManager.js';\n\n/**\n * Fetch interceptor with automatic token refresh on 401\n * @param {string} url - Request URL\n * @param {Object} options - Fetch options\n * @returns {Promise<Response>} Fetch response\n */\nexport async function fetchInterceptor(url, options = {}) {\n const sessionManager = SessionManager.getInstance();\n \n // Ensure credentials are included to send cookies\n options.credentials = options.credentials || 'include';\n \n // Add headers\n options.headers = {\n ...options.headers,\n 'cf-session-id': sessionManager.getSessionId(),\n };\n \n // Add token to header if HTTP (not HTTPS)\n if (sessionManager.shouldUseTokenHeader()) {\n const token = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (token) {\n options.headers[sessionManager.config.tokenCookieName] = token;\n }\n }\n \n let response = await fetch(url, options);\n \n // Retry once if unauthorized with INVALID_SESSION\n if (response.status === 401) {\n const clonedResponse = response.clone();\n try {\n const data = await clonedResponse.json();\n if (data.error_msg === 'INVALID_SESSION') {\n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n const existingToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (!existingToken) {\n await sessionManager.refreshToken();\n }\n }\n options.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const newToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (newToken) {\n options.headers[sessionManager.config.tokenCookieName] = newToken;\n }\n }\n response = await fetch(url, options);\n }\n } catch (e) {\n // Not JSON or parsing failed, return original response\n }\n }\n \n return response;\n}\n\n/**\n * Axios interceptor with automatic token refresh on 401 and INVALID_SESSION\n * @param {Object} axiosInstance - Axios instance\n */\nexport function setupAxiosInterceptor(axiosInstance) {\n const sessionManager = SessionManager.getInstance();\n \n // Request interceptor to add cf-session-id and token\n axiosInstance.interceptors.request.use(\n (config) => {\n config.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const token = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (token) {\n config.headers[sessionManager.config.tokenCookieName] = token;\n }\n }\n return config;\n },\n (error) => Promise.reject(error)\n );\n \n // Response interceptor for token refresh\n axiosInstance.interceptors.response.use(\n (response) => response,\n async (error) => {\n const originalRequest = error.config;\n \n // Retry once if unauthorized with INVALID_SESSION\n if (error.response?.status === 401 && !originalRequest._retry) {\n if (error.response?.data?.error_msg === 'INVALID_SESSION') {\n originalRequest._retry = true;\n \n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n const existingToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (!existingToken) {\n await sessionManager.refreshToken();\n }\n }\n \n originalRequest.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const newToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (newToken) {\n originalRequest.headers[sessionManager.config.tokenCookieName] = newToken;\n }\n }\n return axiosInstance(originalRequest);\n }\n }\n \n return Promise.reject(error);\n }\n );\n \n return axiosInstance;\n}\n"],"names":["SessionError","Error","constructor","message","code","details","super","this","name","captureStackTrace","Object","setPrototypeOf","prototype","ConfigurationError","BootstrapError","NetworkError","SSRError","LOG_LEVELS","NONE","ERROR","WARN","INFO","DEBUG","logger","level","setLevel","toUpperCase","error","args","console","warn","info","debug","SessionManager","instance","config","bootstrapUrl","refreshInterval","tokenExpiry","maxRetries","tokenCookieName","state","isInitialized","isLoading","lastRefreshTime","errorCode","nextRefreshTime","cfSessionId","initializationFailed","refreshTimerId","listeners","Set","initializationPromise","configure","trim","undefined","isFinite","credentials","logLevel","initialize","skipCookieCheck","window","getCookie","Date","now","storedSessionId","setCfSessionId","generateSessionId","startTokenRefresh","notifyListeners","token","getToken","lastError","attempt","newSessionId","response","fetch","method","headers","ok","status","statusText","url","data","json","jsonError","originalError","setCookieHeader","get","serverSetCookie","includes","refresh_time","expire","setCookie","delay","Math","min","pow","Promise","resolve","setTimeout","isRefreshing","waitForRefresh","sessionId","maxAge","crypto","randomUUID","random","toString","substr","getSessionId","shouldUseTokenHeader","location","protocol","document","parts","cookie","split","length","decodeURIComponent","pop","shift","String","value","sanitizedName","replace","expires","toUTCString","isSecure","secureFlag","encodedValue","encodeURIComponent","interval","stopTokenRefresh","async","refreshToken","errorMessage","clearTimeout","getSessionStatus","timeUntilRefresh","max","subscribe","listener","TypeError","add","delete","Array","from","forEach","destroy","clear","getInstance","useSession","options","autoInitialize","sessionManager","sessionState","setSessionState","useState","useEffect","newState","catch","refresh","useCallback","fetchInterceptor","clonedResponse","clone","error_msg","newToken","e","setupAxiosInterceptor","axiosInstance","interceptors","request","use","reject","originalRequest","_retry"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mapnests/gateway-web-sdk",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Session token management SDK with automatic refresh for React/Next.js applications",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",