@mapnests/gateway-web-sdk 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -87,6 +87,7 @@ const sm = SessionManager.getInstance();
|
|
|
87
87
|
async function manual(url, init = {}) {
|
|
88
88
|
const opts = { ...init, headers: { ...(init.headers || {}) }, credentials: 'include' };
|
|
89
89
|
opts.headers['cf-session-id'] = sm.getSessionId();
|
|
90
|
+
opts.headers['x-client-platform'] = 'web';
|
|
90
91
|
if (sm.shouldUseTokenHeader()) {
|
|
91
92
|
const t = sm.getToken(sm.config.tokenCookieName);
|
|
92
93
|
if (t) opts.headers[sm.config.tokenCookieName] = t;
|
|
@@ -106,6 +107,7 @@ async function manual(url, init = {}) {
|
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
opts.headers['cf-session-id'] = sm.getSessionId();
|
|
110
|
+
opts.headers['x-client-platform'] = 'web';
|
|
109
111
|
if (sm.shouldUseTokenHeader()) {
|
|
110
112
|
const nt = sm.getToken(sm.config.tokenCookieName);
|
|
111
113
|
if (nt) opts.headers[sm.config.tokenCookieName] = nt;
|
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(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}};
|
|
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,"x-client-platform":"web",...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}}const h="web";exports.BootstrapError=s,exports.CLIENT_PLATFORM=h,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(),"x-client-platform":h},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(),t.headers["x-client-platform"]=h,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(),e.headers["x-client-platform"]=h,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(),s.headers["x-client-platform"]=h,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
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -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 * @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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"}
|
|
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';\nconst CLIENT_PLATFORM = 'web';\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 'x-client-platform': CLIENT_PLATFORM,\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';\nexport const CLIENT_PLATFORM = 'web';\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 'x-client-platform': CLIENT_PLATFORM,\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 options.headers['x-client-platform'] = CLIENT_PLATFORM;\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 config.headers['x-client-platform'] = CLIENT_PLATFORM;\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 originalRequest.headers['x-client-platform'] = CLIENT_PLATFORM;\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","CLIENT_PLATFORM","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":"2FAIO,MAAMA,UAAqBC,MAChC,WAAAC,CAAYC,EAASC,EAAMC,EAAU,CAAA,GACnCC,MAAMH,GACNI,KAAKC,KAAO,eACZD,KAAKH,KAAOA,EACZG,KAAKF,QAAUA,EAGXJ,MAAMQ,mBACRR,MAAMQ,kBAAkBF,KAAMA,KAAKL,aAIrCQ,OAAOC,eAAeJ,KAAMP,EAAaY,UAC3C,EAGK,MAAMC,UAA2Bb,EACtC,WAAAE,CAAYC,EAASE,GACnBC,MAAMH,EAAS,sBAAuBE,GACtCE,KAAKC,KAAO,oBACd,EAGK,MAAMM,UAAuBd,EAClC,WAAAE,CAAYC,EAASE,GACnBC,MAAMH,EAAS,kBAAmBE,GAClCE,KAAKC,KAAO,gBACd,EAGK,MAAMO,UAAqBf,EAChC,WAAAE,CAAYC,EAASE,GACnBC,MAAMH,EAAS,gBAAiBE,GAChCE,KAAKC,KAAO,cACd,EAGK,MAAMQ,UAAiBhB,EAC5B,WAAAE,CAAYC,EAASE,EAAU,IAC7BC,MAAMH,EAAS,YAAaE,GAC5BE,KAAKC,KAAO,UACd,EC1CG,MAACS,EAAa,CACjBC,KAAM,EACNC,MAAO,EACPC,KAAM,EACNC,KAAM,EACNC,MAAO,GAyCG,MAACC,EAAS,IAtCtB,MACE,WAAArB,GACEK,KAAKiB,MAAQP,EAAWG,IAC1B,CAEA,QAAAK,CAASD,GAELjB,KAAKiB,MADc,iBAAVA,EACIP,EAAWO,EAAME,gBAAkBT,EAAWG,KAE9CI,CAEjB,CAEA,KAAAG,IAASC,GACHrB,KAAKiB,OAASP,EAAWE,OAC3BU,QAAQF,MAAM,sBAAuBC,EAEzC,CAEA,IAAAE,IAAQF,GACFrB,KAAKiB,OAASP,EAAWG,MAC3BS,QAAQC,KAAK,sBAAuBF,EAExC,CAEA,IAAAG,IAAQH,GACFrB,KAAKiB,OAASP,EAAWI,MAC3BQ,QAAQE,KAAK,sBAAuBH,EAExC,CAEA,KAAAI,IAASJ,GACHrB,KAAKiB,OAASP,EAAWK,OAC3BO,QAAQG,MAAM,sBAAuBJ,EAEzC,GCxCF,MAAMK,EACJ,WAAA/B,GACE,GAAI+B,EAAeC,SACjB,OAAOD,EAAeC,SAGxB3B,KAAK4B,OAAS,CACZC,aAAc,qBACdC,gBAAiB,KACjBC,YAAa,KACbC,WAAY,EACZC,gBAAiB,SAGnBjC,KAAKkC,MAAQ,CACXC,eAAe,EACfC,WAAW,EACXC,gBAAiB,KACjBjB,MAAO,KACPkB,UAAW,KACXC,gBAAiB,KACjBR,YAAa,KACbS,YAAa,KACbC,sBAAsB,GAGxBzC,KAAK0C,eAAiB,KACtB1C,KAAK2C,UAAY,IAAIC,IACrB5C,KAAK6C,sBAAwB,KAE7BnB,EAAeC,SAAW3B,IAC5B,CAaA,SAAA8C,CAAUlB,EAAS,IACjB,IAAKA,EAAOC,cAA+C,iBAAxBD,EAAOC,eAA8BD,EAAOC,aAAakB,OAC1F,MAAM,IAAIzC,EAAmB,0DAA2D,CAAEuB,aAAcD,EAAOC,eAGjH,QAA+BmB,IAA3BpB,EAAOE,gBAA+B,CACxC,GAAsC,iBAA3BF,EAAOE,kBAAiCmB,SAASrB,EAAOE,iBACjE,MAAM,IAAIxB,EAAmB,0CAA2C,CAAEwB,gBAAiBF,EAAOE,kBAEpG,GAAIF,EAAOE,iBAAmB,EAC5B,MAAM,IAAIxB,EAAmB,mCAAoC,CAAEwB,gBAAiBF,EAAOE,iBAE/F,CAEA,QAA2BkB,IAAvBpB,EAAOG,YAA2B,CACpC,GAAkC,iBAAvBH,EAAOG,cAA6BkB,SAASrB,EAAOG,aAC7D,MAAM,IAAIzB,EAAmB,sCAAuC,CAAEyB,YAAaH,EAAOG,cAE5F,GAAIH,EAAOG,aAAe,EACxB,MAAM,IAAIzB,EAAmB,+BAAgC,CAAEyB,YAAaH,EAAOG,aAEvF,CAEA/B,KAAK4B,OAAS,IACT5B,KAAK4B,UACLA,EACHsB,iBAAoCF,IAAvBpB,EAAOsB,aAA4BtB,EAAOsB,YACvDC,SAAUvB,EAAOuB,UAAY,QAG/BnC,EAAOE,SAASlB,KAAK4B,OAAOuB,UAExBnD,KAAK4B,OAAOE,iBAAmB9B,KAAK4B,OAAOG,aAC3C/B,KAAK4B,OAAOE,iBAAmB9B,KAAK4B,OAAOG,aAC7Cf,EAAOO,KAAK,6EAEhB,CAOA,gBAAM6B,CAAWC,GAAkB,GACjC,GAAsB,oBAAXC,OACT,MAAM,IAAI7C,EAAS,sDAGrB,GAAIT,KAAKkC,MAAMO,qBAEb,MADAzB,EAAOO,KAAK,2DACN,IAAIhB,EAAe,kEAI3B,GAAIP,KAAK6C,sBAEP,OADA7B,EAAOS,MAAM,kDACNzB,KAAK6C,sBAId,IAAKQ,EAAiB,CAEpB,GAA2B,SADArD,KAAKuD,UAAU,uBACP,CACjCvC,EAAOQ,KAAK,4DACZxB,KAAKkC,MAAMC,eAAgB,EAC3BnC,KAAKkC,MAAMG,gBAAkBmB,KAAKC,MAClCzD,KAAKkC,MAAMK,gBAAkBiB,KAAKC,MAAQzD,KAAK4B,OAAOE,gBACtD9B,KAAKkC,MAAMH,YAAc/B,KAAK4B,OAAOG,YAGrC,MAAM2B,EAAkB1D,KAAKuD,UAAU,iBASvC,OARIG,EACF1D,KAAKkC,MAAMM,YAAckB,EAEzB1D,KAAK2D,eAAe3D,KAAK4D,qBAG3B5D,KAAK6D,kBAAkB7D,KAAK4B,OAAOE,iBACnC9B,KAAK8D,kBACE,CAAEC,MAAO/D,KAAKgE,SAAShE,KAAK4B,OAAOK,iBAC5C,CACF,CAEAjC,KAAKkC,MAAME,WAAY,EACvBpC,KAAKkC,MAAMd,MAAQ,KACnBpB,KAAK8D,kBAEL9D,KAAK6C,sBAAwB,WAC3B,IAAIoB,EAEJ,IAAK,IAAIC,EAAU,EAAGA,GAAWlE,KAAK4B,OAAOI,WAAYkC,IACvD,IAEE,MAAMC,EAAenE,KAAK4D,oBAC1B5D,KAAK2D,eAAeQ,GAEpBnD,EAAOQ,KAAK,kCAAkC0C,KAAWlE,KAAK4B,OAAOI,eAAgBhC,KAAK4B,OAAOC,cAEjG,MAAMuC,QAAiBC,MAAMrE,KAAK4B,OAAOC,aAAc,CACrDyC,OAAQ,MACRpB,YAAalD,KAAK4B,OAAOsB,YAAc,UAAY,cACnDqB,QAAS,CACP,eAAgB,mBAChB,gBAAiBvE,KAAKkC,MAAMM,YAC5B,oBAzJU,SA0JPxC,KAAK4B,OAAO2C,WAInB,IAAKH,EAASI,GACZ,MAAM,IAAIjE,EAAe,qBAAqB6D,EAASK,UAAUL,EAASM,aAAc,CACtFD,OAAQL,EAASK,OACjBC,WAAYN,EAASM,WACrBC,IAAK3E,KAAK4B,OAAOC,eAIrB,IAAI+C,EACJ,IACEA,QAAaR,EAASS,MACxB,CAAE,MAAOC,GACP,MAAM,IAAIvE,EAAe,uCAAwC,CAAEwE,cAAeD,EAAUlF,SAC9F,CACAoB,EAAOQ,KAAK,wBAGZ,MAAMwD,EAAkBZ,EAASG,QAAQU,IAAI,cACvCC,EAAkBF,GAAmBA,EAAgBG,SAASnF,KAAK4B,OAAOK,iBAG1EH,EAAkB8C,EAAKQ,aACL,IAApBR,EAAKQ,aACLpF,KAAK4B,OAAOE,gBAGVC,EAAc6C,EAAKS,OACP,IAAdT,EAAKS,OACLrF,KAAK4B,OAAOG,YAuBhB,OArBI6C,EAAKb,QAAUmB,GACjBlE,EAAOS,MAAM,+CACbzB,KAAKsF,UAAUtF,KAAK4B,OAAOK,gBAAiB2C,EAAKb,MAAOhC,EAAc,MAC7DmD,GACTlE,EAAOS,MAAM,gDAGfzB,KAAKkC,MAAMC,eAAgB,EAC3BnC,KAAKkC,MAAME,WAAY,EACvBpC,KAAKkC,MAAMG,gBAAkBmB,KAAKC,MAClCzD,KAAKkC,MAAMK,gBAAkBiB,KAAKC,MAAQ3B,EAC1C9B,KAAKkC,MAAMH,YAAcA,EACzB/B,KAAKkC,MAAMd,MAAQ,KAGnBpB,KAAKsF,UAAU,sBAAuB,OAAQvD,EAAc,KAG5D/B,KAAK6D,kBAAkB/B,GAEvB9B,KAAK8D,kBACEc,CACT,CAAE,MAAOxD,GAIP,GAHA6C,EAAY7C,aAAiB1B,MAAQ0B,EAAQ,IAAIZ,EAAa,yBAA0B,CAAEY,UAC1FJ,EAAOI,MAAM,qBAAqB8C,YAAmBD,EAAUrE,SAE3DsE,EAAUlE,KAAK4B,OAAOI,WAAY,CACpC,MAAMuD,EAAQC,KAAKC,IAAI,IAAOD,KAAKE,IAAI,EAAGxB,EAAU,GAAI,KACxDlD,EAAOQ,KAAK,eAAe+D,gBACrB,IAAII,QAAQC,GAAWC,WAAWD,EAASL,GACnD,CACF,CASF,MANAvE,EAAOI,MAAM,iCACbpB,KAAKkC,MAAME,WAAY,EACvBpC,KAAKkC,MAAMd,MAAQ6C,GAAWrE,SAAW,gBACzCI,KAAKkC,MAAMI,UAAY2B,GAAWpE,MAAQ,gBAC1CG,KAAKkC,MAAMO,sBAAuB,EAClCzC,KAAK8D,kBACCG,CACP,EA7F4B,GA+F7B,IACE,aAAajE,KAAK6C,qBACpB,CAAC,QACC7C,KAAK6C,sBAAwB,IAC/B,CACF,CAMA,YAAAiD,GACE,OAAsC,OAA/B9F,KAAK6C,qBACd,CAMA,oBAAMkD,GACJ,OAAI/F,KAAK6C,uBACP7B,EAAOS,MAAM,2CACNzB,KAAK6C,uBAEP8C,QAAQC,SACjB,CAMA,cAAAjC,CAAeqC,GACbhG,KAAKkC,MAAMM,YAAcwD,EACzB,MAAMC,EAASjG,KAAK4B,OAAOG,YAAc,IACzC/B,KAAKsF,UAAU,gBAAiBU,EAAWC,EAC7C,CAMA,iBAAArC,GACE,MAAsB,oBAAXsC,QAA0BA,OAAOC,WACnCD,OAAOC,aAET,GAAG3C,KAAKC,SAAS+B,KAAKY,SAASC,SAAS,IAAIC,OAAO,EAAG,IAC/D,CAMA,YAAAC,GACE,OAAOvG,KAAKkC,MAAMM,WACpB,CAMA,oBAAAgE,GACE,MAAsB,oBAAXlD,QACyB,UAA7BA,OAAOmD,SAASC,QACzB,CAOA,SAAAnD,CAAUtD,GACR,GAAwB,oBAAb0G,SAA0B,OAAO,KAC5C,MACMC,EADQ,KAAKD,SAASE,SACRC,MAAM,KAAK7G,MAC/B,GAAqB,IAAjB2G,EAAMG,OACR,IACE,OAAOC,mBAAmBJ,EAAMK,MAAMH,MAAM,KAAKI,QACnD,CAAE,MAAO9F,GAEP,OADAJ,EAAOI,MAAM,iCAAkCA,aAAiB1B,MAAQ0B,EAAMxB,QAAUuH,OAAO/F,IACxF,IACT,CAEF,OAAO,IACT,CAOA,QAAA4C,CAAS/D,EAAOD,KAAK4B,OAAOK,iBAC1B,OAAOjC,KAAKuD,UAAUtD,EACxB,CAQA,SAAAqF,CAAUrF,EAAMmH,EAAOnB,GACrB,GAAwB,oBAAbU,SAET,YADA3F,EAAOO,KAAK,gDAKd,MAAM8F,EAAgBpH,EAAKqH,QAAQ,kBAAmB,IACtD,IAAKD,EAEH,YADArG,EAAOI,MAAM,gCAIf,MAAMmG,EAAU,IAAI/D,KAAKA,KAAKC,MAAiB,IAATwC,GAAeuB,cAC/CC,EAA6B,oBAAXnE,QAAuD,WAA7BA,OAAOmD,SAASC,SAC5DgB,EAAaD,EAAW,WAAa,GACrCE,EAAeC,mBAAmBR,GACxCT,SAASE,OAAS,GAAGQ,KAAiBM,sBAAiCJ,kBAAwBG,IAC/F1G,EAAOS,MAAM,eAAe4F,iBAA6BpB,KAAUwB,EAAW,YAAc,6BAC9F,CAMA,iBAAA5D,CAAkBgE,EAAW7H,KAAK4B,OAAOE,iBACvC9B,KAAK8H,mBACL9G,EAAOQ,KAAK,+BAA+BqG,EAAW,eAEtD7H,KAAK0C,eAAiBmD,WAAWkC,UAC/B/G,EAAOQ,KAAK,4BACZ,UACQxB,KAAKgI,cACb,CAAE,MAAO5G,GACP,MAAM6G,EAAe7G,aAAiB1B,MAAQ0B,EAAMxB,QAAUuH,OAAO/F,GAC/DkB,EAAYlB,GAAOvB,MAAQ,gBACjCmB,EAAOI,MAAM,uBAAwB6G,GACrCjI,KAAKkC,MAAMd,MAAQ6G,EACnBjI,KAAKkC,MAAMI,UAAYA,EACvBtC,KAAK8D,iBACP,GACC+D,EACL,CAKA,gBAAAC,GACM9H,KAAK0C,iBACPwF,aAAalI,KAAK0C,gBAClB1C,KAAK0C,eAAiB,KACtB1B,EAAOS,MAAM,+BAEjB,CAMA,kBAAMuG,GAGJ,OAFAhH,EAAOQ,KAAK,kCACZxB,KAAKkC,MAAMO,sBAAuB,EAC3BzC,KAAKoD,YAAW,EACzB,CAMA,gBAAA+E,GACE,MAAO,CACLhG,cAAenC,KAAKkC,MAAMC,cAC1BC,UAAWpC,KAAKkC,MAAME,UACtBC,gBAAiBrC,KAAKkC,MAAMG,gBAC5BE,gBAAiBvC,KAAKkC,MAAMK,gBAC5BR,YAAa/B,KAAKkC,MAAMH,YACxBX,MAAOpB,KAAKkC,MAAMd,MAClBkB,UAAWtC,KAAKkC,MAAMI,UACtBG,qBAAsBzC,KAAKkC,MAAMO,qBACjC2F,iBAAkBpI,KAAKkC,MAAMK,gBACzBiD,KAAK6C,IAAI,EAAGrI,KAAKkC,MAAMK,gBAAkBiB,KAAKC,OAC9C,KAER,CAOA,SAAA6E,CAAUC,GACR,GAAwB,mBAAbA,EACT,MAAM,IAAIC,UAAU,+BAGtB,OADAxI,KAAK2C,UAAU8F,IAAIF,GACZ,KACLvI,KAAK2C,UAAU+F,OAAOH,GAE1B,CAKA,eAAAzE,GACE,MAAMW,EAASzE,KAAKmI,mBAEpBQ,MAAMC,KAAK5I,KAAK2C,WAAWkG,QAAQN,IACjC,IACEA,EAAS9D,EACX,CAAE,MAAOrD,GACP,MAAM6G,EAAe7G,aAAiB1B,MAAQ0B,EAAMxB,QAAUuH,OAAO/F,GACrEJ,EAAOI,MAAM,kBAAmB6G,EAClC,GAEJ,CAKA,OAAAa,GACE9I,KAAK8H,mBACL9H,KAAK2C,UAAUoG,QACf/I,KAAKkC,MAAQ,CACXC,eAAe,EACfC,WAAW,EACXC,gBAAiB,KACjBjB,MAAO,KACPkB,UAAW,KACXC,gBAAiB,KACjBR,YAAa,KACbS,YAAa,KACbC,sBAAsB,GAGxBzC,KAAKsF,UAAU,gBAAiB,IAAI,GACpCtF,KAAKsF,UAAU,sBAAuB,IAAI,GAC1CtE,EAAOQ,KAAK,YACd,CAMA,kBAAOwH,GAIL,OAHKtH,EAAeC,WAClBD,EAAeC,SAAW,IAAID,GAEzBA,EAAeC,QACxB,ECheU,MAACsH,EAAkB,gPAQxBlB,eAAgCpD,EAAKuE,EAAU,IACpD,MAAMC,EAAiBzH,EAAesH,cAatC,GAVAE,EAAQhG,YAAcgG,EAAQhG,aAAe,UAG7CgG,EAAQ3E,QAAU,IACb2E,EAAQ3E,QACX,gBAAiB4E,EAAe5C,eAChC,oBAAqB0C,GAInBE,EAAe3C,uBAAwB,CACzC,MAAMzC,EAAQoF,EAAenF,SAASmF,EAAevH,OAAOK,iBACxD8B,IACFmF,EAAQ3E,QAAQ4E,EAAevH,OAAOK,iBAAmB8B,EAE7D,CAEA,IAAIK,QAAiBC,MAAMM,EAAKuE,GAGhC,GAAwB,MAApB9E,EAASK,OAAgB,CAC3B,MAAM2E,EAAiBhF,EAASiF,QAChC,IAEE,GAAuB,2BADJD,EAAevE,QACzByE,UAAiC,CACxC,GAAIH,EAAerD,qBACXqD,EAAepD,qBAChB,CACiBoD,EAAenF,SAASmF,EAAevH,OAAOK,wBAE5DkH,EAAenB,cAEzB,CAGA,GAFAkB,EAAQ3E,QAAQ,iBAAmB4E,EAAe5C,eAClD2C,EAAQ3E,QAAQ,qBAAuB0E,EACnCE,EAAe3C,uBAAwB,CACzC,MAAM+C,EAAWJ,EAAenF,SAASmF,EAAevH,OAAOK,iBAC3DsH,IACFL,EAAQ3E,QAAQ4E,EAAevH,OAAOK,iBAAmBsH,EAE7D,CACAnF,QAAiBC,MAAMM,EAAKuE,EAC9B,CACF,CAAE,MAAOM,GAET,CACF,CAEA,OAAOpF,CACT,iDAMO,SAA+BqF,GACpC,MAAMN,EAAiBzH,EAAesH,cAsDtC,OAnDAS,EAAcC,aAAaC,QAAQC,IAChChI,IAGC,GAFAA,EAAO2C,QAAQ,iBAAmB4E,EAAe5C,eACjD3E,EAAO2C,QAAQ,qBAAuB0E,EAClCE,EAAe3C,uBAAwB,CACzC,MAAMzC,EAAQoF,EAAenF,SAASmF,EAAevH,OAAOK,iBACxD8B,IACFnC,EAAO2C,QAAQ4E,EAAevH,OAAOK,iBAAmB8B,EAE5D,CACA,OAAOnC,GAERR,GAAUuE,QAAQkE,OAAOzI,IAI5BqI,EAAcC,aAAatF,SAASwF,IACjCxF,GAAaA,EACd2D,MAAO3G,IACL,MAAM0I,EAAkB1I,EAAMQ,OAG9B,GAA+B,MAA3BR,EAAMgD,UAAUK,SAAmBqF,EAAgBC,QACb,oBAApC3I,EAAMgD,UAAUQ,MAAM0E,UAAiC,CAGzD,GAFAQ,EAAgBC,QAAS,EAErBZ,EAAerD,qBACXqD,EAAepD,qBAChB,CACiBoD,EAAenF,SAASmF,EAAevH,OAAOK,wBAE5DkH,EAAenB,cAEzB,CAIA,GAFA8B,EAAgBvF,QAAQ,iBAAmB4E,EAAe5C,eAC1DuD,EAAgBvF,QAAQ,qBAAuB0E,EAC3CE,EAAe3C,uBAAwB,CACzC,MAAM+C,EAAWJ,EAAenF,SAASmF,EAAevH,OAAOK,iBAC3DsH,IACFO,EAAgBvF,QAAQ4E,EAAevH,OAAOK,iBAAmBsH,EAErE,CACA,OAAOE,EAAcK,EACvB,CAGF,OAAOnE,QAAQkE,OAAOzI,KAInBqI,CACT,qBChHO,SAAoBP,EAAU,IACjC,MAAMc,eAAEA,GAAiB,GAASd,EAE5BC,EAAiBzH,EAAesH,eAE/BiB,EAAcC,GAAmBC,WAAS,IAC7ChB,EAAehB,oBAInBiC,EAAAA,UAAU,IACcjB,EAAeb,UAAW+B,IAC1CH,EAAgBG,KAIrB,IAGHD,EAAAA,UAAU,MACFJ,GAAmBC,EAAa9H,eAAkB8H,EAAa7H,WAAc6H,EAAaxH,sBAC1F0G,EAAe/F,aAAakH,MAAMlJ,IAC9BJ,EAAOI,MAAM,8BAA+BA,MAGrD,CAAC4I,EAAgBC,EAAa9H,cAAe8H,EAAa7H,UAAW6H,EAAaxH,uBAGrF,MAAM8H,EAAUC,EAAAA,YAAYzC,UACxB,UACUoB,EAAenB,cACzB,CAAE,MAAO5G,GAEL,MADAJ,EAAOI,MAAM,yBAA0BA,GACjCA,CACV,GACD,IAGGgC,EAAaoH,EAAAA,YAAYzC,UAC3B,UACUoB,EAAe/F,YACzB,CAAE,MAAOhC,GAEL,MADAJ,EAAOI,MAAM,gCAAiCA,GACxCA,CACV,GACD,IAEH,MAAO,CAEHe,cAAe8H,EAAa9H,cAC5BC,UAAW6H,EAAa7H,UACxBhB,MAAO6I,EAAa7I,MACpBiB,gBAAiB4H,EAAa5H,gBAC9BE,gBAAiB0H,EAAa1H,gBAC9B6F,iBAAkB6B,EAAa7B,iBAC/B3F,qBAAsBwH,EAAaxH,qBAGnC8H,UACAnH,aAER"}
|
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 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};
|
|
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,"x-client-platform":"web",...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}}const d="web";async function u(e,t={}){const i=f.getInstance();if(t.credentials=t.credentials||"include",t.headers={...t.headers,"cf-session-id":i.getSessionId(),"x-client-platform":d},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(),t.headers["x-client-platform"]=d,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 g(e){const t=f.getInstance();return e.interceptors.request.use(e=>{if(e.headers["cf-session-id"]=t.getSessionId(),e.headers["x-client-platform"]=d,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(),s.headers["x-client-platform"]=d,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,d as CLIENT_PLATFORM,r as ConfigurationError,l as LOG_LEVELS,o as NetworkError,a as SSRError,s as SessionError,f as SessionManager,f as default,u as fetchInterceptor,h as logger,g as setupAxiosInterceptor,c as useSession};
|
|
2
2
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -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 * @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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"}
|
|
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';\nconst CLIENT_PLATFORM = 'web';\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 'x-client-platform': CLIENT_PLATFORM,\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';\nexport const CLIENT_PLATFORM = 'web';\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 'x-client-platform': CLIENT_PLATFORM,\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 options.headers['x-client-platform'] = CLIENT_PLATFORM;\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 config.headers['x-client-platform'] = CLIENT_PLATFORM;\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 originalRequest.headers['x-client-platform'] = CLIENT_PLATFORM;\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","CLIENT_PLATFORM","fetchInterceptor","clonedResponse","clone","error_msg","newToken","e","setupAxiosInterceptor","axiosInstance","interceptors","request","use","reject","originalRequest","_retry"],"mappings":"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"}
|
package/package.json
CHANGED