@mapnests/gateway-web-sdk 1.0.8 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.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 +2 -2
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 n extends t{constructor(e,t){super(e,"NETWORK_ERROR",t),this.name="NetworkError"}}class r extends t{constructor(e,t={}){super(e,"SSR_ERROR",t),this.name="SSRError"}}const o={NONE:0,ERROR:1,WARN:2,INFO:3,DEBUG:4};const a=new class{constructor(){this.level=o.WARN}setLevel(e){this.level="string"==typeof e?o[e.toUpperCase()]??o.WARN:e}error(...e){this.level>=o.ERROR&&console.error("[SessionManager]",...e)}warn(...e){this.level>=o.WARN&&console.warn("[SessionManager]",...e)}info(...e){this.level>=o.INFO&&console.info("[SessionManager]",...e)}debug(...e){this.level>=o.DEBUG&&console.debug("[SessionManager]",...e)}},l={bootstrapUrl:"/session/bootstrap",refreshInterval:15e5,tokenExpiry:18e5,maxRetries:3,bootstrapTimeout:3e4,tokenCookieName:"token",invalidSessionError:"INVALID-GW-SESSION"};class h{constructor(){if(h.instance)return h.instance;this.config={...l},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,this._configured=!1,this._boundHandleVisibilityChange=this._handleVisibilityChange.bind(this),this._boundHandlePageShow=this._handlePageShow.bind(this),this._boundHandleOnline=this._handleOnline.bind(this),h.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})}if(void 0!==e.maxRetries&&("number"!=typeof e.maxRetries||!Number.isInteger(e.maxRetries)||e.maxRetries<1))throw new i("maxRetries must be a positive integer",{maxRetries:e.maxRetries});this.config={...this.config,...e,credentials:void 0===e.credentials||e.credentials,logLevel:e.logLevel||"WARN"},a.setLevel(this.config.logLevel),this._configured=!0,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 r("Cannot initialize in non-browser environment (SSR)");if(this._configured||a.warn("initialize() called before configure(). Using default config."),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(this.state.isInitialized&&!e)return a.debug("Session already initialized"),{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();a.info(`Calling bootstrap API (attempt ${t}/${this.config.maxRetries}):`,this.config.bootstrapUrl);const i=new AbortController,r=setTimeout(()=>i.abort(),this.config.bootstrapTimeout);let o,l;try{o=await fetch(this.config.bootstrapUrl,{method:"GET",credentials:this.config.credentials?"include":"same-origin",signal:i.signal,headers:{"cf-session-id":e,"x-client-platform":"web",...this.config.headers}})}catch(e){if("AbortError"===e.name)throw new n(`Bootstrap request timed out after ${this.config.bootstrapTimeout}ms`,{timeout:this.config.bootstrapTimeout,url:this.config.bootstrapUrl});throw e}finally{clearTimeout(r)}if(!o.ok)throw new s(`Bootstrap failed: ${o.status} ${o.statusText}`,{status:o.status,statusText:o.statusText,url:this.config.bootstrapUrl});try{l=await o.json()}catch(e){throw new s("Bootstrap response is not valid JSON",{originalError:e.message})}a.info("Bootstrap successful");const h=l.refresh_time?1e3*l.refresh_time:this.config.refreshInterval,d=l.expire_time?1e3*l.expire_time:this.config.tokenExpiry,f=l[this.config.tokenCookieName];return f&&this.setCookie(this.config.tokenCookieName,f,d/1e3),this.setCfSessionId(e,d/1e3),this.state.isInitialized=!0,this.state.isLoading=!1,this.state.lastRefreshTime=Date.now(),this.state.nextRefreshTime=Date.now()+h,this.state.tokenExpiry=d,this.state.error=null,this.startTokenRefresh(h),this.notifyListeners(),l}catch(i){if(e=i instanceof Error?i:new n("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()}_isTokenLikelyExpired(){return!this.state.lastRefreshTime||!this.state.tokenExpiry||Date.now()>=this.state.lastRefreshTime+this.state.tokenExpiry}async ensureReady(){if(!this.state.isInitialized||this._isTokenLikelyExpired())if(this.initializationPromise)try{await this.initializationPromise}catch(e){a.debug("ensureReady: in-flight initialization failed, proceeding with request")}else if(this._configured&&!this.state.initializationFailed)try{await this.initialize(this.state.isInitialized)}catch(e){a.debug("ensureReady: initialization failed, proceeding with request")}}needsRefresh(){return this.state.isInitialized&&this._isTokenLikelyExpired()}setCfSessionId(e,t){this.state.cfSessionId=e;const i=t||this.config.tokenExpiry/1e3;this.setCookie("cf-session-id",e,i)}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}_sanitizeCookieName(e){return e.replace(/[^a-zA-Z0-9_-]/g,"")}getCookie(e){if("undefined"==typeof document)return null;const t=this._sanitizeCookieName(e);if(!t)return null;const i=`; ${document.cookie}`.split(`; ${t}=`);if(2===i.length)try{return decodeURIComponent(i.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=this._sanitizeCookieName(e);if(!s)return void a.error("Invalid cookie name provided");const n=new Date(Date.now()+1e3*i).toUTCString(),r="undefined"!=typeof window&&"https:"===window.location.protocol,o=r?"; Secure":"",l=encodeURIComponent(t);document.cookie=`${s}=${l}; path=/; expires=${n}; SameSite=Lax${o}`,a.debug(`Cookie set: ${s}, expires in ${i}s${r?" (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(t){const i=t instanceof Error?t.message:String(t),s=t?.code||"UNKNOWN_ERROR";a.error("Auto-refresh failed:",i),this.state.error=i,this.state.errorCode=s,this.notifyListeners();const n=Math.min(2*e,6e4);a.info(`Scheduling auto-refresh retry in ${n/1e3}s`),this.startTokenRefresh(n)}},e),this._startVisibilityListener(),this._startOnlineListener()}stopTokenRefresh(){this.refreshTimerId&&(clearTimeout(this.refreshTimerId),this.refreshTimerId=null,a.debug("Token refresh timer stopped")),this._stopVisibilityListener(),this._stopOnlineListener()}_startVisibilityListener(){"undefined"!=typeof document&&(document.addEventListener("visibilitychange",this._boundHandleVisibilityChange),"undefined"!=typeof window&&window.addEventListener("pageshow",this._boundHandlePageShow))}_stopVisibilityListener(){"undefined"!=typeof document&&(document.removeEventListener("visibilitychange",this._boundHandleVisibilityChange),"undefined"!=typeof window&&window.removeEventListener("pageshow",this._boundHandlePageShow))}_refreshIfOverdue(){if(!this.state.isInitialized||this.state.initializationFailed)return!1;if(this.isRefreshing())return!1;const e=Date.now(),{nextRefreshTime:t}=this.state;return!!(t&&e>=t)&&(a.info("Missed scheduled refresh — refreshing token now"),this.refreshToken().catch(e=>{const t=e instanceof Error?e.message:String(e);a.error("Triggered refresh failed:",t)}),!0)}_handleVisibilityChange(){"visible"===document.visibilityState&&this._refreshIfOverdue()}_handlePageShow(e){e.persisted&&this.state.isInitialized&&!this.state.initializationFailed&&(this.isRefreshing()||(a.info("Page restored from bfcache — forcing bootstrap"),this.refreshToken().catch(e=>{const t=e instanceof Error?e.message:String(e);a.error("bfcache-triggered refresh failed:",t)})))}_startOnlineListener(){"undefined"!=typeof window&&window.addEventListener("online",this._boundHandleOnline)}_stopOnlineListener(){"undefined"!=typeof window&&window.removeEventListener("online",this._boundHandleOnline)}_handleOnline(){this.state.initializationFailed&&(a.info("Network restored — retrying session initialization"),this.refreshToken().catch(e=>{const t=e instanceof Error?e.message:String(e);a.error("Online-triggered refresh failed:",t)}))}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.initializationPromise=null,this.listeners.clear(),this.setCookie("cf-session-id","",-1),this.setCookie(this.config.tokenCookieName,"",-1),this.config={...l},this.state={isInitialized:!1,isLoading:!1,lastRefreshTime:null,error:null,errorCode:null,nextRefreshTime:null,tokenExpiry:null,cfSessionId:null,initializationFailed:!1},this._configured=!1,a.info("Destroyed")}static getInstance(){return h.instance||(h.instance=new h),h.instance}}const d="web";exports.BootstrapError=s,exports.CLIENT_PLATFORM=d,exports.ConfigurationError=i,exports.LOG_LEVELS=o,exports.NetworkError=n,exports.SSRError=r,exports.SessionError=t,exports.SessionManager=h,exports.default=h,exports.fetchInterceptor=async function(e,t={}){const i=h.getInstance();await i.ensureReady();const s={...t,credentials:t.credentials||"include",headers:{...t.headers,"cf-session-id":i.getSessionId(),"x-client-platform":d}};if(i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(s.headers[i.config.tokenCookieName]=e)}let n=await fetch(e,s);if(401===n.status){const t=n.clone();try{if((await t.json()).error_msg===i.config.invalidSessionError){if(i.isRefreshing()?await i.waitForRefresh():await i.refreshToken(),s.headers["cf-session-id"]=i.getSessionId(),i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(s.headers[i.config.tokenCookieName]=e)}n=await fetch(e,s)}}catch(e){}}return n},exports.logger=a,exports.setupAxiosInterceptor=function(e){const t=h.getInstance();return void 0!==e._gwSessionRequestId&&e.interceptors.request.eject(e._gwSessionRequestId),void 0!==e._gwSessionResponseId&&e.interceptors.response.eject(e._gwSessionResponseId),e._gwSessionRequestId=e.interceptors.request.use(async e=>{if(await t.ensureReady(),t.config.credentials&&(e.withCredentials=!0),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._gwSessionResponseId=e.interceptors.response.use(e=>e,async i=>{const s=i.config;if(401===i.response?.status&&!s._retry&&i.response?.data?.error_msg===t.config.invalidSessionError){if(s._retry=!0,t.isRefreshing()?await t.waitForRefresh():await t.refreshToken(),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},exports.useSession=function(t={}){const{autoInitialize:i=!0}=t,s=h.getInstance(),[n,r]=e.useState(()=>s.getSessionStatus()),[o,l]=e.useState(()=>{const{nextRefreshTime:e}=s.getSessionStatus();return e?Math.max(0,e-Date.now()):null}),d=e.useRef(n.nextRefreshTime);e.useEffect(()=>s.subscribe(e=>{r(e),d.current=e.nextRefreshTime}),[]),e.useEffect(()=>{if(!n.nextRefreshTime)return void l(null);l(Math.max(0,n.nextRefreshTime-Date.now()));const e=setInterval(()=>{const e=d.current;l(e?Math.max(0,e-Date.now()):null)},1e3);return()=>clearInterval(e)},[n.nextRefreshTime]),e.useEffect(()=>{!i||n.isInitialized||n.isLoading||n.initializationFailed||s.initialize().catch(e=>{a.error("Auto-initialization failed:",e)})},[i,n.isInitialized,n.isLoading,n.initializationFailed]);const f=e.useCallback(async()=>{try{await s.refreshToken()}catch(e){throw a.error("Manual refresh failed:",e),e}},[]),c=e.useCallback(async()=>{try{await s.initialize()}catch(e){throw a.error("Manual initialization failed:",e),e}},[]);return{isInitialized:n.isInitialized,isLoading:n.isLoading,error:n.error,lastRefreshTime:n.lastRefreshTime,nextRefreshTime:n.nextRefreshTime,timeUntilRefresh:o,initializationFailed:n.initializationFailed,refresh:f,initialize:c}};
|
|
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 n extends t{constructor(e,t){super(e,"NETWORK_ERROR",t),this.name="NetworkError"}}class r extends t{constructor(e,t={}){super(e,"SSR_ERROR",t),this.name="SSRError"}}const o={bootstrapUrl:"/session/bootstrap",refreshInterval:15e5,tokenExpiry:18e5,maxRetries:3,bootstrapTimeout:3e4,tokenCookieName:"token",invalidSessionError:"INVALID-GW-SESSION"};class a{constructor(){if(a.instance)return a.instance;this.config={...o},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,this._configured=!1,this._boundHandleVisibilityChange=this._handleVisibilityChange.bind(this),this._boundHandlePageShow=this._handlePageShow.bind(this),this._boundHandleOnline=this._handleOnline.bind(this),a.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})}if(void 0!==e.maxRetries&&("number"!=typeof e.maxRetries||!Number.isInteger(e.maxRetries)||e.maxRetries<1))throw new i("maxRetries must be a positive integer",{maxRetries:e.maxRetries});this.config={...this.config,...e,credentials:void 0===e.credentials||e.credentials},this._configured=!0}async initialize(e=!1){if("undefined"==typeof window)throw new r("Cannot initialize in non-browser environment (SSR)");if(this.state.initializationFailed)throw new s("Initialization failed after max retries. Reload page to retry.");if(this.initializationPromise)return this.initializationPromise;if(this.state.isInitialized&&!e)return{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(),t=new AbortController,i=setTimeout(()=>t.abort(),this.config.bootstrapTimeout);let r,o;try{r=await fetch(this.config.bootstrapUrl,{method:"GET",credentials:this.config.credentials?"include":"same-origin",signal:t.signal,headers:{"cf-session-id":e,"x-client-platform":"web",...this.config.headers}})}catch(e){if("AbortError"===e.name)throw new n(`Bootstrap request timed out after ${this.config.bootstrapTimeout}ms`,{timeout:this.config.bootstrapTimeout,url:this.config.bootstrapUrl});throw e}finally{clearTimeout(i)}if(!r.ok)throw new s(`Bootstrap failed: ${r.status} ${r.statusText}`,{status:r.status,statusText:r.statusText,url:this.config.bootstrapUrl});try{o=await r.json()}catch(e){throw new s("Bootstrap response is not valid JSON",{originalError:e.message})}const a="number"==typeof o.refresh_time&&isFinite(o.refresh_time)&&o.refresh_time>0?1e3*o.refresh_time:this.config.refreshInterval,h="number"==typeof o.expire_time&&isFinite(o.expire_time)&&o.expire_time>0?1e3*o.expire_time:this.config.tokenExpiry,l=o[this.config.tokenCookieName];return l&&this.setCookie(this.config.tokenCookieName,l,h/1e3),this.setCfSessionId(e,h/1e3),this.state.isInitialized=!0,this.state.isLoading=!1,this.state.lastRefreshTime=Date.now(),this.state.nextRefreshTime=Date.now()+a,this.state.tokenExpiry=h,this.state.error=null,this.startTokenRefresh(a),this.notifyListeners(),o}catch(i){if(e=i instanceof Error?i:new n("Unknown error occurred",{error:i}),t<this.config.maxRetries){const e=Math.min(1e3*Math.pow(2,t-1),5e3);await new Promise(t=>setTimeout(t,e))}}throw 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?this.initializationPromise:Promise.resolve()}_isTokenLikelyExpired(){return!this.state.lastRefreshTime||!this.state.tokenExpiry||Date.now()>=this.state.lastRefreshTime+this.state.tokenExpiry}async ensureReady(){if(!this.state.isInitialized||this._isTokenLikelyExpired())if(this.initializationPromise)try{await this.initializationPromise}catch(e){}else if(this._configured&&!this.state.initializationFailed)try{await this.initialize(this.state.isInitialized)}catch(e){}}needsRefresh(){return this.state.isInitialized&&this._isTokenLikelyExpired()}setCfSessionId(e,t){this.state.cfSessionId=e;const i=t||this.config.tokenExpiry/1e3;this.setCookie("cf-session-id",e,i)}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}_sanitizeCookieName(e){return e.replace(/[^a-zA-Z0-9_-]/g,"")}getCookie(e){if("undefined"==typeof document)return null;const t=this._sanitizeCookieName(e);if(!t)return null;const i=`; ${document.cookie}`.split(`; ${t}=`);if(2===i.length)try{return decodeURIComponent(i.pop().split(";").shift())}catch(e){return null}return null}getToken(e=this.config.tokenCookieName){return this.getCookie(e)}setCookie(e,t,i){if("undefined"==typeof document)return;const s=this._sanitizeCookieName(e);if(!s)return;const n=new Date(Date.now()+1e3*i).toUTCString(),r="undefined"!=typeof window&&"https:"===window.location.protocol?"; Secure":"",o=encodeURIComponent(t);document.cookie=`${s}=${o}; path=/; expires=${n}; SameSite=Lax${r}`}startTokenRefresh(e=this.config.refreshInterval){this.stopTokenRefresh(),this.refreshTimerId=setTimeout(async()=>{try{await this.refreshToken()}catch(t){const i=t instanceof Error?t.message:String(t),s=t?.code||"UNKNOWN_ERROR";this.state.error=i,this.state.errorCode=s,this.notifyListeners();const n=Math.min(2*e,6e4);this.startTokenRefresh(n)}},e),this._startVisibilityListener(),this._startOnlineListener()}stopTokenRefresh(){this.refreshTimerId&&(clearTimeout(this.refreshTimerId),this.refreshTimerId=null),this._stopVisibilityListener(),this._stopOnlineListener()}_startVisibilityListener(){"undefined"!=typeof document&&(document.addEventListener("visibilitychange",this._boundHandleVisibilityChange),"undefined"!=typeof window&&window.addEventListener("pageshow",this._boundHandlePageShow))}_stopVisibilityListener(){"undefined"!=typeof document&&(document.removeEventListener("visibilitychange",this._boundHandleVisibilityChange),"undefined"!=typeof window&&window.removeEventListener("pageshow",this._boundHandlePageShow))}_refreshIfOverdue(){if(!this.state.isInitialized||this.state.initializationFailed)return!1;if(this.isRefreshing())return!1;const e=Date.now(),{nextRefreshTime:t}=this.state;return!!(t&&e>=t)&&(this.refreshToken().catch(()=>{}),!0)}_handleVisibilityChange(){"visible"===document.visibilityState&&this._refreshIfOverdue()}_handlePageShow(e){e.persisted&&this.state.isInitialized&&!this.state.initializationFailed&&(this.isRefreshing()||this.refreshToken().catch(()=>{}))}_startOnlineListener(){"undefined"!=typeof window&&window.addEventListener("online",this._boundHandleOnline)}_stopOnlineListener(){"undefined"!=typeof window&&window.removeEventListener("online",this._boundHandleOnline)}_handleOnline(){this.state.initializationFailed&&this.refreshToken().catch(()=>{})}async refreshToken(){return 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){}})}destroy(){this.stopTokenRefresh(),this.initializationPromise=null,this.listeners.clear(),this.setCookie("cf-session-id","",-1),this.setCookie(this.config.tokenCookieName,"",-1),this.config={...o},this.state={isInitialized:!1,isLoading:!1,lastRefreshTime:null,error:null,errorCode:null,nextRefreshTime:null,tokenExpiry:null,cfSessionId:null,initializationFailed:!1},this._configured=!1}static getInstance(){return a.instance||(a.instance=new a),a.instance}}const h="web";const l=new class{setLevel(){}error(){}warn(){}info(){}debug(){}};exports.BootstrapError=s,exports.CLIENT_PLATFORM=h,exports.ConfigurationError=i,exports.LOG_LEVELS={NONE:0,ERROR:1,WARN:2,INFO:3,DEBUG:4},exports.NetworkError=n,exports.SSRError=r,exports.SessionError=t,exports.SessionManager=a,exports.default=a,exports.fetchInterceptor=async function(e,t={}){const i=a.getInstance();await i.ensureReady();const s={...t,credentials:t.credentials||"include",headers:{...t.headers,"cf-session-id":i.getSessionId(),"x-client-platform":h}};if(i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(s.headers[i.config.tokenCookieName]=e)}let n=await fetch(e,s);if(401===n.status){const t=n.clone();try{if((await t.json()).error_msg===i.config.invalidSessionError){if(i.isRefreshing()?await i.waitForRefresh():await i.refreshToken(),s.headers["cf-session-id"]=i.getSessionId(),i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(s.headers[i.config.tokenCookieName]=e)}n=await fetch(e,s)}}catch(e){}}return n},exports.logger=l,exports.setupAxiosInterceptor=function(e){const t=a.getInstance();return void 0!==e._gwSessionRequestId&&e.interceptors.request.eject(e._gwSessionRequestId),void 0!==e._gwSessionResponseId&&e.interceptors.response.eject(e._gwSessionResponseId),e._gwSessionRequestId=e.interceptors.request.use(async e=>{if(await t.ensureReady(),t.config.credentials&&(e.withCredentials=!0),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._gwSessionResponseId=e.interceptors.response.use(e=>e,async i=>{const s=i.config;if(401===i.response?.status&&!s._retry&&i.response?.data?.error_msg===t.config.invalidSessionError){if(s._retry=!0,t.isRefreshing()?await t.waitForRefresh():await t.refreshToken(),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=a.getInstance(),[n,r]=e.useState(()=>s.getSessionStatus()),[o,h]=e.useState(()=>{const{nextRefreshTime:e}=s.getSessionStatus();return e?Math.max(0,e-Date.now()):null}),l=e.useRef(n.nextRefreshTime);e.useEffect(()=>s.subscribe(e=>{r(e),l.current=e.nextRefreshTime}),[]),e.useEffect(()=>{if(!n.nextRefreshTime)return void h(null);h(Math.max(0,n.nextRefreshTime-Date.now()));const e=setInterval(()=>{const e=l.current;h(e?Math.max(0,e-Date.now()):null)},1e3);return()=>clearInterval(e)},[n.nextRefreshTime]),e.useEffect(()=>{!i||n.isInitialized||n.isLoading||n.initializationFailed||s.initialize().catch(()=>{})},[i,n.isInitialized,n.isLoading,n.initializationFailed]);const d=e.useCallback(async()=>{await s.refreshToken()},[]),f=e.useCallback(async()=>{await s.initialize()},[]);return{isInitialized:n.isInitialized,isLoading:n.isLoading,error:n.error,lastRefreshTime:n.lastRefreshTime,nextRefreshTime:n.nextRefreshTime,timeUntilRefresh:o,initializationFailed:n.initializationFailed,refresh:d,initialize:f}};
|
|
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';\nconst CLIENT_PLATFORM = 'web';\n\nconst DEFAULT_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 bootstrapTimeout: 30000, // 30 seconds timeout for bootstrap fetch\n tokenCookieName: 'token', // Must match server's session_header\n invalidSessionError: 'INVALID-GW-SESSION', // Must match server's error response\n};\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 = { ...DEFAULT_CONFIG };\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 this._configured = false;\n this._boundHandleVisibilityChange = this._handleVisibilityChange.bind(this);\n this._boundHandlePageShow = this._handlePageShow.bind(this);\n this._boundHandleOnline = this._handleOnline.bind(this);\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 {number} config.bootstrapTimeout - Timeout for bootstrap fetch in milliseconds (default: 30s)\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 if (config.maxRetries !== undefined) {\n if (typeof config.maxRetries !== 'number' || !Number.isInteger(config.maxRetries) || config.maxRetries < 1) {\n throw new ConfigurationError('maxRetries must be a positive integer', { maxRetries: config.maxRetries });\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 this._configured = true;\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} forceRefresh - Force a new bootstrap call even if already initialized (used by refreshToken)\n * @returns {Promise<Object>} Bootstrap response\n */\n async initialize(forceRefresh = false) {\n if (typeof window === 'undefined') {\n throw new SSRError('Cannot initialize in non-browser environment (SSR)');\n }\n\n if (!this._configured) {\n logger.warn('initialize() called before configure(). Using default config.');\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 // If already initialized in this page session and not a forced refresh, return current state\n if (this.state.isInitialized && !forceRefresh) {\n logger.debug('Session already initialized');\n return { token: this.getToken(this.config.tokenCookieName) };\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 const newSessionId = this.generateSessionId();\n\n logger.info(`Calling bootstrap API (attempt ${attempt}/${this.config.maxRetries}):`, this.config.bootstrapUrl);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.bootstrapTimeout);\n\n let response;\n try {\n response = await fetch(this.config.bootstrapUrl, {\n method: 'GET',\n credentials: this.config.credentials ? 'include' : 'same-origin',\n signal: controller.signal,\n headers: {\n 'cf-session-id': newSessionId,\n 'x-client-platform': CLIENT_PLATFORM,\n ...this.config.headers,\n },\n });\n } catch (fetchError) {\n if (fetchError.name === 'AbortError') {\n throw new NetworkError(`Bootstrap request timed out after ${this.config.bootstrapTimeout}ms`, {\n timeout: this.config.bootstrapTimeout,\n url: this.config.bootstrapUrl\n });\n }\n throw fetchError;\n } finally {\n clearTimeout(timeoutId);\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 // 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 from response (in seconds) or fall back to config\n const tokenExpiry = data.expire_time\n ? data.expire_time * 1000\n : this.config.tokenExpiry;\n\n // If server returned token in response body (non-cookie mode),\n // store it as a client-side cookie. Key matches server's session_header.\n const tokenFromResponse = data[this.config.tokenCookieName];\n if (tokenFromResponse) {\n this.setCookie(this.config.tokenCookieName, tokenFromResponse, tokenExpiry / 1000);\n }\n\n // Now safe to update cf-session-id — new token is stored/set\n this.setCfSessionId(newSessionId, tokenExpiry / 1000);\n\n this.state.isInitialized = true;\n this.state.isLoading = false;\n this.state.lastRefreshTime = Date.now();\n this.state.nextRefreshTime = Date.now() + refreshInterval;\n this.state.tokenExpiry = tokenExpiry;\n this.state.error = null;\n\n // Start automatic refresh timer with dynamic interval\n this.startTokenRefresh(refreshInterval);\n\n this.notifyListeners();\n return data;\n } catch (error) {\n lastError = error instanceof Error ? error : new NetworkError('Unknown error occurred', { error });\n logger.error(`Bootstrap attempt ${attempt} failed:`, lastError.message);\n\n if (attempt < this.config.maxRetries) {\n const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);\n logger.info(`Retrying in ${delay}ms...`);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n\n logger.error('All bootstrap attempts failed');\n this.state.isLoading = false;\n this.state.error = lastError?.message || 'Unknown error';\n this.state.errorCode = lastError?.code || 'UNKNOWN_ERROR';\n this.state.initializationFailed = true;\n this.notifyListeners();\n throw lastError;\n })();\n\n try {\n return await this.initializationPromise;\n } finally {\n this.initializationPromise = null;\n }\n }\n\n /**\n * Check if token refresh is currently in progress\n * @returns {boolean} True if refresh is in progress\n */\n isRefreshing() {\n return this.initializationPromise !== null;\n }\n\n /**\n * Wait for ongoing refresh to complete\n * @returns {Promise<Object>} Bootstrap response from ongoing refresh\n */\n async waitForRefresh() {\n if (this.initializationPromise) {\n logger.debug('Waiting for ongoing refresh to complete');\n return this.initializationPromise;\n }\n return Promise.resolve();\n }\n\n /**\n * Check if the token is likely expired based on in-memory timestamps.\n * After a full browser close + reopen, lastRefreshTime is null, so this returns true.\n * @returns {boolean} True if token appears expired or state is unknown\n */\n _isTokenLikelyExpired() {\n if (!this.state.lastRefreshTime || !this.state.tokenExpiry) return true;\n return Date.now() >= this.state.lastRefreshTime + this.state.tokenExpiry;\n }\n\n /**\n * Ensure the session is ready before making API calls.\n * - If initialized and token not expired, resolves immediately.\n * - If initialization is in progress, waits for it.\n * - If not initialized but configured, triggers initialization and waits.\n * - If not configured or permanently failed, resolves (lets request proceed;\n * the 401 handler in the interceptor will deal with it).\n * @returns {Promise<void>}\n */\n async ensureReady() {\n // Happy path: session is live and token hasn't expired\n if (this.state.isInitialized && !this._isTokenLikelyExpired()) {\n return;\n }\n\n // An initialization / refresh is already in flight — piggyback on it\n if (this.initializationPromise) {\n try {\n await this.initializationPromise;\n } catch (_) {\n // Initialization failed — let the request proceed, 401 handler will deal with it\n logger.debug('ensureReady: in-flight initialization failed, proceeding with request');\n }\n return;\n }\n\n // Not initialized / token expired, no call in flight, but we *can* bootstrap\n if (this._configured && !this.state.initializationFailed) {\n try {\n await this.initialize(this.state.isInitialized); // force refresh if already initialized but token expired\n } catch (_) {\n logger.debug('ensureReady: initialization failed, proceeding with request');\n }\n }\n\n // Not configured or permanently failed — nothing we can do here\n }\n\n /**\n * Check if the session needs a refresh (initialized but token expired).\n * Useful for interceptors to proactively refresh before sending a request.\n * @returns {boolean}\n */\n needsRefresh() {\n return this.state.isInitialized && this._isTokenLikelyExpired();\n }\n\n /**\n * Set cf-session-id in cookie\n * @param {string} sessionId - Session ID\n * @param {number} maxAge - Max age in seconds\n */\n setCfSessionId(sessionId, maxAge) {\n this.state.cfSessionId = sessionId;\n const cookieMaxAge = maxAge || this.config.tokenExpiry / 1000;\n this.setCookie('cf-session-id', sessionId, cookieMaxAge);\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 * Shared cookie name sanitizer used by both getCookie and setCookie\n * @param {string} name - Raw cookie name\n * @returns {string} Sanitized cookie name\n */\n _sanitizeCookieName(name) {\n return name.replace(/[^a-zA-Z0-9_-]/g, '');\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 sanitizedName = this._sanitizeCookieName(name);\n if (!sanitizedName) return null;\n const value = `; ${document.cookie}`;\n const parts = value.split(`; ${sanitizedName}=`);\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 const sanitizedName = this._sanitizeCookieName(name);\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 // Schedule retry instead of dying permanently\n const retryDelay = Math.min(interval * 2, 60000);\n logger.info(`Scheduling auto-refresh retry in ${retryDelay / 1000}s`);\n this.startTokenRefresh(retryDelay);\n }\n }, interval);\n\n // Listen for visibility changes (tab wake from sleep / bfcache restore)\n this._startVisibilityListener();\n // Listen for network recovery\n this._startOnlineListener();\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 this._stopVisibilityListener();\n this._stopOnlineListener();\n }\n\n /**\n * Start listening for page visibility changes and bfcache restore\n */\n _startVisibilityListener() {\n if (typeof document === 'undefined') return;\n document.addEventListener('visibilitychange', this._boundHandleVisibilityChange);\n if (typeof window !== 'undefined') {\n window.addEventListener('pageshow', this._boundHandlePageShow);\n }\n }\n\n /**\n * Stop listening for page visibility changes\n */\n _stopVisibilityListener() {\n if (typeof document === 'undefined') return;\n document.removeEventListener('visibilitychange', this._boundHandleVisibilityChange);\n if (typeof window !== 'undefined') {\n window.removeEventListener('pageshow', this._boundHandlePageShow);\n }\n }\n\n /**\n * Check if token refresh is overdue and trigger refresh if so\n * @returns {boolean} True if refresh was triggered\n */\n _refreshIfOverdue() {\n if (!this.state.isInitialized || this.state.initializationFailed) return false;\n if (this.isRefreshing()) return false;\n\n const now = Date.now();\n const { nextRefreshTime } = this.state;\n\n if (nextRefreshTime && now >= nextRefreshTime) {\n logger.info('Missed scheduled refresh — refreshing token now');\n this.refreshToken().catch(error => {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('Triggered refresh failed:', errorMessage);\n });\n return true;\n }\n return false;\n }\n\n /**\n * Handle visibility change — refresh if overdue when tab becomes visible\n */\n _handleVisibilityChange() {\n if (document.visibilityState !== 'visible') return;\n this._refreshIfOverdue();\n }\n\n /**\n * Handle pageshow — force bootstrap on bfcache restore (browser close + reopen)\n */\n _handlePageShow(event) {\n if (!event.persisted) return;\n // bfcache restore means browser was closed — always force a fresh bootstrap\n if (!this.state.isInitialized || this.state.initializationFailed) return;\n if (this.isRefreshing()) return;\n logger.info('Page restored from bfcache — forcing bootstrap');\n this.refreshToken().catch(error => {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('bfcache-triggered refresh failed:', errorMessage);\n });\n }\n\n /**\n * Start listening for network recovery\n */\n _startOnlineListener() {\n if (typeof window === 'undefined') return;\n window.addEventListener('online', this._boundHandleOnline);\n }\n\n /**\n * Stop listening for network recovery\n */\n _stopOnlineListener() {\n if (typeof window === 'undefined') return;\n window.removeEventListener('online', this._boundHandleOnline);\n }\n\n /**\n * Handle online event — retry if initialization had failed due to network\n */\n _handleOnline() {\n if (!this.state.initializationFailed) return;\n logger.info('Network restored — retrying session initialization');\n this.refreshToken().catch(error => {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('Online-triggered refresh failed:', errorMessage);\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 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.initializationPromise = null;\n this.listeners.clear();\n this.setCookie('cf-session-id', '', -1);\n this.setCookie(this.config.tokenCookieName, '', -1);\n this.config = { ...DEFAULT_CONFIG };\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 this._configured = false;\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;","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 // Gate on session readiness: if not initialized (e.g., browser was closed and\n // reopened), this triggers bootstrap and waits; if a refresh is in progress\n // (tab wake, online recovery, concurrent 401), it piggybacks on it.\n await sessionManager.ensureReady();\n\n // FIX #6: Shallow-clone options to avoid mutating the caller's object\n const requestOptions = {\n ...options,\n credentials: options.credentials || 'include',\n headers: {\n ...options.headers,\n 'cf-session-id': sessionManager.getSessionId(),\n 'x-client-platform': CLIENT_PLATFORM,\n },\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 requestOptions.headers[sessionManager.config.tokenCookieName] = token;\n }\n }\n\n let response = await fetch(url, requestOptions);\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 === sessionManager.config.invalidSessionError) {\n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n await sessionManager.refreshToken();\n }\n requestOptions.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const newToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (newToken) {\n requestOptions.headers[sessionManager.config.tokenCookieName] = newToken;\n }\n }\n response = await fetch(url, requestOptions);\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 * @returns {Object} The same axios instance with interceptors attached\n */\nexport function setupAxiosInterceptor(axiosInstance) {\n const sessionManager = SessionManager.getInstance();\n\n // FIX #7: Eject previous interceptors if this instance was already set up\n if (axiosInstance._gwSessionRequestId !== undefined) {\n axiosInstance.interceptors.request.eject(axiosInstance._gwSessionRequestId);\n }\n if (axiosInstance._gwSessionResponseId !== undefined) {\n axiosInstance.interceptors.response.eject(axiosInstance._gwSessionResponseId);\n }\n\n // Request interceptor to add cf-session-id, token, and credentials\n axiosInstance._gwSessionRequestId = axiosInstance.interceptors.request.use(\n async (config) => {\n // Gate on session readiness before every request\n await sessionManager.ensureReady();\n if (sessionManager.config.credentials) {\n config.withCredentials = true;\n }\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._gwSessionResponseId = 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 === sessionManager.config.invalidSessionError) {\n originalRequest._retry = true;\n\n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n await sessionManager.refreshToken();\n }\n\n originalRequest.headers['cf-session-id'] = sessionManager.getSessionId();\n 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}","import { useState, useEffect, useCallback, useRef } 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 // FIX #11: Separate countdown state — only this re-renders every second,\n // not the full session state which would cause unnecessary subscriber churn\n const [timeUntilRefresh, setTimeUntilRefresh] = useState(() => {\n const { nextRefreshTime } = sessionManager.getSessionStatus();\n return nextRefreshTime ? Math.max(0, nextRefreshTime - Date.now()) : null;\n });\n\n const nextRefreshTimeRef = useRef(sessionState.nextRefreshTime);\n\n // Subscribe to meaningful session state changes only\n useEffect(() => {\n const unsubscribe = sessionManager.subscribe((newState) => {\n setSessionState(newState);\n nextRefreshTimeRef.current = newState.nextRefreshTime;\n });\n\n return unsubscribe;\n }, []); // sessionManager is a singleton, no need to include in deps\n\n // Local tick timer for countdown display — avoids global tick in SessionManager\n useEffect(() => {\n if (!sessionState.nextRefreshTime) {\n setTimeUntilRefresh(null);\n return;\n }\n // Set immediately on mount / when nextRefreshTime changes\n setTimeUntilRefresh(Math.max(0, sessionState.nextRefreshTime - Date.now()));\n const tickId = setInterval(() => {\n const nrt = nextRefreshTimeRef.current;\n setTimeUntilRefresh(nrt ? Math.max(0, nrt - Date.now()) : null);\n }, 1000);\n return () => clearInterval(tickId);\n }, [sessionState.nextRefreshTime]);\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,\n initializationFailed: sessionState.initializationFailed,\n\n // Actions\n refresh,\n initialize,\n };\n}\n\nexport default useSession;"],"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","DEFAULT_CONFIG","bootstrapUrl","refreshInterval","tokenExpiry","maxRetries","bootstrapTimeout","tokenCookieName","invalidSessionError","SessionManager","instance","config","state","isInitialized","isLoading","lastRefreshTime","errorCode","nextRefreshTime","cfSessionId","initializationFailed","refreshTimerId","listeners","Set","initializationPromise","_configured","_boundHandleVisibilityChange","_handleVisibilityChange","bind","_boundHandlePageShow","_handlePageShow","_boundHandleOnline","_handleOnline","configure","trim","undefined","isFinite","Number","isInteger","credentials","logLevel","initialize","forceRefresh","window","token","getToken","notifyListeners","lastError","attempt","newSessionId","generateSessionId","controller","AbortController","timeoutId","setTimeout","abort","response","data","fetch","method","signal","headers","fetchError","timeout","url","clearTimeout","ok","status","statusText","json","jsonError","originalError","refresh_time","expire_time","tokenFromResponse","setCookie","setCfSessionId","Date","now","startTokenRefresh","delay","Math","min","pow","Promise","resolve","isRefreshing","waitForRefresh","_isTokenLikelyExpired","ensureReady","_","needsRefresh","sessionId","maxAge","cookieMaxAge","crypto","randomUUID","random","toString","substr","getSessionId","shouldUseTokenHeader","location","protocol","_sanitizeCookieName","replace","getCookie","document","sanitizedName","parts","cookie","split","length","decodeURIComponent","pop","shift","String","value","expires","toUTCString","isSecure","secureFlag","encodedValue","encodeURIComponent","interval","stopTokenRefresh","async","refreshToken","errorMessage","retryDelay","_startVisibilityListener","_startOnlineListener","_stopVisibilityListener","_stopOnlineListener","addEventListener","removeEventListener","_refreshIfOverdue","catch","visibilityState","event","persisted","getSessionStatus","timeUntilRefresh","max","subscribe","listener","TypeError","add","delete","Array","from","forEach","destroy","clear","getInstance","CLIENT_PLATFORM","options","sessionManager","requestOptions","clonedResponse","clone","error_msg","newToken","e","axiosInstance","_gwSessionRequestId","interceptors","request","eject","_gwSessionResponseId","use","withCredentials","reject","originalRequest","_retry","autoInitialize","sessionState","setSessionState","useState","setTimeUntilRefresh","nextRefreshTimeRef","useRef","useEffect","newState","current","tickId","setInterval","nrt","clearInterval","refresh","useCallback"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/errors.js","../src/SessionManager.js","../src/interceptors.js","../src/logger.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","import {BootstrapError, ConfigurationError, NetworkError, SSRError} from './errors.js';\nconst CLIENT_PLATFORM = 'web';\n\nconst DEFAULT_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 bootstrapTimeout: 30000, // 30 seconds timeout for bootstrap fetch\n tokenCookieName: 'token', // Must match server's session_header\n invalidSessionError: 'INVALID-GW-SESSION', // Must match server's error response\n};\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 = { ...DEFAULT_CONFIG };\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 this._configured = false;\n this._boundHandleVisibilityChange = this._handleVisibilityChange.bind(this);\n this._boundHandlePageShow = this._handlePageShow.bind(this);\n this._boundHandleOnline = this._handleOnline.bind(this);\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 {number} config.bootstrapTimeout - Timeout for bootstrap fetch in milliseconds (default: 30s)\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 if (config.maxRetries !== undefined) {\n if (typeof config.maxRetries !== 'number' || !Number.isInteger(config.maxRetries) || config.maxRetries < 1) {\n throw new ConfigurationError('maxRetries must be a positive integer', { maxRetries: config.maxRetries });\n }\n }\n\n this.config = {\n ...this.config,\n ...config,\n credentials: config.credentials !== undefined ? config.credentials : true,\n };\n\n this._configured = true;\n }\n\n /**\n * Initialize session by calling bootstrap API\n * @param {boolean} forceRefresh - Force a new bootstrap call even if already initialized (used by refreshToken)\n * @returns {Promise<Object>} Bootstrap response\n */\n async initialize(forceRefresh = 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 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 return this.initializationPromise;\n }\n\n // If already initialized in this page session and not a forced refresh, return current state\n if (this.state.isInitialized && !forceRefresh) {\n return { token: this.getToken(this.config.tokenCookieName) };\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 const newSessionId = this.generateSessionId();\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.bootstrapTimeout);\n\n let response;\n try {\n response = await fetch(this.config.bootstrapUrl, {\n method: 'GET',\n credentials: this.config.credentials ? 'include' : 'same-origin',\n signal: controller.signal,\n headers: {\n 'cf-session-id': newSessionId,\n 'x-client-platform': CLIENT_PLATFORM,\n ...this.config.headers,\n },\n });\n } catch (fetchError) {\n if (fetchError.name === 'AbortError') {\n throw new NetworkError(`Bootstrap request timed out after ${this.config.bootstrapTimeout}ms`, {\n timeout: this.config.bootstrapTimeout,\n url: this.config.bootstrapUrl\n });\n }\n throw fetchError;\n } finally {\n clearTimeout(timeoutId);\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\n // Use refresh_time from response (in seconds) or fall back to config.\n // Reject non-positive values to prevent silent cookie expiry failures.\n const refreshInterval =\n typeof data.refresh_time === 'number' && isFinite(data.refresh_time) && data.refresh_time > 0\n ? data.refresh_time * 1000\n : this.config.refreshInterval;\n\n // Calculate token expiry from response (in seconds) or fall back to config.\n // Reject non-positive values to prevent cookies with past expiry dates.\n const tokenExpiry =\n typeof data.expire_time === 'number' && isFinite(data.expire_time) && data.expire_time > 0\n ? data.expire_time * 1000\n : this.config.tokenExpiry;\n\n // If server returned token in response body (non-cookie mode),\n // store it as a client-side cookie. Key matches server's session_header.\n const tokenFromResponse = data[this.config.tokenCookieName];\n if (tokenFromResponse) {\n this.setCookie(this.config.tokenCookieName, tokenFromResponse, tokenExpiry / 1000);\n }\n\n // Now safe to update cf-session-id — new token is stored/set\n this.setCfSessionId(newSessionId, tokenExpiry / 1000);\n\n this.state.isInitialized = true;\n this.state.isLoading = false;\n this.state.lastRefreshTime = Date.now();\n this.state.nextRefreshTime = Date.now() + refreshInterval;\n this.state.tokenExpiry = tokenExpiry;\n this.state.error = null;\n\n // Start automatic refresh timer with dynamic interval\n this.startTokenRefresh(refreshInterval);\n\n this.notifyListeners();\n return data;\n } catch (error) {\n lastError = error instanceof Error ? error : new NetworkError('Unknown error occurred', { error });\n\n if (attempt < this.config.maxRetries) {\n const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n\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 return this.initializationPromise;\n }\n return Promise.resolve();\n }\n\n /**\n * Check if the token is likely expired based on in-memory timestamps.\n * After a full browser close + reopen, lastRefreshTime is null, so this returns true.\n * @returns {boolean} True if token appears expired or state is unknown\n */\n _isTokenLikelyExpired() {\n if (!this.state.lastRefreshTime || !this.state.tokenExpiry) return true;\n return Date.now() >= this.state.lastRefreshTime + this.state.tokenExpiry;\n }\n\n /**\n * Ensure the session is ready before making API calls.\n * - If initialized and token not expired, resolves immediately.\n * - If initialization is in progress, waits for it.\n * - If not initialized but configured, triggers initialization and waits.\n * - If not configured or permanently failed, resolves (lets request proceed;\n * the 401 handler in the interceptor will deal with it).\n * @returns {Promise<void>}\n */\n async ensureReady() {\n // Happy path: session is live and token hasn't expired\n if (this.state.isInitialized && !this._isTokenLikelyExpired()) {\n return;\n }\n\n // An initialization / refresh is already in flight — piggyback on it\n if (this.initializationPromise) {\n try {\n await this.initializationPromise;\n } catch (_) {\n // Initialization failed — let the request proceed, 401 handler will deal with it\n }\n return;\n }\n\n // Not initialized / token expired, no call in flight, but we *can* bootstrap\n if (this._configured && !this.state.initializationFailed) {\n try {\n await this.initialize(this.state.isInitialized); // force refresh if already initialized but token expired\n } catch (_) {\n // Initialization failed — nothing we can do, let request proceed\n }\n }\n\n // Not configured or permanently failed — nothing we can do here\n }\n\n /**\n * Check if the session needs a refresh (initialized but token expired).\n * Useful for interceptors to proactively refresh before sending a request.\n * @returns {boolean}\n */\n needsRefresh() {\n return this.state.isInitialized && this._isTokenLikelyExpired();\n }\n\n /**\n * Set cf-session-id in cookie\n * @param {string} sessionId - Session ID\n * @param {number} maxAge - Max age in seconds\n */\n setCfSessionId(sessionId, maxAge) {\n this.state.cfSessionId = sessionId;\n const cookieMaxAge = maxAge || this.config.tokenExpiry / 1000;\n this.setCookie('cf-session-id', sessionId, cookieMaxAge);\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 * Shared cookie name sanitizer used by both getCookie and setCookie\n * @param {string} name - Raw cookie name\n * @returns {string} Sanitized cookie name\n */\n _sanitizeCookieName(name) {\n return name.replace(/[^a-zA-Z0-9_-]/g, '');\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 sanitizedName = this._sanitizeCookieName(name);\n if (!sanitizedName) return null;\n const value = `; ${document.cookie}`;\n const parts = value.split(`; ${sanitizedName}=`);\n if (parts.length === 2) {\n try {\n return decodeURIComponent(parts.pop().split(';').shift());\n } catch (_) {\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') return;\n\n const sanitizedName = this._sanitizeCookieName(name);\n if (!sanitizedName) return;\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 }\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\n this.refreshTimerId = setTimeout(async () => {\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 this.state.error = errorMessage;\n this.state.errorCode = errorCode;\n this.notifyListeners();\n // Schedule retry instead of dying permanently\n const retryDelay = Math.min(interval * 2, 60000);\n this.startTokenRefresh(retryDelay);\n }\n }, interval);\n\n // Listen for visibility changes (tab wake from sleep / bfcache restore)\n this._startVisibilityListener();\n // Listen for network recovery\n this._startOnlineListener();\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 }\n this._stopVisibilityListener();\n this._stopOnlineListener();\n }\n\n /**\n * Start listening for page visibility changes and bfcache restore\n */\n _startVisibilityListener() {\n if (typeof document === 'undefined') return;\n document.addEventListener('visibilitychange', this._boundHandleVisibilityChange);\n if (typeof window !== 'undefined') {\n window.addEventListener('pageshow', this._boundHandlePageShow);\n }\n }\n\n /**\n * Stop listening for page visibility changes\n */\n _stopVisibilityListener() {\n if (typeof document === 'undefined') return;\n document.removeEventListener('visibilitychange', this._boundHandleVisibilityChange);\n if (typeof window !== 'undefined') {\n window.removeEventListener('pageshow', this._boundHandlePageShow);\n }\n }\n\n /**\n * Check if token refresh is overdue and trigger refresh if so\n * @returns {boolean} True if refresh was triggered\n */\n _refreshIfOverdue() {\n if (!this.state.isInitialized || this.state.initializationFailed) return false;\n if (this.isRefreshing()) return false;\n\n const now = Date.now();\n const { nextRefreshTime } = this.state;\n\n if (nextRefreshTime && now >= nextRefreshTime) {\n this.refreshToken().catch(() => {});\n return true;\n }\n return false;\n }\n\n /**\n * Handle visibility change — refresh if overdue when tab becomes visible\n */\n _handleVisibilityChange() {\n if (document.visibilityState !== 'visible') return;\n this._refreshIfOverdue();\n }\n\n /**\n * Handle pageshow — force bootstrap on bfcache restore (browser close + reopen)\n */\n _handlePageShow(event) {\n if (!event.persisted) return;\n // bfcache restore means browser was closed — always force a fresh bootstrap\n if (!this.state.isInitialized || this.state.initializationFailed) return;\n if (this.isRefreshing()) return;\n this.refreshToken().catch(() => {});\n }\n\n /**\n * Start listening for network recovery\n */\n _startOnlineListener() {\n if (typeof window === 'undefined') return;\n window.addEventListener('online', this._boundHandleOnline);\n }\n\n /**\n * Stop listening for network recovery\n */\n _stopOnlineListener() {\n if (typeof window === 'undefined') return;\n window.removeEventListener('online', this._boundHandleOnline);\n }\n\n /**\n * Handle online event — retry if initialization had failed due to network\n */\n _handleOnline() {\n if (!this.state.initializationFailed) return;\n this.refreshToken().catch(() => {});\n }\n\n /**\n * Manually refresh the session token\n * @returns {Promise<Object>} Bootstrap response\n */\n async refreshToken() {\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 Array.from(this.listeners).forEach(listener => {\n try {\n listener(status);\n } catch (_) {\n // Silently ignore listener errors\n }\n });\n }\n\n /**\n * Clean up and reset the session manager\n */\n destroy() {\n this.stopTokenRefresh();\n this.initializationPromise = null;\n this.listeners.clear();\n this.setCookie('cf-session-id', '', -1);\n this.setCookie(this.config.tokenCookieName, '', -1);\n this.config = { ...DEFAULT_CONFIG };\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 this._configured = false;\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;","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 // Gate on session readiness: if not initialized (e.g., browser was closed and\n // reopened), this triggers bootstrap and waits; if a refresh is in progress\n // (tab wake, online recovery, concurrent 401), it piggybacks on it.\n await sessionManager.ensureReady();\n\n // FIX #6: Shallow-clone options to avoid mutating the caller's object\n const requestOptions = {\n ...options,\n credentials: options.credentials || 'include',\n headers: {\n ...options.headers,\n 'cf-session-id': sessionManager.getSessionId(),\n 'x-client-platform': CLIENT_PLATFORM,\n },\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 requestOptions.headers[sessionManager.config.tokenCookieName] = token;\n }\n }\n\n let response = await fetch(url, requestOptions);\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 === sessionManager.config.invalidSessionError) {\n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n await sessionManager.refreshToken();\n }\n requestOptions.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const newToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (newToken) {\n requestOptions.headers[sessionManager.config.tokenCookieName] = newToken;\n }\n }\n response = await fetch(url, requestOptions);\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 * @returns {Object} The same axios instance with interceptors attached\n */\nexport function setupAxiosInterceptor(axiosInstance) {\n const sessionManager = SessionManager.getInstance();\n\n // FIX #7: Eject previous interceptors if this instance was already set up\n if (axiosInstance._gwSessionRequestId !== undefined) {\n axiosInstance.interceptors.request.eject(axiosInstance._gwSessionRequestId);\n }\n if (axiosInstance._gwSessionResponseId !== undefined) {\n axiosInstance.interceptors.response.eject(axiosInstance._gwSessionResponseId);\n }\n\n // Request interceptor to add cf-session-id, token, and credentials\n axiosInstance._gwSessionRequestId = axiosInstance.interceptors.request.use(\n async (config) => {\n // Gate on session readiness before every request\n await sessionManager.ensureReady();\n if (sessionManager.config.credentials) {\n config.withCredentials = true;\n }\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._gwSessionResponseId = 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 === sessionManager.config.invalidSessionError) {\n originalRequest._retry = true;\n\n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n await sessionManager.refreshToken();\n }\n\n originalRequest.headers['cf-session-id'] = sessionManager.getSessionId();\n 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 * Logger utility — disabled for production builds.\n * All methods are no-ops to prevent leaking internal details to the browser console.\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 setLevel() {}\n error() {}\n warn() {}\n info() {}\n debug() {}\n}\n\nexport const logger = new Logger();\nexport { LOG_LEVELS };\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport SessionManager from '../SessionManager.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 // FIX #11: Separate countdown state — only this re-renders every second,\n // not the full session state which would cause unnecessary subscriber churn\n const [timeUntilRefresh, setTimeUntilRefresh] = useState(() => {\n const { nextRefreshTime } = sessionManager.getSessionStatus();\n return nextRefreshTime ? Math.max(0, nextRefreshTime - Date.now()) : null;\n });\n\n const nextRefreshTimeRef = useRef(sessionState.nextRefreshTime);\n\n // Subscribe to meaningful session state changes only\n useEffect(() => {\n const unsubscribe = sessionManager.subscribe((newState) => {\n setSessionState(newState);\n nextRefreshTimeRef.current = newState.nextRefreshTime;\n });\n\n return unsubscribe;\n }, []); // sessionManager is a singleton, no need to include in deps\n\n // Local tick timer for countdown display — avoids global tick in SessionManager\n useEffect(() => {\n if (!sessionState.nextRefreshTime) {\n setTimeUntilRefresh(null);\n return;\n }\n // Set immediately on mount / when nextRefreshTime changes\n setTimeUntilRefresh(Math.max(0, sessionState.nextRefreshTime - Date.now()));\n const tickId = setInterval(() => {\n const nrt = nextRefreshTimeRef.current;\n setTimeUntilRefresh(nrt ? Math.max(0, nrt - Date.now()) : null);\n }, 1000);\n return () => clearInterval(tickId);\n }, [sessionState.nextRefreshTime]);\n\n // Auto-initialize if enabled\n useEffect(() => {\n if (autoInitialize && !sessionState.isInitialized && !sessionState.isLoading && !sessionState.initializationFailed) {\n sessionManager.initialize().catch(() => {});\n }\n }, [autoInitialize, sessionState.isInitialized, sessionState.isLoading, sessionState.initializationFailed]);\n\n // Manual refresh function\n const refresh = useCallback(async () => {\n await sessionManager.refreshToken();\n }, []);\n\n // Initialize function (for manual control)\n const initialize = useCallback(async () => {\n await sessionManager.initialize();\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,\n initializationFailed: sessionState.initializationFailed,\n\n // Actions\n refresh,\n initialize,\n };\n}\n\nexport default useSession;"],"names":["SessionError","Error","constructor","message","code","details","super","this","name","captureStackTrace","Object","setPrototypeOf","prototype","ConfigurationError","BootstrapError","NetworkError","SSRError","DEFAULT_CONFIG","bootstrapUrl","refreshInterval","tokenExpiry","maxRetries","bootstrapTimeout","tokenCookieName","invalidSessionError","SessionManager","instance","config","state","isInitialized","isLoading","lastRefreshTime","error","errorCode","nextRefreshTime","cfSessionId","initializationFailed","refreshTimerId","listeners","Set","initializationPromise","_configured","_boundHandleVisibilityChange","_handleVisibilityChange","bind","_boundHandlePageShow","_handlePageShow","_boundHandleOnline","_handleOnline","configure","trim","undefined","isFinite","Number","isInteger","credentials","initialize","forceRefresh","window","token","getToken","notifyListeners","lastError","attempt","newSessionId","generateSessionId","controller","AbortController","timeoutId","setTimeout","abort","response","data","fetch","method","signal","headers","fetchError","timeout","url","clearTimeout","ok","status","statusText","json","jsonError","originalError","refresh_time","expire_time","tokenFromResponse","setCookie","setCfSessionId","Date","now","startTokenRefresh","delay","Math","min","pow","Promise","resolve","isRefreshing","waitForRefresh","_isTokenLikelyExpired","ensureReady","_","needsRefresh","sessionId","maxAge","cookieMaxAge","crypto","randomUUID","random","toString","substr","getSessionId","shouldUseTokenHeader","location","protocol","_sanitizeCookieName","replace","getCookie","document","sanitizedName","parts","cookie","split","length","decodeURIComponent","pop","shift","value","expires","toUTCString","secureFlag","encodedValue","encodeURIComponent","interval","stopTokenRefresh","async","refreshToken","errorMessage","String","retryDelay","_startVisibilityListener","_startOnlineListener","_stopVisibilityListener","_stopOnlineListener","addEventListener","removeEventListener","_refreshIfOverdue","catch","visibilityState","event","persisted","getSessionStatus","timeUntilRefresh","max","subscribe","listener","TypeError","add","delete","Array","from","forEach","destroy","clear","getInstance","CLIENT_PLATFORM","logger","setLevel","warn","info","debug","NONE","ERROR","WARN","INFO","DEBUG","options","sessionManager","requestOptions","clonedResponse","clone","error_msg","newToken","e","axiosInstance","_gwSessionRequestId","interceptors","request","eject","_gwSessionResponseId","use","withCredentials","reject","originalRequest","_retry","autoInitialize","sessionState","setSessionState","useState","setTimeUntilRefresh","nextRefreshTimeRef","useRef","useEffect","newState","current","tickId","setInterval","nrt","clearInterval","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,EC7CF,MAEMS,EAAiB,CACrBC,aAAc,qBACdC,gBAAiB,KACjBC,YAAa,KACbC,WAAY,EACZC,iBAAkB,IAClBC,gBAAiB,QACjBC,oBAAqB,sBAOvB,MAAMC,EACJ,WAAAvB,GACE,GAAIuB,EAAeC,SACjB,OAAOD,EAAeC,SAGxBnB,KAAKoB,OAAS,IAAKV,GAEnBV,KAAKqB,MAAQ,CACXC,eAAe,EACfC,WAAW,EACXC,gBAAiB,KACjBC,MAAO,KACPC,UAAW,KACXC,gBAAiB,KACjBd,YAAa,KACbe,YAAa,KACbC,sBAAsB,GAGxB7B,KAAK8B,eAAiB,KACtB9B,KAAK+B,UAAY,IAAIC,IACrBhC,KAAKiC,sBAAwB,KAC7BjC,KAAKkC,aAAc,EACnBlC,KAAKmC,6BAA+BnC,KAAKoC,wBAAwBC,KAAKrC,MACtEA,KAAKsC,qBAAuBtC,KAAKuC,gBAAgBF,KAAKrC,MACtDA,KAAKwC,mBAAqBxC,KAAKyC,cAAcJ,KAAKrC,MAElDkB,EAAeC,SAAWnB,IAC5B,CAcA,SAAA0C,CAAUtB,EAAS,IACjB,IAAKA,EAAOT,cAA+C,iBAAxBS,EAAOT,eAA8BS,EAAOT,aAAagC,OAC1F,MAAM,IAAIrC,EAAmB,0DAA2D,CAAEK,aAAcS,EAAOT,eAGjH,QAA+BiC,IAA3BxB,EAAOR,gBAA+B,CACxC,GAAsC,iBAA3BQ,EAAOR,kBAAiCiC,SAASzB,EAAOR,iBACjE,MAAM,IAAIN,EAAmB,0CAA2C,CAAEM,gBAAiBQ,EAAOR,kBAEpG,GAAIQ,EAAOR,iBAAmB,EAC5B,MAAM,IAAIN,EAAmB,mCAAoC,CAAEM,gBAAiBQ,EAAOR,iBAE/F,CAEA,QAA2BgC,IAAvBxB,EAAOP,YAA2B,CACpC,GAAkC,iBAAvBO,EAAOP,cAA6BgC,SAASzB,EAAOP,aAC7D,MAAM,IAAIP,EAAmB,sCAAuC,CAAEO,YAAaO,EAAOP,cAE5F,GAAIO,EAAOP,aAAe,EACxB,MAAM,IAAIP,EAAmB,+BAAgC,CAAEO,YAAaO,EAAOP,aAEvF,CAEA,QAA0B+B,IAAtBxB,EAAON,aACwB,iBAAtBM,EAAON,aAA4BgC,OAAOC,UAAU3B,EAAON,aAAeM,EAAON,WAAa,GACvG,MAAM,IAAIR,EAAmB,wCAAyC,CAAEQ,WAAYM,EAAON,aAI/Fd,KAAKoB,OAAS,IACTpB,KAAKoB,UACLA,EACH4B,iBAAoCJ,IAAvBxB,EAAO4B,aAA4B5B,EAAO4B,aAGzDhD,KAAKkC,aAAc,CACrB,CAOA,gBAAMe,CAAWC,GAAe,GAC9B,GAAsB,oBAAXC,OACT,MAAM,IAAI1C,EAAS,sDAGrB,GAAIT,KAAKqB,MAAMQ,qBACb,MAAM,IAAItB,EAAe,kEAI3B,GAAIP,KAAKiC,sBACP,OAAOjC,KAAKiC,sBAId,GAAIjC,KAAKqB,MAAMC,gBAAkB4B,EAC/B,MAAO,CAAEE,MAAOpD,KAAKqD,SAASrD,KAAKoB,OAAOJ,kBAG5ChB,KAAKqB,MAAME,WAAY,EACvBvB,KAAKqB,MAAMI,MAAQ,KACnBzB,KAAKsD,kBAELtD,KAAKiC,sBAAwB,WAC3B,IAAIsB,EAEJ,IAAK,IAAIC,EAAU,EAAGA,GAAWxD,KAAKoB,OAAON,WAAY0C,IACvD,IACE,MAAMC,EAAezD,KAAK0D,oBAEpBC,EAAa,IAAIC,gBACjBC,EAAYC,WAAW,IAAMH,EAAWI,QAAS/D,KAAKoB,OAAOL,kBAEnE,IAAIiD,EAgCAC,EA/BJ,IACED,QAAiBE,MAAMlE,KAAKoB,OAAOT,aAAc,CAC/CwD,OAAQ,MACRnB,YAAahD,KAAKoB,OAAO4B,YAAc,UAAY,cACnDoB,OAAQT,EAAWS,OACnBC,QAAS,CACP,gBAAiBZ,EACjB,oBA/IQ,SAgJLzD,KAAKoB,OAAOiD,UAGrB,CAAE,MAAOC,GACP,GAAwB,eAApBA,EAAWrE,KACb,MAAM,IAAIO,EAAa,qCAAqCR,KAAKoB,OAAOL,qBAAsB,CAC5FwD,QAASvE,KAAKoB,OAAOL,iBACrByD,IAAKxE,KAAKoB,OAAOT,eAGrB,MAAM2D,CACR,CAAC,QACCG,aAAaZ,EACf,CAEA,IAAKG,EAASU,GACZ,MAAM,IAAInE,EAAe,qBAAqByD,EAASW,UAAUX,EAASY,aAAc,CACtFD,OAAQX,EAASW,OACjBC,WAAYZ,EAASY,WACrBJ,IAAKxE,KAAKoB,OAAOT,eAKrB,IACEsD,QAAaD,EAASa,MACxB,CAAE,MAAOC,GACP,MAAM,IAAIvE,EAAe,uCAAwC,CAAEwE,cAAeD,EAAUlF,SAC9F,CAIA,MAAMgB,EACyB,iBAAtBqD,EAAKe,cAA6BnC,SAASoB,EAAKe,eAAiBf,EAAKe,aAAe,EACpE,IAApBf,EAAKe,aACLhF,KAAKoB,OAAOR,gBAIZC,EACwB,iBAArBoD,EAAKgB,aAA4BpC,SAASoB,EAAKgB,cAAgBhB,EAAKgB,YAAc,EAClE,IAAnBhB,EAAKgB,YACLjF,KAAKoB,OAAOP,YAIZqE,EAAoBjB,EAAKjE,KAAKoB,OAAOJ,iBAmB3C,OAlBIkE,GACFlF,KAAKmF,UAAUnF,KAAKoB,OAAOJ,gBAAiBkE,EAAmBrE,EAAc,KAI/Eb,KAAKoF,eAAe3B,EAAc5C,EAAc,KAEhDb,KAAKqB,MAAMC,eAAgB,EAC3BtB,KAAKqB,MAAME,WAAY,EACvBvB,KAAKqB,MAAMG,gBAAkB6D,KAAKC,MAClCtF,KAAKqB,MAAMM,gBAAkB0D,KAAKC,MAAQ1E,EAC1CZ,KAAKqB,MAAMR,YAAcA,EACzBb,KAAKqB,MAAMI,MAAQ,KAGnBzB,KAAKuF,kBAAkB3E,GAEvBZ,KAAKsD,kBACEW,CACT,CAAE,MAAOxC,GAGP,GAFA8B,EAAY9B,aAAiB/B,MAAQ+B,EAAQ,IAAIjB,EAAa,yBAA0B,CAAEiB,UAEtF+B,EAAUxD,KAAKoB,OAAON,WAAY,CACpC,MAAM0E,EAAQC,KAAKC,IAAI,IAAOD,KAAKE,IAAI,EAAGnC,EAAU,GAAI,WAClD,IAAIoC,QAAQC,GAAW/B,WAAW+B,EAASL,GACnD,CACF,CAQF,MALAxF,KAAKqB,MAAME,WAAY,EACvBvB,KAAKqB,MAAMI,MAAQ8B,GAAW3D,SAAW,gBACzCI,KAAKqB,MAAMK,UAAY6B,GAAW1D,MAAQ,gBAC1CG,KAAKqB,MAAMQ,sBAAuB,EAClC7B,KAAKsD,kBACCC,CACP,EArG4B,GAuG7B,IACE,aAAavD,KAAKiC,qBACpB,CAAC,QACCjC,KAAKiC,sBAAwB,IAC/B,CACF,CAMA,YAAA6D,GACE,OAAsC,OAA/B9F,KAAKiC,qBACd,CAMA,oBAAM8D,GACJ,OAAI/F,KAAKiC,sBACAjC,KAAKiC,sBAEP2D,QAAQC,SACjB,CAOA,qBAAAG,GACE,OAAKhG,KAAKqB,MAAMG,kBAAoBxB,KAAKqB,MAAMR,aACxCwE,KAAKC,OAAStF,KAAKqB,MAAMG,gBAAkBxB,KAAKqB,MAAMR,WAC/D,CAWA,iBAAMoF,GAEJ,IAAIjG,KAAKqB,MAAMC,eAAkBtB,KAAKgG,wBAKtC,GAAIhG,KAAKiC,sBACP,UACQjC,KAAKiC,qBACb,CAAE,MAAOiE,GAET,MAKF,GAAIlG,KAAKkC,cAAgBlC,KAAKqB,MAAMQ,qBAClC,UACQ7B,KAAKiD,WAAWjD,KAAKqB,MAAMC,cACnC,CAAE,MAAO4E,GAET,CAIJ,CAOA,YAAAC,GACE,OAAOnG,KAAKqB,MAAMC,eAAiBtB,KAAKgG,uBAC1C,CAOA,cAAAZ,CAAegB,EAAWC,GACxBrG,KAAKqB,MAAMO,YAAcwE,EACzB,MAAME,EAAeD,GAAUrG,KAAKoB,OAAOP,YAAc,IACzDb,KAAKmF,UAAU,gBAAiBiB,EAAWE,EAC7C,CAMA,iBAAA5C,GACE,MAAsB,oBAAX6C,QAA0BA,OAAOC,WACnCD,OAAOC,aAET,GAAGnB,KAAKC,SAASG,KAAKgB,SAASC,SAAS,IAAIC,OAAO,EAAG,IAC/D,CAMA,YAAAC,GACE,OAAO5G,KAAKqB,MAAMO,WACpB,CAMA,oBAAAiF,GACE,MAAsB,oBAAX1D,QACyB,UAA7BA,OAAO2D,SAASC,QACzB,CAOA,mBAAAC,CAAoB/G,GAClB,OAAOA,EAAKgH,QAAQ,kBAAmB,GACzC,CAOA,SAAAC,CAAUjH,GACR,GAAwB,oBAAbkH,SAA0B,OAAO,KAC5C,MAAMC,EAAgBpH,KAAKgH,oBAAoB/G,GAC/C,IAAKmH,EAAe,OAAO,KAC3B,MACMC,EADQ,KAAKF,SAASG,SACRC,MAAM,KAAKH,MAC/B,GAAqB,IAAjBC,EAAMG,OACR,IACE,OAAOC,mBAAmBJ,EAAMK,MAAMH,MAAM,KAAKI,QACnD,CAAE,MAAOzB,GACP,OAAO,IACT,CAEF,OAAO,IACT,CAOA,QAAA7C,CAASpD,EAAOD,KAAKoB,OAAOJ,iBAC1B,OAAOhB,KAAKkH,UAAUjH,EACxB,CAQA,SAAAkF,CAAUlF,EAAM2H,EAAOvB,GACrB,GAAwB,oBAAbc,SAA0B,OAErC,MAAMC,EAAgBpH,KAAKgH,oBAAoB/G,GAC/C,IAAKmH,EAAe,OAEpB,MAAMS,EAAU,IAAIxC,KAAKA,KAAKC,MAAiB,IAATe,GAAeyB,cAE/CC,EAD6B,oBAAX5E,QAAuD,WAA7BA,OAAO2D,SAASC,SACpC,WAAa,GACrCiB,EAAeC,mBAAmBL,GACxCT,SAASG,OAAS,GAAGF,KAAiBY,sBAAiCH,kBAAwBE,GACjG,CAMA,iBAAAxC,CAAkB2C,EAAWlI,KAAKoB,OAAOR,iBACvCZ,KAAKmI,mBAELnI,KAAK8B,eAAiBgC,WAAWsE,UAC/B,UACQpI,KAAKqI,cACb,CAAE,MAAO5G,GACP,MAAM6G,EAAe7G,aAAiB/B,MAAQ+B,EAAM7B,QAAU2I,OAAO9G,GAC/DC,EAAYD,GAAO5B,MAAQ,gBACjCG,KAAKqB,MAAMI,MAAQ6G,EACnBtI,KAAKqB,MAAMK,UAAYA,EACvB1B,KAAKsD,kBAEL,MAAMkF,EAAa/C,KAAKC,IAAe,EAAXwC,EAAc,KAC1ClI,KAAKuF,kBAAkBiD,EACzB,GACCN,GAGHlI,KAAKyI,2BAELzI,KAAK0I,sBACP,CAKA,gBAAAP,GACMnI,KAAK8B,iBACP2C,aAAazE,KAAK8B,gBAClB9B,KAAK8B,eAAiB,MAExB9B,KAAK2I,0BACL3I,KAAK4I,qBACP,CAKA,wBAAAH,GAC0B,oBAAbtB,WACXA,SAAS0B,iBAAiB,mBAAoB7I,KAAKmC,8BAC7B,oBAAXgB,QACTA,OAAO0F,iBAAiB,WAAY7I,KAAKsC,sBAE7C,CAKA,uBAAAqG,GAC0B,oBAAbxB,WACXA,SAAS2B,oBAAoB,mBAAoB9I,KAAKmC,8BAChC,oBAAXgB,QACTA,OAAO2F,oBAAoB,WAAY9I,KAAKsC,sBAEhD,CAMA,iBAAAyG,GACE,IAAK/I,KAAKqB,MAAMC,eAAiBtB,KAAKqB,MAAMQ,qBAAsB,OAAO,EACzE,GAAI7B,KAAK8F,eAAgB,OAAO,EAEhC,MAAMR,EAAMD,KAAKC,OACX3D,gBAAEA,GAAoB3B,KAAKqB,MAEjC,SAAIM,GAAmB2D,GAAO3D,KAC5B3B,KAAKqI,eAAeW,MAAM,SACnB,EAGX,CAKA,uBAAA5G,GACmC,YAA7B+E,SAAS8B,iBACbjJ,KAAK+I,mBACP,CAKA,eAAAxG,CAAgB2G,GACTA,EAAMC,WAENnJ,KAAKqB,MAAMC,gBAAiBtB,KAAKqB,MAAMQ,uBACxC7B,KAAK8F,gBACT9F,KAAKqI,eAAeW,MAAM,QAC5B,CAKA,oBAAAN,GACwB,oBAAXvF,QACXA,OAAO0F,iBAAiB,SAAU7I,KAAKwC,mBACzC,CAKA,mBAAAoG,GACwB,oBAAXzF,QACXA,OAAO2F,oBAAoB,SAAU9I,KAAKwC,mBAC5C,CAKA,aAAAC,GACOzC,KAAKqB,MAAMQ,sBAChB7B,KAAKqI,eAAeW,MAAM,OAC5B,CAMA,kBAAMX,GAEJ,OADArI,KAAKqB,MAAMQ,sBAAuB,EAC3B7B,KAAKiD,YAAW,EACzB,CAMA,gBAAAmG,GACE,MAAO,CACL9H,cAAetB,KAAKqB,MAAMC,cAC1BC,UAAWvB,KAAKqB,MAAME,UACtBC,gBAAiBxB,KAAKqB,MAAMG,gBAC5BG,gBAAiB3B,KAAKqB,MAAMM,gBAC5Bd,YAAab,KAAKqB,MAAMR,YACxBY,MAAOzB,KAAKqB,MAAMI,MAClBC,UAAW1B,KAAKqB,MAAMK,UACtBG,qBAAsB7B,KAAKqB,MAAMQ,qBACjCwH,iBAAkBrJ,KAAKqB,MAAMM,gBACzB8D,KAAK6D,IAAI,EAAGtJ,KAAKqB,MAAMM,gBAAkB0D,KAAKC,OAC9C,KAER,CAOA,SAAAiE,CAAUC,GACR,GAAwB,mBAAbA,EACT,MAAM,IAAIC,UAAU,+BAGtB,OADAzJ,KAAK+B,UAAU2H,IAAIF,GACZ,KACLxJ,KAAK+B,UAAU4H,OAAOH,GAE1B,CAKA,eAAAlG,GACE,MAAMqB,EAAS3E,KAAKoJ,mBACpBQ,MAAMC,KAAK7J,KAAK+B,WAAW+H,QAAQN,IACjC,IACEA,EAAS7E,EACX,CAAE,MAAOuB,GAET,GAEJ,CAKA,OAAA6D,GACE/J,KAAKmI,mBACLnI,KAAKiC,sBAAwB,KAC7BjC,KAAK+B,UAAUiI,QACfhK,KAAKmF,UAAU,gBAAiB,IAAI,GACpCnF,KAAKmF,UAAUnF,KAAKoB,OAAOJ,gBAAiB,OAC5ChB,KAAKoB,OAAS,IAAKV,GACnBV,KAAKqB,MAAQ,CACXC,eAAe,EACfC,WAAW,EACXC,gBAAiB,KACjBC,MAAO,KACPC,UAAW,KACXC,gBAAiB,KACjBd,YAAa,KACbe,YAAa,KACbC,sBAAsB,GAExB7B,KAAKkC,aAAc,CACrB,CAMA,kBAAO+H,GAIL,OAHK/I,EAAeC,WAClBD,EAAeC,SAAW,IAAID,GAEzBA,EAAeC,QACxB,EC7mBU,MAAC+I,EAAkB,MCoBnB,MAACC,EAAS,IARtB,MACE,QAAAC,GAAY,CACZ,KAAA3I,GAAS,CACT,IAAA4I,GAAQ,CACR,IAAAC,GAAQ,CACR,KAAAC,GAAS,sGAbQ,CACjBC,KAAM,EACNC,MAAO,EACPC,KAAM,EACNC,KAAM,EACNC,MAAO,wIDDFxC,eAAgC5D,EAAKqG,EAAU,IACpD,MAAMC,EAAiB5J,EAAe+I,oBAKhCa,EAAe7E,cAGrB,MAAM8E,EAAiB,IAClBF,EACH7H,YAAa6H,EAAQ7H,aAAe,UACpCqB,QAAS,IACJwG,EAAQxG,QACX,gBAAiByG,EAAelE,eAChC,oBAAqBsD,IAKzB,GAAIY,EAAejE,uBAAwB,CACzC,MAAMzD,EAAQ0H,EAAezH,SAASyH,EAAe1J,OAAOJ,iBACxDoC,IACF2H,EAAe1G,QAAQyG,EAAe1J,OAAOJ,iBAAmBoC,EAEpE,CAEA,IAAIY,QAAiBE,MAAMM,EAAKuG,GAGhC,GAAwB,MAApB/G,EAASW,OAAgB,CAC3B,MAAMqG,EAAiBhH,EAASiH,QAChC,IAEE,UADmBD,EAAenG,QACzBqG,YAAcJ,EAAe1J,OAAOH,oBAAqB,CAOhE,GANI6J,EAAehF,qBACXgF,EAAe/E,uBAEf+E,EAAezC,eAEvB0C,EAAe1G,QAAQ,iBAAmByG,EAAelE,eACrDkE,EAAejE,uBAAwB,CACzC,MAAMsE,EAAWL,EAAezH,SAASyH,EAAe1J,OAAOJ,iBAC3DmK,IACFJ,EAAe1G,QAAQyG,EAAe1J,OAAOJ,iBAAmBmK,EAEpE,CACAnH,QAAiBE,MAAMM,EAAKuG,EAC9B,CACF,CAAE,MAAOK,GAET,CACF,CAEA,OAAOpH,CACT,iDAOO,SAA+BqH,GACpC,MAAMP,EAAiB5J,EAAe+I,cAgEtC,YA7D0CrH,IAAtCyI,EAAcC,qBAChBD,EAAcE,aAAaC,QAAQC,MAAMJ,EAAcC,0BAEd1I,IAAvCyI,EAAcK,sBAChBL,EAAcE,aAAavH,SAASyH,MAAMJ,EAAcK,sBAI1DL,EAAcC,oBAAsBD,EAAcE,aAAaC,QAAQG,IACrEvD,MAAOhH,IAQL,SANM0J,EAAe7E,cACjB6E,EAAe1J,OAAO4B,cACxB5B,EAAOwK,iBAAkB,GAE3BxK,EAAOiD,QAAQ,iBAAmByG,EAAelE,eACjDxF,EAAOiD,QAAQ,qBAAuB6F,EAClCY,EAAejE,uBAAwB,CACzC,MAAMzD,EAAQ0H,EAAezH,SAASyH,EAAe1J,OAAOJ,iBACxDoC,IACFhC,EAAOiD,QAAQyG,EAAe1J,OAAOJ,iBAAmBoC,EAE5D,CACA,OAAOhC,GAERK,GAAUmE,QAAQiG,OAAOpK,IAI5B4J,EAAcK,qBAAuBL,EAAcE,aAAavH,SAAS2H,IACtE3H,GAAaA,EACdoE,MAAO3G,IACL,MAAMqK,EAAkBrK,EAAML,OAG9B,GAA+B,MAA3BK,EAAMuC,UAAUW,SAAmBmH,EAAgBC,QACjDtK,EAAMuC,UAAUC,MAAMiH,YAAcJ,EAAe1J,OAAOH,oBAAqB,CAWjF,GAVA6K,EAAgBC,QAAS,EAErBjB,EAAehF,qBACXgF,EAAe/E,uBAEf+E,EAAezC,eAGvByD,EAAgBzH,QAAQ,iBAAmByG,EAAelE,eAC1DkF,EAAgBzH,QAAQ,qBAAuB6F,EAC3CY,EAAejE,uBAAwB,CACzC,MAAMsE,EAAWL,EAAezH,SAASyH,EAAe1J,OAAOJ,iBAC3DmK,IACFW,EAAgBzH,QAAQyG,EAAe1J,OAAOJ,iBAAmBmK,EAErE,CACA,OAAOE,EAAcS,EACvB,CAGF,OAAOlG,QAAQiG,OAAOpK,KAInB4J,CACT,qBE9HO,SAAoBR,EAAU,IACjC,MAAMmB,eAAEA,GAAiB,GAASnB,EAE5BC,EAAiB5J,EAAe+I,eAE/BgC,EAAcC,GAAmBC,WAAS,IAC7CrB,EAAe1B,qBAKZC,EAAkB+C,GAAuBD,EAAAA,SAAS,KACrD,MAAMxK,gBAAEA,GAAoBmJ,EAAe1B,mBAC3C,OAAOzH,EAAkB8D,KAAK6D,IAAI,EAAG3H,EAAkB0D,KAAKC,OAAS,OAGnE+G,EAAqBC,EAAAA,OAAOL,EAAatK,iBAG/C4K,EAAAA,UAAU,IACczB,EAAevB,UAAWiD,IAC1CN,EAAgBM,GAChBH,EAAmBI,QAAUD,EAAS7K,kBAI3C,IAGH4K,EAAAA,UAAU,KACN,IAAKN,EAAatK,gBAEd,YADAyK,EAAoB,MAIxBA,EAAoB3G,KAAK6D,IAAI,EAAG2C,EAAatK,gBAAkB0D,KAAKC,QACpE,MAAMoH,EAASC,YAAY,KACvB,MAAMC,EAAMP,EAAmBI,QAC/BL,EAAoBQ,EAAMnH,KAAK6D,IAAI,EAAGsD,EAAMvH,KAAKC,OAAS,OAC3D,KACH,MAAO,IAAMuH,cAAcH,IAC5B,CAACT,EAAatK,kBAGjB4K,EAAAA,UAAU,MACFP,GAAmBC,EAAa3K,eAAkB2K,EAAa1K,WAAc0K,EAAapK,sBAC1FiJ,EAAe7H,aAAa+F,MAAM,SAEvC,CAACgD,EAAgBC,EAAa3K,cAAe2K,EAAa1K,UAAW0K,EAAapK,uBAGrF,MAAMiL,EAAUC,EAAAA,YAAY3E,gBAClB0C,EAAezC,gBACtB,IAGGpF,EAAa8J,EAAAA,YAAY3E,gBACrB0C,EAAe7H,cACtB,IAEH,MAAO,CAEH3B,cAAe2K,EAAa3K,cAC5BC,UAAW0K,EAAa1K,UACxBE,MAAOwK,EAAaxK,MACpBD,gBAAiByK,EAAazK,gBAC9BG,gBAAiBsK,EAAatK,gBAC9B0H,mBACAxH,qBAAsBoK,EAAapK,qBAGnCiL,UACA7J,aAER"}
|
package/dist/index.esm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useState as e,useRef as i,useEffect as t,useCallback as s}from"react";class n extends Error{constructor(e,i,t={}){super(e),this.name="SessionError",this.code=i,this.details=t,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor),Object.setPrototypeOf(this,n.prototype)}}class r extends n{constructor(e,i){super(e,"CONFIGURATION_ERROR",i),this.name="ConfigurationError"}}class o extends n{constructor(e,i){super(e,"BOOTSTRAP_ERROR",i),this.name="BootstrapError"}}class a extends n{constructor(e,i){super(e,"NETWORK_ERROR",i),this.name="NetworkError"}}class h extends n{constructor(e,i={}){super(e,"SSR_ERROR",i),this.name="SSRError"}}const l={NONE:0,ERROR:1,WARN:2,INFO:3,DEBUG:4};const d=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)}},f={bootstrapUrl:"/session/bootstrap",refreshInterval:15e5,tokenExpiry:18e5,maxRetries:3,bootstrapTimeout:3e4,tokenCookieName:"token",invalidSessionError:"INVALID-GW-SESSION"};class c{constructor(){if(c.instance)return c.instance;this.config={...f},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,this._configured=!1,this._boundHandleVisibilityChange=this._handleVisibilityChange.bind(this),this._boundHandlePageShow=this._handlePageShow.bind(this),this._boundHandleOnline=this._handleOnline.bind(this),c.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})}if(void 0!==e.maxRetries&&("number"!=typeof e.maxRetries||!Number.isInteger(e.maxRetries)||e.maxRetries<1))throw new r("maxRetries must be a positive integer",{maxRetries:e.maxRetries});this.config={...this.config,...e,credentials:void 0===e.credentials||e.credentials,logLevel:e.logLevel||"WARN"},d.setLevel(this.config.logLevel),this._configured=!0,this.config.refreshInterval&&this.config.tokenExpiry&&this.config.refreshInterval>=this.config.tokenExpiry&&d.warn("refreshInterval should be less than tokenExpiry to prevent race conditions")}async initialize(e=!1){if("undefined"==typeof window)throw new h("Cannot initialize in non-browser environment (SSR)");if(this._configured||d.warn("initialize() called before configure(). Using default config."),this.state.initializationFailed)throw d.warn("Initialization previously failed. Reload page to retry."),new o("Initialization failed after max retries. Reload page to retry.");if(this.initializationPromise)return d.debug("Initialization already in progress, waiting..."),this.initializationPromise;if(this.state.isInitialized&&!e)return d.debug("Session already initialized"),{token:this.getToken(this.config.tokenCookieName)};this.state.isLoading=!0,this.state.error=null,this.notifyListeners(),this.initializationPromise=(async()=>{let e;for(let i=1;i<=this.config.maxRetries;i++)try{const e=this.generateSessionId();d.info(`Calling bootstrap API (attempt ${i}/${this.config.maxRetries}):`,this.config.bootstrapUrl);const t=new AbortController,s=setTimeout(()=>t.abort(),this.config.bootstrapTimeout);let n,r;try{n=await fetch(this.config.bootstrapUrl,{method:"GET",credentials:this.config.credentials?"include":"same-origin",signal:t.signal,headers:{"cf-session-id":e,"x-client-platform":"web",...this.config.headers}})}catch(e){if("AbortError"===e.name)throw new a(`Bootstrap request timed out after ${this.config.bootstrapTimeout}ms`,{timeout:this.config.bootstrapTimeout,url:this.config.bootstrapUrl});throw e}finally{clearTimeout(s)}if(!n.ok)throw new o(`Bootstrap failed: ${n.status} ${n.statusText}`,{status:n.status,statusText:n.statusText,url:this.config.bootstrapUrl});try{r=await n.json()}catch(e){throw new o("Bootstrap response is not valid JSON",{originalError:e.message})}d.info("Bootstrap successful");const h=r.refresh_time?1e3*r.refresh_time:this.config.refreshInterval,l=r.expire_time?1e3*r.expire_time:this.config.tokenExpiry,f=r[this.config.tokenCookieName];return f&&this.setCookie(this.config.tokenCookieName,f,l/1e3),this.setCfSessionId(e,l/1e3),this.state.isInitialized=!0,this.state.isLoading=!1,this.state.lastRefreshTime=Date.now(),this.state.nextRefreshTime=Date.now()+h,this.state.tokenExpiry=l,this.state.error=null,this.startTokenRefresh(h),this.notifyListeners(),r}catch(t){if(e=t instanceof Error?t:new a("Unknown error occurred",{error:t}),d.error(`Bootstrap attempt ${i} failed:`,e.message),i<this.config.maxRetries){const e=Math.min(1e3*Math.pow(2,i-1),5e3);d.info(`Retrying in ${e}ms...`),await new Promise(i=>setTimeout(i,e))}}throw d.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?(d.debug("Waiting for ongoing refresh to complete"),this.initializationPromise):Promise.resolve()}_isTokenLikelyExpired(){return!this.state.lastRefreshTime||!this.state.tokenExpiry||Date.now()>=this.state.lastRefreshTime+this.state.tokenExpiry}async ensureReady(){if(!this.state.isInitialized||this._isTokenLikelyExpired())if(this.initializationPromise)try{await this.initializationPromise}catch(e){d.debug("ensureReady: in-flight initialization failed, proceeding with request")}else if(this._configured&&!this.state.initializationFailed)try{await this.initialize(this.state.isInitialized)}catch(e){d.debug("ensureReady: initialization failed, proceeding with request")}}needsRefresh(){return this.state.isInitialized&&this._isTokenLikelyExpired()}setCfSessionId(e,i){this.state.cfSessionId=e;const t=i||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}_sanitizeCookieName(e){return e.replace(/[^a-zA-Z0-9_-]/g,"")}getCookie(e){if("undefined"==typeof document)return null;const i=this._sanitizeCookieName(e);if(!i)return null;const t=`; ${document.cookie}`.split(`; ${i}=`);if(2===t.length)try{return decodeURIComponent(t.pop().split(";").shift())}catch(e){return d.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,i,t){if("undefined"==typeof document)return void d.warn("Cannot set cookie in non-browser environment");const s=this._sanitizeCookieName(e);if(!s)return void d.error("Invalid cookie name provided");const n=new Date(Date.now()+1e3*t).toUTCString(),r="undefined"!=typeof window&&"https:"===window.location.protocol,o=r?"; Secure":"",a=encodeURIComponent(i);document.cookie=`${s}=${a}; path=/; expires=${n}; SameSite=Lax${o}`,d.debug(`Cookie set: ${s}, expires in ${t}s${r?" (Secure)":""} [WARNING: Not HttpOnly]`)}startTokenRefresh(e=this.config.refreshInterval){this.stopTokenRefresh(),d.info(`Scheduling token refresh in ${e/1e3} seconds`),this.refreshTimerId=setTimeout(async()=>{d.info("Auto-refreshing token...");try{await this.refreshToken()}catch(i){const t=i instanceof Error?i.message:String(i),s=i?.code||"UNKNOWN_ERROR";d.error("Auto-refresh failed:",t),this.state.error=t,this.state.errorCode=s,this.notifyListeners();const n=Math.min(2*e,6e4);d.info(`Scheduling auto-refresh retry in ${n/1e3}s`),this.startTokenRefresh(n)}},e),this._startVisibilityListener(),this._startOnlineListener()}stopTokenRefresh(){this.refreshTimerId&&(clearTimeout(this.refreshTimerId),this.refreshTimerId=null,d.debug("Token refresh timer stopped")),this._stopVisibilityListener(),this._stopOnlineListener()}_startVisibilityListener(){"undefined"!=typeof document&&(document.addEventListener("visibilitychange",this._boundHandleVisibilityChange),"undefined"!=typeof window&&window.addEventListener("pageshow",this._boundHandlePageShow))}_stopVisibilityListener(){"undefined"!=typeof document&&(document.removeEventListener("visibilitychange",this._boundHandleVisibilityChange),"undefined"!=typeof window&&window.removeEventListener("pageshow",this._boundHandlePageShow))}_refreshIfOverdue(){if(!this.state.isInitialized||this.state.initializationFailed)return!1;if(this.isRefreshing())return!1;const e=Date.now(),{nextRefreshTime:i}=this.state;return!!(i&&e>=i)&&(d.info("Missed scheduled refresh — refreshing token now"),this.refreshToken().catch(e=>{const i=e instanceof Error?e.message:String(e);d.error("Triggered refresh failed:",i)}),!0)}_handleVisibilityChange(){"visible"===document.visibilityState&&this._refreshIfOverdue()}_handlePageShow(e){e.persisted&&this.state.isInitialized&&!this.state.initializationFailed&&(this.isRefreshing()||(d.info("Page restored from bfcache — forcing bootstrap"),this.refreshToken().catch(e=>{const i=e instanceof Error?e.message:String(e);d.error("bfcache-triggered refresh failed:",i)})))}_startOnlineListener(){"undefined"!=typeof window&&window.addEventListener("online",this._boundHandleOnline)}_stopOnlineListener(){"undefined"!=typeof window&&window.removeEventListener("online",this._boundHandleOnline)}_handleOnline(){this.state.initializationFailed&&(d.info("Network restored — retrying session initialization"),this.refreshToken().catch(e=>{const i=e instanceof Error?e.message:String(e);d.error("Online-triggered refresh failed:",i)}))}async refreshToken(){return d.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(i=>{try{i(e)}catch(e){const i=e instanceof Error?e.message:String(e);d.error("Listener error:",i)}})}destroy(){this.stopTokenRefresh(),this.initializationPromise=null,this.listeners.clear(),this.setCookie("cf-session-id","",-1),this.setCookie(this.config.tokenCookieName,"",-1),this.config={...f},this.state={isInitialized:!1,isLoading:!1,lastRefreshTime:null,error:null,errorCode:null,nextRefreshTime:null,tokenExpiry:null,cfSessionId:null,initializationFailed:!1},this._configured=!1,d.info("Destroyed")}static getInstance(){return c.instance||(c.instance=new c),c.instance}}function u(n={}){const{autoInitialize:r=!0}=n,o=c.getInstance(),[a,h]=e(()=>o.getSessionStatus()),[l,f]=e(()=>{const{nextRefreshTime:e}=o.getSessionStatus();return e?Math.max(0,e-Date.now()):null}),u=i(a.nextRefreshTime);t(()=>o.subscribe(e=>{h(e),u.current=e.nextRefreshTime}),[]),t(()=>{if(!a.nextRefreshTime)return void f(null);f(Math.max(0,a.nextRefreshTime-Date.now()));const e=setInterval(()=>{const e=u.current;f(e?Math.max(0,e-Date.now()):null)},1e3);return()=>clearInterval(e)},[a.nextRefreshTime]),t(()=>{!r||a.isInitialized||a.isLoading||a.initializationFailed||o.initialize().catch(e=>{d.error("Auto-initialization failed:",e)})},[r,a.isInitialized,a.isLoading,a.initializationFailed]);const g=s(async()=>{try{await o.refreshToken()}catch(e){throw d.error("Manual refresh failed:",e),e}},[]),m=s(async()=>{try{await o.initialize()}catch(e){throw d.error("Manual initialization failed:",e),e}},[]);return{isInitialized:a.isInitialized,isLoading:a.isLoading,error:a.error,lastRefreshTime:a.lastRefreshTime,nextRefreshTime:a.nextRefreshTime,timeUntilRefresh:l,initializationFailed:a.initializationFailed,refresh:g,initialize:m}}const g="web";async function m(e,i={}){const t=c.getInstance();await t.ensureReady();const s={...i,credentials:i.credentials||"include",headers:{...i.headers,"cf-session-id":t.getSessionId(),"x-client-platform":g}};if(t.shouldUseTokenHeader()){const e=t.getToken(t.config.tokenCookieName);e&&(s.headers[t.config.tokenCookieName]=e)}let n=await fetch(e,s);if(401===n.status){const i=n.clone();try{if((await i.json()).error_msg===t.config.invalidSessionError){if(t.isRefreshing()?await t.waitForRefresh():await t.refreshToken(),s.headers["cf-session-id"]=t.getSessionId(),t.shouldUseTokenHeader()){const e=t.getToken(t.config.tokenCookieName);e&&(s.headers[t.config.tokenCookieName]=e)}n=await fetch(e,s)}}catch(e){}}return n}function p(e){const i=c.getInstance();return void 0!==e._gwSessionRequestId&&e.interceptors.request.eject(e._gwSessionRequestId),void 0!==e._gwSessionResponseId&&e.interceptors.response.eject(e._gwSessionResponseId),e._gwSessionRequestId=e.interceptors.request.use(async e=>{if(await i.ensureReady(),i.config.credentials&&(e.withCredentials=!0),e.headers["cf-session-id"]=i.getSessionId(),e.headers["x-client-platform"]=g,i.shouldUseTokenHeader()){const t=i.getToken(i.config.tokenCookieName);t&&(e.headers[i.config.tokenCookieName]=t)}return e},e=>Promise.reject(e)),e._gwSessionResponseId=e.interceptors.response.use(e=>e,async t=>{const s=t.config;if(401===t.response?.status&&!s._retry&&t.response?.data?.error_msg===i.config.invalidSessionError){if(s._retry=!0,i.isRefreshing()?await i.waitForRefresh():await i.refreshToken(),s.headers["cf-session-id"]=i.getSessionId(),s.headers["x-client-platform"]=g,i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(s.headers[i.config.tokenCookieName]=e)}return e(s)}return Promise.reject(t)}),e}export{o as BootstrapError,g as CLIENT_PLATFORM,r as ConfigurationError,l as LOG_LEVELS,a as NetworkError,h as SSRError,n as SessionError,c as SessionManager,c as default,m as fetchInterceptor,d as logger,p as setupAxiosInterceptor,u as useSession};
|
|
1
|
+
import{useState as e,useRef as t,useEffect as i,useCallback as s}from"react";class n 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,n.prototype)}}class r extends n{constructor(e,t){super(e,"CONFIGURATION_ERROR",t),this.name="ConfigurationError"}}class o extends n{constructor(e,t){super(e,"BOOTSTRAP_ERROR",t),this.name="BootstrapError"}}class a extends n{constructor(e,t){super(e,"NETWORK_ERROR",t),this.name="NetworkError"}}class h extends n{constructor(e,t={}){super(e,"SSR_ERROR",t),this.name="SSRError"}}const l={bootstrapUrl:"/session/bootstrap",refreshInterval:15e5,tokenExpiry:18e5,maxRetries:3,bootstrapTimeout:3e4,tokenCookieName:"token",invalidSessionError:"INVALID-GW-SESSION"};class d{constructor(){if(d.instance)return d.instance;this.config={...l},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,this._configured=!1,this._boundHandleVisibilityChange=this._handleVisibilityChange.bind(this),this._boundHandlePageShow=this._handlePageShow.bind(this),this._boundHandleOnline=this._handleOnline.bind(this),d.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})}if(void 0!==e.maxRetries&&("number"!=typeof e.maxRetries||!Number.isInteger(e.maxRetries)||e.maxRetries<1))throw new r("maxRetries must be a positive integer",{maxRetries:e.maxRetries});this.config={...this.config,...e,credentials:void 0===e.credentials||e.credentials},this._configured=!0}async initialize(e=!1){if("undefined"==typeof window)throw new h("Cannot initialize in non-browser environment (SSR)");if(this.state.initializationFailed)throw new o("Initialization failed after max retries. Reload page to retry.");if(this.initializationPromise)return this.initializationPromise;if(this.state.isInitialized&&!e)return{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(),t=new AbortController,i=setTimeout(()=>t.abort(),this.config.bootstrapTimeout);let s,n;try{s=await fetch(this.config.bootstrapUrl,{method:"GET",credentials:this.config.credentials?"include":"same-origin",signal:t.signal,headers:{"cf-session-id":e,"x-client-platform":"web",...this.config.headers}})}catch(e){if("AbortError"===e.name)throw new a(`Bootstrap request timed out after ${this.config.bootstrapTimeout}ms`,{timeout:this.config.bootstrapTimeout,url:this.config.bootstrapUrl});throw e}finally{clearTimeout(i)}if(!s.ok)throw new o(`Bootstrap failed: ${s.status} ${s.statusText}`,{status:s.status,statusText:s.statusText,url:this.config.bootstrapUrl});try{n=await s.json()}catch(e){throw new o("Bootstrap response is not valid JSON",{originalError:e.message})}const r="number"==typeof n.refresh_time&&isFinite(n.refresh_time)&&n.refresh_time>0?1e3*n.refresh_time:this.config.refreshInterval,h="number"==typeof n.expire_time&&isFinite(n.expire_time)&&n.expire_time>0?1e3*n.expire_time:this.config.tokenExpiry,l=n[this.config.tokenCookieName];return l&&this.setCookie(this.config.tokenCookieName,l,h/1e3),this.setCfSessionId(e,h/1e3),this.state.isInitialized=!0,this.state.isLoading=!1,this.state.lastRefreshTime=Date.now(),this.state.nextRefreshTime=Date.now()+r,this.state.tokenExpiry=h,this.state.error=null,this.startTokenRefresh(r),this.notifyListeners(),n}catch(i){if(e=i instanceof Error?i:new a("Unknown error occurred",{error:i}),t<this.config.maxRetries){const e=Math.min(1e3*Math.pow(2,t-1),5e3);await new Promise(t=>setTimeout(t,e))}}throw 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?this.initializationPromise:Promise.resolve()}_isTokenLikelyExpired(){return!this.state.lastRefreshTime||!this.state.tokenExpiry||Date.now()>=this.state.lastRefreshTime+this.state.tokenExpiry}async ensureReady(){if(!this.state.isInitialized||this._isTokenLikelyExpired())if(this.initializationPromise)try{await this.initializationPromise}catch(e){}else if(this._configured&&!this.state.initializationFailed)try{await this.initialize(this.state.isInitialized)}catch(e){}}needsRefresh(){return this.state.isInitialized&&this._isTokenLikelyExpired()}setCfSessionId(e,t){this.state.cfSessionId=e;const i=t||this.config.tokenExpiry/1e3;this.setCookie("cf-session-id",e,i)}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}_sanitizeCookieName(e){return e.replace(/[^a-zA-Z0-9_-]/g,"")}getCookie(e){if("undefined"==typeof document)return null;const t=this._sanitizeCookieName(e);if(!t)return null;const i=`; ${document.cookie}`.split(`; ${t}=`);if(2===i.length)try{return decodeURIComponent(i.pop().split(";").shift())}catch(e){return null}return null}getToken(e=this.config.tokenCookieName){return this.getCookie(e)}setCookie(e,t,i){if("undefined"==typeof document)return;const s=this._sanitizeCookieName(e);if(!s)return;const n=new Date(Date.now()+1e3*i).toUTCString(),r="undefined"!=typeof window&&"https:"===window.location.protocol?"; Secure":"",o=encodeURIComponent(t);document.cookie=`${s}=${o}; path=/; expires=${n}; SameSite=Lax${r}`}startTokenRefresh(e=this.config.refreshInterval){this.stopTokenRefresh(),this.refreshTimerId=setTimeout(async()=>{try{await this.refreshToken()}catch(t){const i=t instanceof Error?t.message:String(t),s=t?.code||"UNKNOWN_ERROR";this.state.error=i,this.state.errorCode=s,this.notifyListeners();const n=Math.min(2*e,6e4);this.startTokenRefresh(n)}},e),this._startVisibilityListener(),this._startOnlineListener()}stopTokenRefresh(){this.refreshTimerId&&(clearTimeout(this.refreshTimerId),this.refreshTimerId=null),this._stopVisibilityListener(),this._stopOnlineListener()}_startVisibilityListener(){"undefined"!=typeof document&&(document.addEventListener("visibilitychange",this._boundHandleVisibilityChange),"undefined"!=typeof window&&window.addEventListener("pageshow",this._boundHandlePageShow))}_stopVisibilityListener(){"undefined"!=typeof document&&(document.removeEventListener("visibilitychange",this._boundHandleVisibilityChange),"undefined"!=typeof window&&window.removeEventListener("pageshow",this._boundHandlePageShow))}_refreshIfOverdue(){if(!this.state.isInitialized||this.state.initializationFailed)return!1;if(this.isRefreshing())return!1;const e=Date.now(),{nextRefreshTime:t}=this.state;return!!(t&&e>=t)&&(this.refreshToken().catch(()=>{}),!0)}_handleVisibilityChange(){"visible"===document.visibilityState&&this._refreshIfOverdue()}_handlePageShow(e){e.persisted&&this.state.isInitialized&&!this.state.initializationFailed&&(this.isRefreshing()||this.refreshToken().catch(()=>{}))}_startOnlineListener(){"undefined"!=typeof window&&window.addEventListener("online",this._boundHandleOnline)}_stopOnlineListener(){"undefined"!=typeof window&&window.removeEventListener("online",this._boundHandleOnline)}_handleOnline(){this.state.initializationFailed&&this.refreshToken().catch(()=>{})}async refreshToken(){return 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){}})}destroy(){this.stopTokenRefresh(),this.initializationPromise=null,this.listeners.clear(),this.setCookie("cf-session-id","",-1),this.setCookie(this.config.tokenCookieName,"",-1),this.config={...l},this.state={isInitialized:!1,isLoading:!1,lastRefreshTime:null,error:null,errorCode:null,nextRefreshTime:null,tokenExpiry:null,cfSessionId:null,initializationFailed:!1},this._configured=!1}static getInstance(){return d.instance||(d.instance=new d),d.instance}}function f(n={}){const{autoInitialize:r=!0}=n,o=d.getInstance(),[a,h]=e(()=>o.getSessionStatus()),[l,f]=e(()=>{const{nextRefreshTime:e}=o.getSessionStatus();return e?Math.max(0,e-Date.now()):null}),c=t(a.nextRefreshTime);i(()=>o.subscribe(e=>{h(e),c.current=e.nextRefreshTime}),[]),i(()=>{if(!a.nextRefreshTime)return void f(null);f(Math.max(0,a.nextRefreshTime-Date.now()));const e=setInterval(()=>{const e=c.current;f(e?Math.max(0,e-Date.now()):null)},1e3);return()=>clearInterval(e)},[a.nextRefreshTime]),i(()=>{!r||a.isInitialized||a.isLoading||a.initializationFailed||o.initialize().catch(()=>{})},[r,a.isInitialized,a.isLoading,a.initializationFailed]);const u=s(async()=>{await o.refreshToken()},[]),m=s(async()=>{await o.initialize()},[]);return{isInitialized:a.isInitialized,isLoading:a.isLoading,error:a.error,lastRefreshTime:a.lastRefreshTime,nextRefreshTime:a.nextRefreshTime,timeUntilRefresh:l,initializationFailed:a.initializationFailed,refresh:u,initialize:m}}const c="web";async function u(e,t={}){const i=d.getInstance();await i.ensureReady();const s={...t,credentials:t.credentials||"include",headers:{...t.headers,"cf-session-id":i.getSessionId(),"x-client-platform":c}};if(i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(s.headers[i.config.tokenCookieName]=e)}let n=await fetch(e,s);if(401===n.status){const t=n.clone();try{if((await t.json()).error_msg===i.config.invalidSessionError){if(i.isRefreshing()?await i.waitForRefresh():await i.refreshToken(),s.headers["cf-session-id"]=i.getSessionId(),i.shouldUseTokenHeader()){const e=i.getToken(i.config.tokenCookieName);e&&(s.headers[i.config.tokenCookieName]=e)}n=await fetch(e,s)}}catch(e){}}return n}function m(e){const t=d.getInstance();return void 0!==e._gwSessionRequestId&&e.interceptors.request.eject(e._gwSessionRequestId),void 0!==e._gwSessionResponseId&&e.interceptors.response.eject(e._gwSessionResponseId),e._gwSessionRequestId=e.interceptors.request.use(async e=>{if(await t.ensureReady(),t.config.credentials&&(e.withCredentials=!0),e.headers["cf-session-id"]=t.getSessionId(),e.headers["x-client-platform"]=c,t.shouldUseTokenHeader()){const i=t.getToken(t.config.tokenCookieName);i&&(e.headers[t.config.tokenCookieName]=i)}return e},e=>Promise.reject(e)),e._gwSessionResponseId=e.interceptors.response.use(e=>e,async i=>{const s=i.config;if(401===i.response?.status&&!s._retry&&i.response?.data?.error_msg===t.config.invalidSessionError){if(s._retry=!0,t.isRefreshing()?await t.waitForRefresh():await t.refreshToken(),s.headers["cf-session-id"]=t.getSessionId(),s.headers["x-client-platform"]=c,t.shouldUseTokenHeader()){const e=t.getToken(t.config.tokenCookieName);e&&(s.headers[t.config.tokenCookieName]=e)}return e(s)}return Promise.reject(i)}),e}const p={NONE:0,ERROR:1,WARN:2,INFO:3,DEBUG:4};const g=new class{setLevel(){}error(){}warn(){}info(){}debug(){}};export{o as BootstrapError,c as CLIENT_PLATFORM,r as ConfigurationError,p as LOG_LEVELS,a as NetworkError,h as SSRError,n as SessionError,d as SessionManager,d as default,u as fetchInterceptor,g as logger,m as setupAxiosInterceptor,f 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';\nconst CLIENT_PLATFORM = 'web';\n\nconst DEFAULT_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 bootstrapTimeout: 30000, // 30 seconds timeout for bootstrap fetch\n tokenCookieName: 'token', // Must match server's session_header\n invalidSessionError: 'INVALID-GW-SESSION', // Must match server's error response\n};\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 = { ...DEFAULT_CONFIG };\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 this._configured = false;\n this._boundHandleVisibilityChange = this._handleVisibilityChange.bind(this);\n this._boundHandlePageShow = this._handlePageShow.bind(this);\n this._boundHandleOnline = this._handleOnline.bind(this);\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 {number} config.bootstrapTimeout - Timeout for bootstrap fetch in milliseconds (default: 30s)\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 if (config.maxRetries !== undefined) {\n if (typeof config.maxRetries !== 'number' || !Number.isInteger(config.maxRetries) || config.maxRetries < 1) {\n throw new ConfigurationError('maxRetries must be a positive integer', { maxRetries: config.maxRetries });\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 this._configured = true;\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} forceRefresh - Force a new bootstrap call even if already initialized (used by refreshToken)\n * @returns {Promise<Object>} Bootstrap response\n */\n async initialize(forceRefresh = false) {\n if (typeof window === 'undefined') {\n throw new SSRError('Cannot initialize in non-browser environment (SSR)');\n }\n\n if (!this._configured) {\n logger.warn('initialize() called before configure(). Using default config.');\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 // If already initialized in this page session and not a forced refresh, return current state\n if (this.state.isInitialized && !forceRefresh) {\n logger.debug('Session already initialized');\n return { token: this.getToken(this.config.tokenCookieName) };\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 const newSessionId = this.generateSessionId();\n\n logger.info(`Calling bootstrap API (attempt ${attempt}/${this.config.maxRetries}):`, this.config.bootstrapUrl);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.bootstrapTimeout);\n\n let response;\n try {\n response = await fetch(this.config.bootstrapUrl, {\n method: 'GET',\n credentials: this.config.credentials ? 'include' : 'same-origin',\n signal: controller.signal,\n headers: {\n 'cf-session-id': newSessionId,\n 'x-client-platform': CLIENT_PLATFORM,\n ...this.config.headers,\n },\n });\n } catch (fetchError) {\n if (fetchError.name === 'AbortError') {\n throw new NetworkError(`Bootstrap request timed out after ${this.config.bootstrapTimeout}ms`, {\n timeout: this.config.bootstrapTimeout,\n url: this.config.bootstrapUrl\n });\n }\n throw fetchError;\n } finally {\n clearTimeout(timeoutId);\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 // 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 from response (in seconds) or fall back to config\n const tokenExpiry = data.expire_time\n ? data.expire_time * 1000\n : this.config.tokenExpiry;\n\n // If server returned token in response body (non-cookie mode),\n // store it as a client-side cookie. Key matches server's session_header.\n const tokenFromResponse = data[this.config.tokenCookieName];\n if (tokenFromResponse) {\n this.setCookie(this.config.tokenCookieName, tokenFromResponse, tokenExpiry / 1000);\n }\n\n // Now safe to update cf-session-id — new token is stored/set\n this.setCfSessionId(newSessionId, tokenExpiry / 1000);\n\n this.state.isInitialized = true;\n this.state.isLoading = false;\n this.state.lastRefreshTime = Date.now();\n this.state.nextRefreshTime = Date.now() + refreshInterval;\n this.state.tokenExpiry = tokenExpiry;\n this.state.error = null;\n\n // Start automatic refresh timer with dynamic interval\n this.startTokenRefresh(refreshInterval);\n\n this.notifyListeners();\n return data;\n } catch (error) {\n lastError = error instanceof Error ? error : new NetworkError('Unknown error occurred', { error });\n logger.error(`Bootstrap attempt ${attempt} failed:`, lastError.message);\n\n if (attempt < this.config.maxRetries) {\n const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);\n logger.info(`Retrying in ${delay}ms...`);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n\n logger.error('All bootstrap attempts failed');\n this.state.isLoading = false;\n this.state.error = lastError?.message || 'Unknown error';\n this.state.errorCode = lastError?.code || 'UNKNOWN_ERROR';\n this.state.initializationFailed = true;\n this.notifyListeners();\n throw lastError;\n })();\n\n try {\n return await this.initializationPromise;\n } finally {\n this.initializationPromise = null;\n }\n }\n\n /**\n * Check if token refresh is currently in progress\n * @returns {boolean} True if refresh is in progress\n */\n isRefreshing() {\n return this.initializationPromise !== null;\n }\n\n /**\n * Wait for ongoing refresh to complete\n * @returns {Promise<Object>} Bootstrap response from ongoing refresh\n */\n async waitForRefresh() {\n if (this.initializationPromise) {\n logger.debug('Waiting for ongoing refresh to complete');\n return this.initializationPromise;\n }\n return Promise.resolve();\n }\n\n /**\n * Check if the token is likely expired based on in-memory timestamps.\n * After a full browser close + reopen, lastRefreshTime is null, so this returns true.\n * @returns {boolean} True if token appears expired or state is unknown\n */\n _isTokenLikelyExpired() {\n if (!this.state.lastRefreshTime || !this.state.tokenExpiry) return true;\n return Date.now() >= this.state.lastRefreshTime + this.state.tokenExpiry;\n }\n\n /**\n * Ensure the session is ready before making API calls.\n * - If initialized and token not expired, resolves immediately.\n * - If initialization is in progress, waits for it.\n * - If not initialized but configured, triggers initialization and waits.\n * - If not configured or permanently failed, resolves (lets request proceed;\n * the 401 handler in the interceptor will deal with it).\n * @returns {Promise<void>}\n */\n async ensureReady() {\n // Happy path: session is live and token hasn't expired\n if (this.state.isInitialized && !this._isTokenLikelyExpired()) {\n return;\n }\n\n // An initialization / refresh is already in flight — piggyback on it\n if (this.initializationPromise) {\n try {\n await this.initializationPromise;\n } catch (_) {\n // Initialization failed — let the request proceed, 401 handler will deal with it\n logger.debug('ensureReady: in-flight initialization failed, proceeding with request');\n }\n return;\n }\n\n // Not initialized / token expired, no call in flight, but we *can* bootstrap\n if (this._configured && !this.state.initializationFailed) {\n try {\n await this.initialize(this.state.isInitialized); // force refresh if already initialized but token expired\n } catch (_) {\n logger.debug('ensureReady: initialization failed, proceeding with request');\n }\n }\n\n // Not configured or permanently failed — nothing we can do here\n }\n\n /**\n * Check if the session needs a refresh (initialized but token expired).\n * Useful for interceptors to proactively refresh before sending a request.\n * @returns {boolean}\n */\n needsRefresh() {\n return this.state.isInitialized && this._isTokenLikelyExpired();\n }\n\n /**\n * Set cf-session-id in cookie\n * @param {string} sessionId - Session ID\n * @param {number} maxAge - Max age in seconds\n */\n setCfSessionId(sessionId, maxAge) {\n this.state.cfSessionId = sessionId;\n const cookieMaxAge = maxAge || this.config.tokenExpiry / 1000;\n this.setCookie('cf-session-id', sessionId, cookieMaxAge);\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 * Shared cookie name sanitizer used by both getCookie and setCookie\n * @param {string} name - Raw cookie name\n * @returns {string} Sanitized cookie name\n */\n _sanitizeCookieName(name) {\n return name.replace(/[^a-zA-Z0-9_-]/g, '');\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 sanitizedName = this._sanitizeCookieName(name);\n if (!sanitizedName) return null;\n const value = `; ${document.cookie}`;\n const parts = value.split(`; ${sanitizedName}=`);\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 const sanitizedName = this._sanitizeCookieName(name);\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 // Schedule retry instead of dying permanently\n const retryDelay = Math.min(interval * 2, 60000);\n logger.info(`Scheduling auto-refresh retry in ${retryDelay / 1000}s`);\n this.startTokenRefresh(retryDelay);\n }\n }, interval);\n\n // Listen for visibility changes (tab wake from sleep / bfcache restore)\n this._startVisibilityListener();\n // Listen for network recovery\n this._startOnlineListener();\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 this._stopVisibilityListener();\n this._stopOnlineListener();\n }\n\n /**\n * Start listening for page visibility changes and bfcache restore\n */\n _startVisibilityListener() {\n if (typeof document === 'undefined') return;\n document.addEventListener('visibilitychange', this._boundHandleVisibilityChange);\n if (typeof window !== 'undefined') {\n window.addEventListener('pageshow', this._boundHandlePageShow);\n }\n }\n\n /**\n * Stop listening for page visibility changes\n */\n _stopVisibilityListener() {\n if (typeof document === 'undefined') return;\n document.removeEventListener('visibilitychange', this._boundHandleVisibilityChange);\n if (typeof window !== 'undefined') {\n window.removeEventListener('pageshow', this._boundHandlePageShow);\n }\n }\n\n /**\n * Check if token refresh is overdue and trigger refresh if so\n * @returns {boolean} True if refresh was triggered\n */\n _refreshIfOverdue() {\n if (!this.state.isInitialized || this.state.initializationFailed) return false;\n if (this.isRefreshing()) return false;\n\n const now = Date.now();\n const { nextRefreshTime } = this.state;\n\n if (nextRefreshTime && now >= nextRefreshTime) {\n logger.info('Missed scheduled refresh — refreshing token now');\n this.refreshToken().catch(error => {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('Triggered refresh failed:', errorMessage);\n });\n return true;\n }\n return false;\n }\n\n /**\n * Handle visibility change — refresh if overdue when tab becomes visible\n */\n _handleVisibilityChange() {\n if (document.visibilityState !== 'visible') return;\n this._refreshIfOverdue();\n }\n\n /**\n * Handle pageshow — force bootstrap on bfcache restore (browser close + reopen)\n */\n _handlePageShow(event) {\n if (!event.persisted) return;\n // bfcache restore means browser was closed — always force a fresh bootstrap\n if (!this.state.isInitialized || this.state.initializationFailed) return;\n if (this.isRefreshing()) return;\n logger.info('Page restored from bfcache — forcing bootstrap');\n this.refreshToken().catch(error => {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('bfcache-triggered refresh failed:', errorMessage);\n });\n }\n\n /**\n * Start listening for network recovery\n */\n _startOnlineListener() {\n if (typeof window === 'undefined') return;\n window.addEventListener('online', this._boundHandleOnline);\n }\n\n /**\n * Stop listening for network recovery\n */\n _stopOnlineListener() {\n if (typeof window === 'undefined') return;\n window.removeEventListener('online', this._boundHandleOnline);\n }\n\n /**\n * Handle online event — retry if initialization had failed due to network\n */\n _handleOnline() {\n if (!this.state.initializationFailed) return;\n logger.info('Network restored — retrying session initialization');\n this.refreshToken().catch(error => {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error('Online-triggered refresh failed:', errorMessage);\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 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.initializationPromise = null;\n this.listeners.clear();\n this.setCookie('cf-session-id', '', -1);\n this.setCookie(this.config.tokenCookieName, '', -1);\n this.config = { ...DEFAULT_CONFIG };\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 this._configured = false;\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;","import { useState, useEffect, useCallback, useRef } 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 // FIX #11: Separate countdown state — only this re-renders every second,\n // not the full session state which would cause unnecessary subscriber churn\n const [timeUntilRefresh, setTimeUntilRefresh] = useState(() => {\n const { nextRefreshTime } = sessionManager.getSessionStatus();\n return nextRefreshTime ? Math.max(0, nextRefreshTime - Date.now()) : null;\n });\n\n const nextRefreshTimeRef = useRef(sessionState.nextRefreshTime);\n\n // Subscribe to meaningful session state changes only\n useEffect(() => {\n const unsubscribe = sessionManager.subscribe((newState) => {\n setSessionState(newState);\n nextRefreshTimeRef.current = newState.nextRefreshTime;\n });\n\n return unsubscribe;\n }, []); // sessionManager is a singleton, no need to include in deps\n\n // Local tick timer for countdown display — avoids global tick in SessionManager\n useEffect(() => {\n if (!sessionState.nextRefreshTime) {\n setTimeUntilRefresh(null);\n return;\n }\n // Set immediately on mount / when nextRefreshTime changes\n setTimeUntilRefresh(Math.max(0, sessionState.nextRefreshTime - Date.now()));\n const tickId = setInterval(() => {\n const nrt = nextRefreshTimeRef.current;\n setTimeUntilRefresh(nrt ? Math.max(0, nrt - Date.now()) : null);\n }, 1000);\n return () => clearInterval(tickId);\n }, [sessionState.nextRefreshTime]);\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,\n initializationFailed: sessionState.initializationFailed,\n\n // Actions\n refresh,\n initialize,\n };\n}\n\nexport default useSession;","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 // Gate on session readiness: if not initialized (e.g., browser was closed and\n // reopened), this triggers bootstrap and waits; if a refresh is in progress\n // (tab wake, online recovery, concurrent 401), it piggybacks on it.\n await sessionManager.ensureReady();\n\n // FIX #6: Shallow-clone options to avoid mutating the caller's object\n const requestOptions = {\n ...options,\n credentials: options.credentials || 'include',\n headers: {\n ...options.headers,\n 'cf-session-id': sessionManager.getSessionId(),\n 'x-client-platform': CLIENT_PLATFORM,\n },\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 requestOptions.headers[sessionManager.config.tokenCookieName] = token;\n }\n }\n\n let response = await fetch(url, requestOptions);\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 === sessionManager.config.invalidSessionError) {\n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n await sessionManager.refreshToken();\n }\n requestOptions.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const newToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (newToken) {\n requestOptions.headers[sessionManager.config.tokenCookieName] = newToken;\n }\n }\n response = await fetch(url, requestOptions);\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 * @returns {Object} The same axios instance with interceptors attached\n */\nexport function setupAxiosInterceptor(axiosInstance) {\n const sessionManager = SessionManager.getInstance();\n\n // FIX #7: Eject previous interceptors if this instance was already set up\n if (axiosInstance._gwSessionRequestId !== undefined) {\n axiosInstance.interceptors.request.eject(axiosInstance._gwSessionRequestId);\n }\n if (axiosInstance._gwSessionResponseId !== undefined) {\n axiosInstance.interceptors.response.eject(axiosInstance._gwSessionResponseId);\n }\n\n // Request interceptor to add cf-session-id, token, and credentials\n axiosInstance._gwSessionRequestId = axiosInstance.interceptors.request.use(\n async (config) => {\n // Gate on session readiness before every request\n await sessionManager.ensureReady();\n if (sessionManager.config.credentials) {\n config.withCredentials = true;\n }\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._gwSessionResponseId = 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 === sessionManager.config.invalidSessionError) {\n originalRequest._retry = true;\n\n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n await sessionManager.refreshToken();\n }\n\n originalRequest.headers['cf-session-id'] = sessionManager.getSessionId();\n 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}"],"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","DEFAULT_CONFIG","bootstrapUrl","refreshInterval","tokenExpiry","maxRetries","bootstrapTimeout","tokenCookieName","invalidSessionError","SessionManager","instance","config","state","isInitialized","isLoading","lastRefreshTime","errorCode","nextRefreshTime","cfSessionId","initializationFailed","refreshTimerId","listeners","Set","initializationPromise","_configured","_boundHandleVisibilityChange","_handleVisibilityChange","bind","_boundHandlePageShow","_handlePageShow","_boundHandleOnline","_handleOnline","configure","trim","undefined","isFinite","Number","isInteger","credentials","logLevel","initialize","forceRefresh","window","token","getToken","notifyListeners","lastError","attempt","newSessionId","generateSessionId","controller","AbortController","timeoutId","setTimeout","abort","response","data","fetch","method","signal","headers","fetchError","timeout","url","clearTimeout","ok","status","statusText","json","jsonError","originalError","refresh_time","expire_time","tokenFromResponse","setCookie","setCfSessionId","Date","now","startTokenRefresh","delay","Math","min","pow","Promise","resolve","isRefreshing","waitForRefresh","_isTokenLikelyExpired","ensureReady","_","needsRefresh","sessionId","maxAge","cookieMaxAge","crypto","randomUUID","random","toString","substr","getSessionId","shouldUseTokenHeader","location","protocol","_sanitizeCookieName","replace","getCookie","document","sanitizedName","parts","cookie","split","length","decodeURIComponent","pop","shift","String","value","expires","toUTCString","isSecure","secureFlag","encodedValue","encodeURIComponent","interval","stopTokenRefresh","async","refreshToken","errorMessage","retryDelay","_startVisibilityListener","_startOnlineListener","_stopVisibilityListener","_stopOnlineListener","addEventListener","removeEventListener","_refreshIfOverdue","catch","visibilityState","event","persisted","getSessionStatus","timeUntilRefresh","max","subscribe","listener","TypeError","add","delete","Array","from","forEach","destroy","clear","getInstance","useSession","options","autoInitialize","sessionManager","sessionState","setSessionState","useState","setTimeUntilRefresh","nextRefreshTimeRef","useRef","useEffect","newState","current","tickId","setInterval","nrt","clearInterval","refresh","useCallback","CLIENT_PLATFORM","fetchInterceptor","requestOptions","clonedResponse","clone","error_msg","newToken","e","setupAxiosInterceptor","axiosInstance","_gwSessionRequestId","interceptors","request","eject","_gwSessionResponseId","use","withCredentials","reject","originalRequest","_retry"],"mappings":"6EAIO,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,GC3CIK,EAAiB,CACrBC,aAAc,qBACdC,gBAAiB,KACjBC,YAAa,KACbC,WAAY,EACZC,iBAAkB,IAClBC,gBAAiB,QACjBC,oBAAqB,sBAOvB,MAAMC,EACJ,WAAAvC,GACE,GAAIuC,EAAeC,SACjB,OAAOD,EAAeC,SAGxBnC,KAAKoC,OAAS,IAAKV,GAEnB1B,KAAKqC,MAAQ,CACXC,eAAe,EACfC,WAAW,EACXC,gBAAiB,KACjBpB,MAAO,KACPqB,UAAW,KACXC,gBAAiB,KACjBb,YAAa,KACbc,YAAa,KACbC,sBAAsB,GAGxB5C,KAAK6C,eAAiB,KACtB7C,KAAK8C,UAAY,IAAIC,IACrB/C,KAAKgD,sBAAwB,KAC7BhD,KAAKiD,aAAc,EACnBjD,KAAKkD,6BAA+BlD,KAAKmD,wBAAwBC,KAAKpD,MACtEA,KAAKqD,qBAAuBrD,KAAKsD,gBAAgBF,KAAKpD,MACtDA,KAAKuD,mBAAqBvD,KAAKwD,cAAcJ,KAAKpD,MAElDkC,EAAeC,SAAWnC,IAC5B,CAcA,SAAAyD,CAAUrB,EAAS,IACjB,IAAKA,EAAOT,cAA+C,iBAAxBS,EAAOT,eAA8BS,EAAOT,aAAa+B,OAC1F,MAAM,IAAIpD,EAAmB,0DAA2D,CAAEqB,aAAcS,EAAOT,eAGjH,QAA+BgC,IAA3BvB,EAAOR,gBAA+B,CACxC,GAAsC,iBAA3BQ,EAAOR,kBAAiCgC,SAASxB,EAAOR,iBACjE,MAAM,IAAItB,EAAmB,0CAA2C,CAAEsB,gBAAiBQ,EAAOR,kBAEpG,GAAIQ,EAAOR,iBAAmB,EAC5B,MAAM,IAAItB,EAAmB,mCAAoC,CAAEsB,gBAAiBQ,EAAOR,iBAE/F,CAEA,QAA2B+B,IAAvBvB,EAAOP,YAA2B,CACpC,GAAkC,iBAAvBO,EAAOP,cAA6B+B,SAASxB,EAAOP,aAC7D,MAAM,IAAIvB,EAAmB,sCAAuC,CAAEuB,YAAaO,EAAOP,cAE5F,GAAIO,EAAOP,aAAe,EACxB,MAAM,IAAIvB,EAAmB,+BAAgC,CAAEuB,YAAaO,EAAOP,aAEvF,CAEA,QAA0B8B,IAAtBvB,EAAON,aACwB,iBAAtBM,EAAON,aAA4B+B,OAAOC,UAAU1B,EAAON,aAAeM,EAAON,WAAa,GACvG,MAAM,IAAIxB,EAAmB,wCAAyC,CAAEwB,WAAYM,EAAON,aAI/F9B,KAAKoC,OAAS,IACTpC,KAAKoC,UACLA,EACH2B,iBAAoCJ,IAAvBvB,EAAO2B,aAA4B3B,EAAO2B,YACvDC,SAAU5B,EAAO4B,UAAY,QAG/BhD,EAAOE,SAASlB,KAAKoC,OAAO4B,UAC5BhE,KAAKiD,aAAc,EAEfjD,KAAKoC,OAAOR,iBAAmB5B,KAAKoC,OAAOP,aAC3C7B,KAAKoC,OAAOR,iBAAmB5B,KAAKoC,OAAOP,aAC7Cb,EAAOO,KAAK,6EAEhB,CAOA,gBAAM0C,CAAWC,GAAe,GAC9B,GAAsB,oBAAXC,OACT,MAAM,IAAI1D,EAAS,sDAOrB,GAJKT,KAAKiD,aACRjC,EAAOO,KAAK,iEAGVvB,KAAKqC,MAAMO,qBAEb,MADA5B,EAAOO,KAAK,2DACN,IAAIhB,EAAe,kEAI3B,GAAIP,KAAKgD,sBAEP,OADAhC,EAAOS,MAAM,kDACNzB,KAAKgD,sBAId,GAAIhD,KAAKqC,MAAMC,gBAAkB4B,EAE/B,OADAlD,EAAOS,MAAM,+BACN,CAAE2C,MAAOpE,KAAKqE,SAASrE,KAAKoC,OAAOJ,kBAG5ChC,KAAKqC,MAAME,WAAY,EACvBvC,KAAKqC,MAAMjB,MAAQ,KACnBpB,KAAKsE,kBAELtE,KAAKgD,sBAAwB,WAC3B,IAAIuB,EAEJ,IAAK,IAAIC,EAAU,EAAGA,GAAWxE,KAAKoC,OAAON,WAAY0C,IACvD,IACE,MAAMC,EAAezE,KAAK0E,oBAE1B1D,EAAOQ,KAAK,kCAAkCgD,KAAWxE,KAAKoC,OAAON,eAAgB9B,KAAKoC,OAAOT,cAEjG,MAAMgD,EAAa,IAAIC,gBACjBC,EAAYC,WAAW,IAAMH,EAAWI,QAAS/E,KAAKoC,OAAOL,kBAEnE,IAAIiD,EAgCAC,EA/BJ,IACED,QAAiBE,MAAMlF,KAAKoC,OAAOT,aAAc,CAC/CwD,OAAQ,MACRpB,YAAa/D,KAAKoC,OAAO2B,YAAc,UAAY,cACnDqB,OAAQT,EAAWS,OACnBC,QAAS,CACP,gBAAiBZ,EACjB,oBA/JQ,SAgKLzE,KAAKoC,OAAOiD,UAGrB,CAAE,MAAOC,GACP,GAAwB,eAApBA,EAAWrF,KACb,MAAM,IAAIO,EAAa,qCAAqCR,KAAKoC,OAAOL,qBAAsB,CAC5FwD,QAASvF,KAAKoC,OAAOL,iBACrByD,IAAKxF,KAAKoC,OAAOT,eAGrB,MAAM2D,CACR,CAAC,QACCG,aAAaZ,EACf,CAEA,IAAKG,EAASU,GACZ,MAAM,IAAInF,EAAe,qBAAqByE,EAASW,UAAUX,EAASY,aAAc,CACtFD,OAAQX,EAASW,OACjBC,WAAYZ,EAASY,WACrBJ,IAAKxF,KAAKoC,OAAOT,eAKrB,IACEsD,QAAaD,EAASa,MACxB,CAAE,MAAOC,GACP,MAAM,IAAIvF,EAAe,uCAAwC,CAAEwF,cAAeD,EAAUlG,SAC9F,CACAoB,EAAOQ,KAAK,wBAGZ,MAAMI,EAAkBqD,EAAKe,aACL,IAApBf,EAAKe,aACLhG,KAAKoC,OAAOR,gBAGVC,EAAcoD,EAAKgB,YACF,IAAnBhB,EAAKgB,YACLjG,KAAKoC,OAAOP,YAIVqE,EAAoBjB,EAAKjF,KAAKoC,OAAOJ,iBAmB3C,OAlBIkE,GACFlG,KAAKmG,UAAUnG,KAAKoC,OAAOJ,gBAAiBkE,EAAmBrE,EAAc,KAI/E7B,KAAKoG,eAAe3B,EAAc5C,EAAc,KAEhD7B,KAAKqC,MAAMC,eAAgB,EAC3BtC,KAAKqC,MAAME,WAAY,EACvBvC,KAAKqC,MAAMG,gBAAkB6D,KAAKC,MAClCtG,KAAKqC,MAAMK,gBAAkB2D,KAAKC,MAAQ1E,EAC1C5B,KAAKqC,MAAMR,YAAcA,EACzB7B,KAAKqC,MAAMjB,MAAQ,KAGnBpB,KAAKuG,kBAAkB3E,GAEvB5B,KAAKsE,kBACEW,CACT,CAAE,MAAO7D,GAIP,GAHAmD,EAAYnD,aAAiB1B,MAAQ0B,EAAQ,IAAIZ,EAAa,yBAA0B,CAAEY,UAC1FJ,EAAOI,MAAM,qBAAqBoD,YAAmBD,EAAU3E,SAE3D4E,EAAUxE,KAAKoC,OAAON,WAAY,CACpC,MAAM0E,EAAQC,KAAKC,IAAI,IAAOD,KAAKE,IAAI,EAAGnC,EAAU,GAAI,KACxDxD,EAAOQ,KAAK,eAAegF,gBACrB,IAAII,QAAQC,GAAW/B,WAAW+B,EAASL,GACnD,CACF,CASF,MANAxF,EAAOI,MAAM,iCACbpB,KAAKqC,MAAME,WAAY,EACvBvC,KAAKqC,MAAMjB,MAAQmD,GAAW3E,SAAW,gBACzCI,KAAKqC,MAAMI,UAAY8B,GAAW1E,MAAQ,gBAC1CG,KAAKqC,MAAMO,sBAAuB,EAClC5C,KAAKsE,kBACCC,CACP,EAvG4B,GAyG7B,IACE,aAAavE,KAAKgD,qBACpB,CAAC,QACChD,KAAKgD,sBAAwB,IAC/B,CACF,CAMA,YAAA8D,GACE,OAAsC,OAA/B9G,KAAKgD,qBACd,CAMA,oBAAM+D,GACJ,OAAI/G,KAAKgD,uBACPhC,EAAOS,MAAM,2CACNzB,KAAKgD,uBAEP4D,QAAQC,SACjB,CAOA,qBAAAG,GACE,OAAKhH,KAAKqC,MAAMG,kBAAoBxC,KAAKqC,MAAMR,aACxCwE,KAAKC,OAAStG,KAAKqC,MAAMG,gBAAkBxC,KAAKqC,MAAMR,WAC/D,CAWA,iBAAMoF,GAEJ,IAAIjH,KAAKqC,MAAMC,eAAkBtC,KAAKgH,wBAKtC,GAAIhH,KAAKgD,sBACP,UACQhD,KAAKgD,qBACb,CAAE,MAAOkE,GAEPlG,EAAOS,MAAM,wEACf,MAKF,GAAIzB,KAAKiD,cAAgBjD,KAAKqC,MAAMO,qBAClC,UACQ5C,KAAKiE,WAAWjE,KAAKqC,MAAMC,cACnC,CAAE,MAAO4E,GACPlG,EAAOS,MAAM,8DACf,CAIJ,CAOA,YAAA0F,GACE,OAAOnH,KAAKqC,MAAMC,eAAiBtC,KAAKgH,uBAC1C,CAOA,cAAAZ,CAAegB,EAAWC,GACxBrH,KAAKqC,MAAMM,YAAcyE,EACzB,MAAME,EAAeD,GAAUrH,KAAKoC,OAAOP,YAAc,IACzD7B,KAAKmG,UAAU,gBAAiBiB,EAAWE,EAC7C,CAMA,iBAAA5C,GACE,MAAsB,oBAAX6C,QAA0BA,OAAOC,WACnCD,OAAOC,aAET,GAAGnB,KAAKC,SAASG,KAAKgB,SAASC,SAAS,IAAIC,OAAO,EAAG,IAC/D,CAMA,YAAAC,GACE,OAAO5H,KAAKqC,MAAMM,WACpB,CAMA,oBAAAkF,GACE,MAAsB,oBAAX1D,QACyB,UAA7BA,OAAO2D,SAASC,QACzB,CAOA,mBAAAC,CAAoB/H,GAClB,OAAOA,EAAKgI,QAAQ,kBAAmB,GACzC,CAOA,SAAAC,CAAUjI,GACR,GAAwB,oBAAbkI,SAA0B,OAAO,KAC5C,MAAMC,EAAgBpI,KAAKgI,oBAAoB/H,GAC/C,IAAKmI,EAAe,OAAO,KAC3B,MACMC,EADQ,KAAKF,SAASG,SACRC,MAAM,KAAKH,MAC/B,GAAqB,IAAjBC,EAAMG,OACR,IACE,OAAOC,mBAAmBJ,EAAMK,MAAMH,MAAM,KAAKI,QACnD,CAAE,MAAOvH,GAEP,OADAJ,EAAOI,MAAM,iCAAkCA,aAAiB1B,MAAQ0B,EAAMxB,QAAUgJ,OAAOxH,IACxF,IACT,CAEF,OAAO,IACT,CAOA,QAAAiD,CAASpE,EAAOD,KAAKoC,OAAOJ,iBAC1B,OAAOhC,KAAKkI,UAAUjI,EACxB,CAQA,SAAAkG,CAAUlG,EAAM4I,EAAOxB,GACrB,GAAwB,oBAAbc,SAET,YADAnH,EAAOO,KAAK,gDAId,MAAM6G,EAAgBpI,KAAKgI,oBAAoB/H,GAC/C,IAAKmI,EAEH,YADApH,EAAOI,MAAM,gCAIf,MAAM0H,EAAU,IAAIzC,KAAKA,KAAKC,MAAiB,IAATe,GAAe0B,cAC/CC,EAA6B,oBAAX7E,QAAuD,WAA7BA,OAAO2D,SAASC,SAC5DkB,EAAaD,EAAW,WAAa,GACrCE,EAAeC,mBAAmBN,GACxCV,SAASG,OAAS,GAAGF,KAAiBc,sBAAiCJ,kBAAwBG,IAC/FjI,EAAOS,MAAM,eAAe2G,iBAA6Bf,KAAU2B,EAAW,YAAc,6BAC9F,CAMA,iBAAAzC,CAAkB6C,EAAWpJ,KAAKoC,OAAOR,iBACvC5B,KAAKqJ,mBACLrI,EAAOQ,KAAK,+BAA+B4H,EAAW,eAEtDpJ,KAAK6C,eAAiBiC,WAAWwE,UAC/BtI,EAAOQ,KAAK,4BACZ,UACQxB,KAAKuJ,cACb,CAAE,MAAOnI,GACP,MAAMoI,EAAepI,aAAiB1B,MAAQ0B,EAAMxB,QAAUgJ,OAAOxH,GAC/DqB,EAAYrB,GAAOvB,MAAQ,gBACjCmB,EAAOI,MAAM,uBAAwBoI,GACrCxJ,KAAKqC,MAAMjB,MAAQoI,EACnBxJ,KAAKqC,MAAMI,UAAYA,EACvBzC,KAAKsE,kBAEL,MAAMmF,EAAahD,KAAKC,IAAe,EAAX0C,EAAc,KAC1CpI,EAAOQ,KAAK,oCAAoCiI,EAAa,QAC7DzJ,KAAKuG,kBAAkBkD,EACzB,GACCL,GAGHpJ,KAAK0J,2BAEL1J,KAAK2J,sBACP,CAKA,gBAAAN,GACMrJ,KAAK6C,iBACP4C,aAAazF,KAAK6C,gBAClB7C,KAAK6C,eAAiB,KACtB7B,EAAOS,MAAM,gCAEfzB,KAAK4J,0BACL5J,KAAK6J,qBACP,CAKA,wBAAAH,GAC0B,oBAAbvB,WACXA,SAAS2B,iBAAiB,mBAAoB9J,KAAKkD,8BAC7B,oBAAXiB,QACTA,OAAO2F,iBAAiB,WAAY9J,KAAKqD,sBAE7C,CAKA,uBAAAuG,GAC0B,oBAAbzB,WACXA,SAAS4B,oBAAoB,mBAAoB/J,KAAKkD,8BAChC,oBAAXiB,QACTA,OAAO4F,oBAAoB,WAAY/J,KAAKqD,sBAEhD,CAMA,iBAAA2G,GACE,IAAKhK,KAAKqC,MAAMC,eAAiBtC,KAAKqC,MAAMO,qBAAsB,OAAO,EACzE,GAAI5C,KAAK8G,eAAgB,OAAO,EAEhC,MAAMR,EAAMD,KAAKC,OACX5D,gBAAEA,GAAoB1C,KAAKqC,MAEjC,SAAIK,GAAmB4D,GAAO5D,KAC5B1B,EAAOQ,KAAK,mDACZxB,KAAKuJ,eAAeU,MAAM7I,IACxB,MAAMoI,EAAepI,aAAiB1B,MAAQ0B,EAAMxB,QAAUgJ,OAAOxH,GACrEJ,EAAOI,MAAM,4BAA6BoI,MAErC,EAGX,CAKA,uBAAArG,GACmC,YAA7BgF,SAAS+B,iBACblK,KAAKgK,mBACP,CAKA,eAAA1G,CAAgB6G,GACTA,EAAMC,WAENpK,KAAKqC,MAAMC,gBAAiBtC,KAAKqC,MAAMO,uBACxC5C,KAAK8G,iBACT9F,EAAOQ,KAAK,kDACZxB,KAAKuJ,eAAeU,MAAM7I,IACxB,MAAMoI,EAAepI,aAAiB1B,MAAQ0B,EAAMxB,QAAUgJ,OAAOxH,GACrEJ,EAAOI,MAAM,oCAAqCoI,MAEtD,CAKA,oBAAAG,GACwB,oBAAXxF,QACXA,OAAO2F,iBAAiB,SAAU9J,KAAKuD,mBACzC,CAKA,mBAAAsG,GACwB,oBAAX1F,QACXA,OAAO4F,oBAAoB,SAAU/J,KAAKuD,mBAC5C,CAKA,aAAAC,GACOxD,KAAKqC,MAAMO,uBAChB5B,EAAOQ,KAAK,sDACZxB,KAAKuJ,eAAeU,MAAM7I,IACxB,MAAMoI,EAAepI,aAAiB1B,MAAQ0B,EAAMxB,QAAUgJ,OAAOxH,GACrEJ,EAAOI,MAAM,mCAAoCoI,KAErD,CAMA,kBAAMD,GAGJ,OAFAvI,EAAOQ,KAAK,kCACZxB,KAAKqC,MAAMO,sBAAuB,EAC3B5C,KAAKiE,YAAW,EACzB,CAMA,gBAAAoG,GACE,MAAO,CACL/H,cAAetC,KAAKqC,MAAMC,cAC1BC,UAAWvC,KAAKqC,MAAME,UACtBC,gBAAiBxC,KAAKqC,MAAMG,gBAC5BE,gBAAiB1C,KAAKqC,MAAMK,gBAC5Bb,YAAa7B,KAAKqC,MAAMR,YACxBT,MAAOpB,KAAKqC,MAAMjB,MAClBqB,UAAWzC,KAAKqC,MAAMI,UACtBG,qBAAsB5C,KAAKqC,MAAMO,qBACjC0H,iBAAkBtK,KAAKqC,MAAMK,gBACzB+D,KAAK8D,IAAI,EAAGvK,KAAKqC,MAAMK,gBAAkB2D,KAAKC,OAC9C,KAER,CAOA,SAAAkE,CAAUC,GACR,GAAwB,mBAAbA,EACT,MAAM,IAAIC,UAAU,+BAGtB,OADA1K,KAAK8C,UAAU6H,IAAIF,GACZ,KACLzK,KAAK8C,UAAU8H,OAAOH,GAE1B,CAKA,eAAAnG,GACE,MAAMqB,EAAS3F,KAAKqK,mBACpBQ,MAAMC,KAAK9K,KAAK8C,WAAWiI,QAAQN,IACjC,IACEA,EAAS9E,EACX,CAAE,MAAOvE,GACP,MAAMoI,EAAepI,aAAiB1B,MAAQ0B,EAAMxB,QAAUgJ,OAAOxH,GACrEJ,EAAOI,MAAM,kBAAmBoI,EAClC,GAEJ,CAKA,OAAAwB,GACEhL,KAAKqJ,mBACLrJ,KAAKgD,sBAAwB,KAC7BhD,KAAK8C,UAAUmI,QACfjL,KAAKmG,UAAU,gBAAiB,IAAI,GACpCnG,KAAKmG,UAAUnG,KAAKoC,OAAOJ,gBAAiB,OAC5ChC,KAAKoC,OAAS,IAAKV,GACnB1B,KAAKqC,MAAQ,CACXC,eAAe,EACfC,WAAW,EACXC,gBAAiB,KACjBpB,MAAO,KACPqB,UAAW,KACXC,gBAAiB,KACjBb,YAAa,KACbc,YAAa,KACbC,sBAAsB,GAExB5C,KAAKiD,aAAc,EACnBjC,EAAOQ,KAAK,YACd,CAMA,kBAAO0J,GAIL,OAHKhJ,EAAeC,WAClBD,EAAeC,SAAW,IAAID,GAEzBA,EAAeC,QACxB,ECjpBK,SAASgJ,EAAWC,EAAU,IACjC,MAAMC,eAAEA,GAAiB,GAASD,EAE5BE,EAAiBpJ,EAAegJ,eAE/BK,EAAcC,GAAmBC,EAAS,IAC7CH,EAAejB,qBAKZC,EAAkBoB,GAAuBD,EAAS,KACrD,MAAM/I,gBAAEA,GAAoB4I,EAAejB,mBAC3C,OAAO3H,EAAkB+D,KAAK8D,IAAI,EAAG7H,EAAkB2D,KAAKC,OAAS,OAGnEqF,EAAqBC,EAAOL,EAAa7I,iBAG/CmJ,EAAU,IACcP,EAAed,UAAWsB,IAC1CN,EAAgBM,GAChBH,EAAmBI,QAAUD,EAASpJ,kBAI3C,IAGHmJ,EAAU,KACN,IAAKN,EAAa7I,gBAEd,YADAgJ,EAAoB,MAIxBA,EAAoBjF,KAAK8D,IAAI,EAAGgB,EAAa7I,gBAAkB2D,KAAKC,QACpE,MAAM0F,EAASC,YAAY,KACvB,MAAMC,EAAMP,EAAmBI,QAC/BL,EAAoBQ,EAAMzF,KAAK8D,IAAI,EAAG2B,EAAM7F,KAAKC,OAAS,OAC3D,KACH,MAAO,IAAM6F,cAAcH,IAC5B,CAACT,EAAa7I,kBAGjBmJ,EAAU,MACFR,GAAmBE,EAAajJ,eAAkBiJ,EAAahJ,WAAcgJ,EAAa3I,sBAC1F0I,EAAerH,aAAagG,MAAM7I,IAC9BJ,EAAOI,MAAM,8BAA+BA,MAGrD,CAACiK,EAAgBE,EAAajJ,cAAeiJ,EAAahJ,UAAWgJ,EAAa3I,uBAGrF,MAAMwJ,EAAUC,EAAY/C,UACxB,UACUgC,EAAe/B,cACzB,CAAE,MAAOnI,GAEL,MADAJ,EAAOI,MAAM,yBAA0BA,GACjCA,CACV,GACD,IAGG6C,EAAaoI,EAAY/C,UAC3B,UACUgC,EAAerH,YACzB,CAAE,MAAO7C,GAEL,MADAJ,EAAOI,MAAM,gCAAiCA,GACxCA,CACV,GACD,IAEH,MAAO,CAEHkB,cAAeiJ,EAAajJ,cAC5BC,UAAWgJ,EAAahJ,UACxBnB,MAAOmK,EAAanK,MACpBoB,gBAAiB+I,EAAa/I,gBAC9BE,gBAAiB6I,EAAa7I,gBAC9B4H,mBACA1H,qBAAsB2I,EAAa3I,qBAGnCwJ,UACAnI,aAER,CCjGY,MAACqI,EAAkB,MAQxBhD,eAAeiD,EAAiB/G,EAAK4F,EAAU,IACpD,MAAME,EAAiBpJ,EAAegJ,oBAKhCI,EAAerE,cAGrB,MAAMuF,EAAiB,IAClBpB,EACHrH,YAAaqH,EAAQrH,aAAe,UACpCsB,QAAS,IACJ+F,EAAQ/F,QACX,gBAAiBiG,EAAe1D,eAChC,oBAAqB0E,IAKzB,GAAIhB,EAAezD,uBAAwB,CACzC,MAAMzD,EAAQkH,EAAejH,SAASiH,EAAelJ,OAAOJ,iBACxDoC,IACFoI,EAAenH,QAAQiG,EAAelJ,OAAOJ,iBAAmBoC,EAEpE,CAEA,IAAIY,QAAiBE,MAAMM,EAAKgH,GAGhC,GAAwB,MAApBxH,EAASW,OAAgB,CAC3B,MAAM8G,EAAiBzH,EAAS0H,QAChC,IAEE,UADmBD,EAAe5G,QACzB8G,YAAcrB,EAAelJ,OAAOH,oBAAqB,CAOhE,GANIqJ,EAAexE,qBACXwE,EAAevE,uBAEfuE,EAAe/B,eAEvBiD,EAAenH,QAAQ,iBAAmBiG,EAAe1D,eACrD0D,EAAezD,uBAAwB,CACzC,MAAM+E,EAAWtB,EAAejH,SAASiH,EAAelJ,OAAOJ,iBAC3D4K,IACFJ,EAAenH,QAAQiG,EAAelJ,OAAOJ,iBAAmB4K,EAEpE,CACA5H,QAAiBE,MAAMM,EAAKgH,EAC9B,CACF,CAAE,MAAOK,GAET,CACF,CAEA,OAAO7H,CACT,CAOO,SAAS8H,EAAsBC,GACpC,MAAMzB,EAAiBpJ,EAAegJ,cAgEtC,YA7D0CvH,IAAtCoJ,EAAcC,qBAChBD,EAAcE,aAAaC,QAAQC,MAAMJ,EAAcC,0BAEdrJ,IAAvCoJ,EAAcK,sBAChBL,EAAcE,aAAajI,SAASmI,MAAMJ,EAAcK,sBAI1DL,EAAcC,oBAAsBD,EAAcE,aAAaC,QAAQG,IACrE/D,MAAOlH,IAQL,SANMkJ,EAAerE,cACjBqE,EAAelJ,OAAO2B,cACxB3B,EAAOkL,iBAAkB,GAE3BlL,EAAOiD,QAAQ,iBAAmBiG,EAAe1D,eACjDxF,EAAOiD,QAAQ,qBAAuBiH,EAClChB,EAAezD,uBAAwB,CACzC,MAAMzD,EAAQkH,EAAejH,SAASiH,EAAelJ,OAAOJ,iBACxDoC,IACFhC,EAAOiD,QAAQiG,EAAelJ,OAAOJ,iBAAmBoC,EAE5D,CACA,OAAOhC,GAERhB,GAAUwF,QAAQ2G,OAAOnM,IAI5B2L,EAAcK,qBAAuBL,EAAcE,aAAajI,SAASqI,IACtErI,GAAaA,EACdsE,MAAOlI,IACL,MAAMoM,EAAkBpM,EAAMgB,OAG9B,GAA+B,MAA3BhB,EAAM4D,UAAUW,SAAmB6H,EAAgBC,QACjDrM,EAAM4D,UAAUC,MAAM0H,YAAcrB,EAAelJ,OAAOH,oBAAqB,CAWjF,GAVAuL,EAAgBC,QAAS,EAErBnC,EAAexE,qBACXwE,EAAevE,uBAEfuE,EAAe/B,eAGvBiE,EAAgBnI,QAAQ,iBAAmBiG,EAAe1D,eAC1D4F,EAAgBnI,QAAQ,qBAAuBiH,EAC3ChB,EAAezD,uBAAwB,CACzC,MAAM+E,EAAWtB,EAAejH,SAASiH,EAAelJ,OAAOJ,iBAC3D4K,IACFY,EAAgBnI,QAAQiG,EAAelJ,OAAOJ,iBAAmB4K,EAErE,CACA,OAAOG,EAAcS,EACvB,CAGF,OAAO5G,QAAQ2G,OAAOnM,KAInB2L,CACT"}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/errors.js","../src/SessionManager.js","../src/hooks/useSession.js","../src/interceptors.js","../src/logger.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","import {BootstrapError, ConfigurationError, NetworkError, SSRError} from './errors.js';\nconst CLIENT_PLATFORM = 'web';\n\nconst DEFAULT_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 bootstrapTimeout: 30000, // 30 seconds timeout for bootstrap fetch\n tokenCookieName: 'token', // Must match server's session_header\n invalidSessionError: 'INVALID-GW-SESSION', // Must match server's error response\n};\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 = { ...DEFAULT_CONFIG };\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 this._configured = false;\n this._boundHandleVisibilityChange = this._handleVisibilityChange.bind(this);\n this._boundHandlePageShow = this._handlePageShow.bind(this);\n this._boundHandleOnline = this._handleOnline.bind(this);\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 {number} config.bootstrapTimeout - Timeout for bootstrap fetch in milliseconds (default: 30s)\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 if (config.maxRetries !== undefined) {\n if (typeof config.maxRetries !== 'number' || !Number.isInteger(config.maxRetries) || config.maxRetries < 1) {\n throw new ConfigurationError('maxRetries must be a positive integer', { maxRetries: config.maxRetries });\n }\n }\n\n this.config = {\n ...this.config,\n ...config,\n credentials: config.credentials !== undefined ? config.credentials : true,\n };\n\n this._configured = true;\n }\n\n /**\n * Initialize session by calling bootstrap API\n * @param {boolean} forceRefresh - Force a new bootstrap call even if already initialized (used by refreshToken)\n * @returns {Promise<Object>} Bootstrap response\n */\n async initialize(forceRefresh = 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 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 return this.initializationPromise;\n }\n\n // If already initialized in this page session and not a forced refresh, return current state\n if (this.state.isInitialized && !forceRefresh) {\n return { token: this.getToken(this.config.tokenCookieName) };\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 const newSessionId = this.generateSessionId();\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.bootstrapTimeout);\n\n let response;\n try {\n response = await fetch(this.config.bootstrapUrl, {\n method: 'GET',\n credentials: this.config.credentials ? 'include' : 'same-origin',\n signal: controller.signal,\n headers: {\n 'cf-session-id': newSessionId,\n 'x-client-platform': CLIENT_PLATFORM,\n ...this.config.headers,\n },\n });\n } catch (fetchError) {\n if (fetchError.name === 'AbortError') {\n throw new NetworkError(`Bootstrap request timed out after ${this.config.bootstrapTimeout}ms`, {\n timeout: this.config.bootstrapTimeout,\n url: this.config.bootstrapUrl\n });\n }\n throw fetchError;\n } finally {\n clearTimeout(timeoutId);\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\n // Use refresh_time from response (in seconds) or fall back to config.\n // Reject non-positive values to prevent silent cookie expiry failures.\n const refreshInterval =\n typeof data.refresh_time === 'number' && isFinite(data.refresh_time) && data.refresh_time > 0\n ? data.refresh_time * 1000\n : this.config.refreshInterval;\n\n // Calculate token expiry from response (in seconds) or fall back to config.\n // Reject non-positive values to prevent cookies with past expiry dates.\n const tokenExpiry =\n typeof data.expire_time === 'number' && isFinite(data.expire_time) && data.expire_time > 0\n ? data.expire_time * 1000\n : this.config.tokenExpiry;\n\n // If server returned token in response body (non-cookie mode),\n // store it as a client-side cookie. Key matches server's session_header.\n const tokenFromResponse = data[this.config.tokenCookieName];\n if (tokenFromResponse) {\n this.setCookie(this.config.tokenCookieName, tokenFromResponse, tokenExpiry / 1000);\n }\n\n // Now safe to update cf-session-id — new token is stored/set\n this.setCfSessionId(newSessionId, tokenExpiry / 1000);\n\n this.state.isInitialized = true;\n this.state.isLoading = false;\n this.state.lastRefreshTime = Date.now();\n this.state.nextRefreshTime = Date.now() + refreshInterval;\n this.state.tokenExpiry = tokenExpiry;\n this.state.error = null;\n\n // Start automatic refresh timer with dynamic interval\n this.startTokenRefresh(refreshInterval);\n\n this.notifyListeners();\n return data;\n } catch (error) {\n lastError = error instanceof Error ? error : new NetworkError('Unknown error occurred', { error });\n\n if (attempt < this.config.maxRetries) {\n const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n\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 return this.initializationPromise;\n }\n return Promise.resolve();\n }\n\n /**\n * Check if the token is likely expired based on in-memory timestamps.\n * After a full browser close + reopen, lastRefreshTime is null, so this returns true.\n * @returns {boolean} True if token appears expired or state is unknown\n */\n _isTokenLikelyExpired() {\n if (!this.state.lastRefreshTime || !this.state.tokenExpiry) return true;\n return Date.now() >= this.state.lastRefreshTime + this.state.tokenExpiry;\n }\n\n /**\n * Ensure the session is ready before making API calls.\n * - If initialized and token not expired, resolves immediately.\n * - If initialization is in progress, waits for it.\n * - If not initialized but configured, triggers initialization and waits.\n * - If not configured or permanently failed, resolves (lets request proceed;\n * the 401 handler in the interceptor will deal with it).\n * @returns {Promise<void>}\n */\n async ensureReady() {\n // Happy path: session is live and token hasn't expired\n if (this.state.isInitialized && !this._isTokenLikelyExpired()) {\n return;\n }\n\n // An initialization / refresh is already in flight — piggyback on it\n if (this.initializationPromise) {\n try {\n await this.initializationPromise;\n } catch (_) {\n // Initialization failed — let the request proceed, 401 handler will deal with it\n }\n return;\n }\n\n // Not initialized / token expired, no call in flight, but we *can* bootstrap\n if (this._configured && !this.state.initializationFailed) {\n try {\n await this.initialize(this.state.isInitialized); // force refresh if already initialized but token expired\n } catch (_) {\n // Initialization failed — nothing we can do, let request proceed\n }\n }\n\n // Not configured or permanently failed — nothing we can do here\n }\n\n /**\n * Check if the session needs a refresh (initialized but token expired).\n * Useful for interceptors to proactively refresh before sending a request.\n * @returns {boolean}\n */\n needsRefresh() {\n return this.state.isInitialized && this._isTokenLikelyExpired();\n }\n\n /**\n * Set cf-session-id in cookie\n * @param {string} sessionId - Session ID\n * @param {number} maxAge - Max age in seconds\n */\n setCfSessionId(sessionId, maxAge) {\n this.state.cfSessionId = sessionId;\n const cookieMaxAge = maxAge || this.config.tokenExpiry / 1000;\n this.setCookie('cf-session-id', sessionId, cookieMaxAge);\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 * Shared cookie name sanitizer used by both getCookie and setCookie\n * @param {string} name - Raw cookie name\n * @returns {string} Sanitized cookie name\n */\n _sanitizeCookieName(name) {\n return name.replace(/[^a-zA-Z0-9_-]/g, '');\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 sanitizedName = this._sanitizeCookieName(name);\n if (!sanitizedName) return null;\n const value = `; ${document.cookie}`;\n const parts = value.split(`; ${sanitizedName}=`);\n if (parts.length === 2) {\n try {\n return decodeURIComponent(parts.pop().split(';').shift());\n } catch (_) {\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') return;\n\n const sanitizedName = this._sanitizeCookieName(name);\n if (!sanitizedName) return;\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 }\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\n this.refreshTimerId = setTimeout(async () => {\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 this.state.error = errorMessage;\n this.state.errorCode = errorCode;\n this.notifyListeners();\n // Schedule retry instead of dying permanently\n const retryDelay = Math.min(interval * 2, 60000);\n this.startTokenRefresh(retryDelay);\n }\n }, interval);\n\n // Listen for visibility changes (tab wake from sleep / bfcache restore)\n this._startVisibilityListener();\n // Listen for network recovery\n this._startOnlineListener();\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 }\n this._stopVisibilityListener();\n this._stopOnlineListener();\n }\n\n /**\n * Start listening for page visibility changes and bfcache restore\n */\n _startVisibilityListener() {\n if (typeof document === 'undefined') return;\n document.addEventListener('visibilitychange', this._boundHandleVisibilityChange);\n if (typeof window !== 'undefined') {\n window.addEventListener('pageshow', this._boundHandlePageShow);\n }\n }\n\n /**\n * Stop listening for page visibility changes\n */\n _stopVisibilityListener() {\n if (typeof document === 'undefined') return;\n document.removeEventListener('visibilitychange', this._boundHandleVisibilityChange);\n if (typeof window !== 'undefined') {\n window.removeEventListener('pageshow', this._boundHandlePageShow);\n }\n }\n\n /**\n * Check if token refresh is overdue and trigger refresh if so\n * @returns {boolean} True if refresh was triggered\n */\n _refreshIfOverdue() {\n if (!this.state.isInitialized || this.state.initializationFailed) return false;\n if (this.isRefreshing()) return false;\n\n const now = Date.now();\n const { nextRefreshTime } = this.state;\n\n if (nextRefreshTime && now >= nextRefreshTime) {\n this.refreshToken().catch(() => {});\n return true;\n }\n return false;\n }\n\n /**\n * Handle visibility change — refresh if overdue when tab becomes visible\n */\n _handleVisibilityChange() {\n if (document.visibilityState !== 'visible') return;\n this._refreshIfOverdue();\n }\n\n /**\n * Handle pageshow — force bootstrap on bfcache restore (browser close + reopen)\n */\n _handlePageShow(event) {\n if (!event.persisted) return;\n // bfcache restore means browser was closed — always force a fresh bootstrap\n if (!this.state.isInitialized || this.state.initializationFailed) return;\n if (this.isRefreshing()) return;\n this.refreshToken().catch(() => {});\n }\n\n /**\n * Start listening for network recovery\n */\n _startOnlineListener() {\n if (typeof window === 'undefined') return;\n window.addEventListener('online', this._boundHandleOnline);\n }\n\n /**\n * Stop listening for network recovery\n */\n _stopOnlineListener() {\n if (typeof window === 'undefined') return;\n window.removeEventListener('online', this._boundHandleOnline);\n }\n\n /**\n * Handle online event — retry if initialization had failed due to network\n */\n _handleOnline() {\n if (!this.state.initializationFailed) return;\n this.refreshToken().catch(() => {});\n }\n\n /**\n * Manually refresh the session token\n * @returns {Promise<Object>} Bootstrap response\n */\n async refreshToken() {\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 Array.from(this.listeners).forEach(listener => {\n try {\n listener(status);\n } catch (_) {\n // Silently ignore listener errors\n }\n });\n }\n\n /**\n * Clean up and reset the session manager\n */\n destroy() {\n this.stopTokenRefresh();\n this.initializationPromise = null;\n this.listeners.clear();\n this.setCookie('cf-session-id', '', -1);\n this.setCookie(this.config.tokenCookieName, '', -1);\n this.config = { ...DEFAULT_CONFIG };\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 this._configured = false;\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;","import { useState, useEffect, useCallback, useRef } from 'react';\nimport SessionManager from '../SessionManager.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 // FIX #11: Separate countdown state — only this re-renders every second,\n // not the full session state which would cause unnecessary subscriber churn\n const [timeUntilRefresh, setTimeUntilRefresh] = useState(() => {\n const { nextRefreshTime } = sessionManager.getSessionStatus();\n return nextRefreshTime ? Math.max(0, nextRefreshTime - Date.now()) : null;\n });\n\n const nextRefreshTimeRef = useRef(sessionState.nextRefreshTime);\n\n // Subscribe to meaningful session state changes only\n useEffect(() => {\n const unsubscribe = sessionManager.subscribe((newState) => {\n setSessionState(newState);\n nextRefreshTimeRef.current = newState.nextRefreshTime;\n });\n\n return unsubscribe;\n }, []); // sessionManager is a singleton, no need to include in deps\n\n // Local tick timer for countdown display — avoids global tick in SessionManager\n useEffect(() => {\n if (!sessionState.nextRefreshTime) {\n setTimeUntilRefresh(null);\n return;\n }\n // Set immediately on mount / when nextRefreshTime changes\n setTimeUntilRefresh(Math.max(0, sessionState.nextRefreshTime - Date.now()));\n const tickId = setInterval(() => {\n const nrt = nextRefreshTimeRef.current;\n setTimeUntilRefresh(nrt ? Math.max(0, nrt - Date.now()) : null);\n }, 1000);\n return () => clearInterval(tickId);\n }, [sessionState.nextRefreshTime]);\n\n // Auto-initialize if enabled\n useEffect(() => {\n if (autoInitialize && !sessionState.isInitialized && !sessionState.isLoading && !sessionState.initializationFailed) {\n sessionManager.initialize().catch(() => {});\n }\n }, [autoInitialize, sessionState.isInitialized, sessionState.isLoading, sessionState.initializationFailed]);\n\n // Manual refresh function\n const refresh = useCallback(async () => {\n await sessionManager.refreshToken();\n }, []);\n\n // Initialize function (for manual control)\n const initialize = useCallback(async () => {\n await sessionManager.initialize();\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,\n initializationFailed: sessionState.initializationFailed,\n\n // Actions\n refresh,\n initialize,\n };\n}\n\nexport default useSession;","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 // Gate on session readiness: if not initialized (e.g., browser was closed and\n // reopened), this triggers bootstrap and waits; if a refresh is in progress\n // (tab wake, online recovery, concurrent 401), it piggybacks on it.\n await sessionManager.ensureReady();\n\n // FIX #6: Shallow-clone options to avoid mutating the caller's object\n const requestOptions = {\n ...options,\n credentials: options.credentials || 'include',\n headers: {\n ...options.headers,\n 'cf-session-id': sessionManager.getSessionId(),\n 'x-client-platform': CLIENT_PLATFORM,\n },\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 requestOptions.headers[sessionManager.config.tokenCookieName] = token;\n }\n }\n\n let response = await fetch(url, requestOptions);\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 === sessionManager.config.invalidSessionError) {\n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n await sessionManager.refreshToken();\n }\n requestOptions.headers['cf-session-id'] = sessionManager.getSessionId();\n if (sessionManager.shouldUseTokenHeader()) {\n const newToken = sessionManager.getToken(sessionManager.config.tokenCookieName);\n if (newToken) {\n requestOptions.headers[sessionManager.config.tokenCookieName] = newToken;\n }\n }\n response = await fetch(url, requestOptions);\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 * @returns {Object} The same axios instance with interceptors attached\n */\nexport function setupAxiosInterceptor(axiosInstance) {\n const sessionManager = SessionManager.getInstance();\n\n // FIX #7: Eject previous interceptors if this instance was already set up\n if (axiosInstance._gwSessionRequestId !== undefined) {\n axiosInstance.interceptors.request.eject(axiosInstance._gwSessionRequestId);\n }\n if (axiosInstance._gwSessionResponseId !== undefined) {\n axiosInstance.interceptors.response.eject(axiosInstance._gwSessionResponseId);\n }\n\n // Request interceptor to add cf-session-id, token, and credentials\n axiosInstance._gwSessionRequestId = axiosInstance.interceptors.request.use(\n async (config) => {\n // Gate on session readiness before every request\n await sessionManager.ensureReady();\n if (sessionManager.config.credentials) {\n config.withCredentials = true;\n }\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._gwSessionResponseId = 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 === sessionManager.config.invalidSessionError) {\n originalRequest._retry = true;\n\n if (sessionManager.isRefreshing()) {\n await sessionManager.waitForRefresh();\n } else {\n await sessionManager.refreshToken();\n }\n\n originalRequest.headers['cf-session-id'] = sessionManager.getSessionId();\n 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 * Logger utility — disabled for production builds.\n * All methods are no-ops to prevent leaking internal details to the browser console.\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 setLevel() {}\n error() {}\n warn() {}\n info() {}\n debug() {}\n}\n\nexport const logger = new Logger();\nexport { LOG_LEVELS };\n"],"names":["SessionError","Error","constructor","message","code","details","super","this","name","captureStackTrace","Object","setPrototypeOf","prototype","ConfigurationError","BootstrapError","NetworkError","SSRError","DEFAULT_CONFIG","bootstrapUrl","refreshInterval","tokenExpiry","maxRetries","bootstrapTimeout","tokenCookieName","invalidSessionError","SessionManager","instance","config","state","isInitialized","isLoading","lastRefreshTime","error","errorCode","nextRefreshTime","cfSessionId","initializationFailed","refreshTimerId","listeners","Set","initializationPromise","_configured","_boundHandleVisibilityChange","_handleVisibilityChange","bind","_boundHandlePageShow","_handlePageShow","_boundHandleOnline","_handleOnline","configure","trim","undefined","isFinite","Number","isInteger","credentials","initialize","forceRefresh","window","token","getToken","notifyListeners","lastError","attempt","newSessionId","generateSessionId","controller","AbortController","timeoutId","setTimeout","abort","response","data","fetch","method","signal","headers","fetchError","timeout","url","clearTimeout","ok","status","statusText","json","jsonError","originalError","refresh_time","expire_time","tokenFromResponse","setCookie","setCfSessionId","Date","now","startTokenRefresh","delay","Math","min","pow","Promise","resolve","isRefreshing","waitForRefresh","_isTokenLikelyExpired","ensureReady","_","needsRefresh","sessionId","maxAge","cookieMaxAge","crypto","randomUUID","random","toString","substr","getSessionId","shouldUseTokenHeader","location","protocol","_sanitizeCookieName","replace","getCookie","document","sanitizedName","parts","cookie","split","length","decodeURIComponent","pop","shift","value","expires","toUTCString","secureFlag","encodedValue","encodeURIComponent","interval","stopTokenRefresh","async","refreshToken","errorMessage","String","retryDelay","_startVisibilityListener","_startOnlineListener","_stopVisibilityListener","_stopOnlineListener","addEventListener","removeEventListener","_refreshIfOverdue","catch","visibilityState","event","persisted","getSessionStatus","timeUntilRefresh","max","subscribe","listener","TypeError","add","delete","Array","from","forEach","destroy","clear","getInstance","useSession","options","autoInitialize","sessionManager","sessionState","setSessionState","useState","setTimeUntilRefresh","nextRefreshTimeRef","useRef","useEffect","newState","current","tickId","setInterval","nrt","clearInterval","refresh","useCallback","CLIENT_PLATFORM","fetchInterceptor","requestOptions","clonedResponse","clone","error_msg","newToken","e","setupAxiosInterceptor","axiosInstance","_gwSessionRequestId","interceptors","request","eject","_gwSessionResponseId","use","withCredentials","reject","originalRequest","_retry","LOG_LEVELS","NONE","ERROR","WARN","INFO","DEBUG","logger","setLevel","warn","info","debug"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mapnests/gateway-web-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "Session token management SDK with automatic refresh for React/Next.js applications",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"nextjs",
|
|
37
37
|
"sdk"
|
|
38
38
|
],
|
|
39
|
-
"author": "
|
|
39
|
+
"author": "Md. Yeasir Arafat <myabadhon@gmail.com>",
|
|
40
40
|
"license": "MIT",
|
|
41
41
|
"engines": {
|
|
42
42
|
"node": ">=14.0.0"
|