@saas-support/react 0.3.1 → 0.3.2
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 +1 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.js +284 -220
- package/dist/react.d.ts +16 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class b extends Error{constructor(t,e,s="unknown"){super(e),this.name="SaaSError",this.code=t,this.domain=s}get isNotFound(){return this.code===404}get isUnauthorized(){return this.code===401}get isForbidden(){return this.code===403}get isConflict(){return this.code===409}get isRateLimited(){return this.code===429}}class g{constructor(t,e){this.baseUrl=t,this.authMode=e}async request(t,e,s,r){const i={"Content-Type":"application/json",...this.getAuthHeaders(),...r},a=await(await fetch(`${this.baseUrl}${e}`,{method:t,headers:i,body:s?JSON.stringify(s):void 0})).json();if(a.code&&a.code>=400){const d=this.inferDomain(e);throw new b(a.code,a.message||"Request failed",d)}return a.data}async get(t,e){return this.request("GET",t,void 0,e)}async post(t,e,s){return this.request("POST",t,e,s)}async patch(t,e,s){return this.request("PATCH",t,e,s)}async del(t,e){return this.request("DELETE",t,void 0,e)}getAuthHeaders(){switch(this.authMode.type){case"publishableKey":case"apiKey":return{"X-API-Key":this.authMode.key};case"portalToken":case"embedToken":return{Authorization:`Bearer ${this.authMode.token}`}}}inferDomain(t){return t.startsWith("/auth")?"auth":t.startsWith("/billing")?"billing":t.startsWith("/report")?"report":"unknown"}}class R{constructor(t){this.accessToken=null,this.refreshToken=null,this.refreshTimer=null,this.onRefreshNeeded=null,this.storageKey=`ss_rt_${t.slice(0,12)}`,this.refreshToken=this.loadRefreshToken()}setRefreshCallback(t){this.onRefreshNeeded=t}getAccessToken(){return this.accessToken}getRefreshToken(){return this.refreshToken}hasRefreshToken(){return this.refreshToken!==null}setTokens(t,e){this.accessToken=t,this.refreshToken=e,this.saveRefreshToken(e),this.scheduleRefresh(t)}clearTokens(){this.accessToken=null,this.refreshToken=null,this.removeRefreshToken(),this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null)}destroy(){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null)}scheduleRefresh(t){var r;this.refreshTimer&&clearTimeout(this.refreshTimer);const e=this.getTokenExpiry(t);if(!e)return;const s=e*1e3-Date.now()-6e4;if(s<=0){(r=this.onRefreshNeeded)==null||r.call(this);return}this.refreshTimer=setTimeout(()=>{var i;(i=this.onRefreshNeeded)==null||i.call(this)},s)}getTokenExpiry(t){try{const e=t.split(".")[1];return JSON.parse(atob(e)).exp??null}catch{return null}}loadRefreshToken(){try{return localStorage.getItem(this.storageKey)}catch{return null}}saveRefreshToken(t){try{localStorage.setItem(this.storageKey,t)}catch{}}removeRefreshToken(){try{localStorage.removeItem(this.storageKey)}catch{}}}class M{constructor(){this.listeners=new Map}on(t,e){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e),()=>{var s;(s=this.listeners.get(t))==null||s.delete(e)}}emit(t,e){var s;(s=this.listeners.get(t))==null||s.forEach(r=>r(e))}removeAll(){this.listeners.clear()}}const k=500,T=600,U=5*60*1e3;class w{constructor(t,e,s,r){this.cachedUser=null,this.cachedSettings=null,this.loaded=!1,this.transport=t,this.tokenManager=e,this.emitter=s,this.baseUrl=r}async load(){var t,e;if(!this.loaded){try{this.cachedSettings=await this.transport.get("/auth/settings")}catch(s){console.warn("[SaaS Support] Failed to load project settings:",s)}if((t=this.tokenManager)!=null&&t.hasRefreshToken())try{await this.performRefresh()}catch{(e=this.tokenManager)==null||e.clearTokens()}this.loaded=!0}}async signIn(t,e){const s=await this.transport.post("/auth/login",{email:t,password:e});if("mfaRequired"in s&&s.mfaRequired)return s;const r=s;return this.setSession(r),r}async signUp(t,e){const s=await this.transport.post("/auth/register",{email:t,password:e});return this.setSession(s),s}async signOut(){var e;const t=(e=this.tokenManager)==null?void 0:e.getRefreshToken();if(t)try{await this.transport.post("/auth/logout",{refreshToken:t})}catch{}this.clearSession()}async signInWithOAuth(t){const e=`${this.baseUrl}/auth/oauth/${t}/popup-callback`,{authUrl:s,state:r}=await this.transport.get(`/auth/oauth/${t}?redirect_uri=${encodeURIComponent(e)}`),i=window.screenX+(window.innerWidth-k)/2,y=window.screenY+(window.innerHeight-T)/2,a=window.open(s,"saas-support-oauth",`width=${k},height=${T},left=${i},top=${y},toolbar=no,menubar=no`);return new Promise((d,c)=>{let o=!1;const u=async h=>{var m;if(((m=h.data)==null?void 0:m.type)==="saas-support:oauth-callback"&&!o){if(o=!0,window.removeEventListener("message",u),clearTimeout(f),clearInterval(p),a==null||a.close(),h.data.error){c(new Error(`OAuth error: ${h.data.error}`));return}try{const l=await this.transport.post(`/auth/oauth/${t}/callback`,{code:h.data.code,state:h.data.state||r});this.setSession(l),d(l)}catch(l){c(l)}}};window.addEventListener("message",u);const f=setTimeout(()=>{o||(o=!0,window.removeEventListener("message",u),clearInterval(p),a==null||a.close(),c(new Error("OAuth popup timed out")))},U),p=setInterval(()=>{a!=null&&a.closed&&!o&&(o=!0,clearInterval(p),clearTimeout(f),window.removeEventListener("message",u),c(new Error("OAuth popup was closed")))},500)})}async submitMfaCode(t,e){const s=await this.transport.post("/auth/login/mfa",{mfaToken:t,code:e});return this.setSession(s),s}async sendMagicLink(t,e){await this.transport.post("/auth/magic-link/send",{email:t,redirectUrl:e})}async verifyMagicLink(t){const e=await this.transport.post("/auth/magic-link/verify",{token:t});return this.setSession(e),e}async sendPasswordReset(t,e){await this.transport.post("/auth/password-reset/send",{email:t,redirectUrl:e})}async resetPassword(t,e){await this.transport.post("/auth/password-reset/verify",{token:t,newPassword:e})}async setupMfa(){return this.transport.post("/auth/mfa/setup",void 0,this.authHeaders())}async verifyMfa(t){return this.transport.post("/auth/mfa/verify",{code:t},this.authHeaders())}async disableMfa(t){await this.transport.post("/auth/mfa/disable",{code:t},this.authHeaders())}async getToken(){var e,s,r;const t=((e=this.tokenManager)==null?void 0:e.getAccessToken())??null;if(t)return t;if((s=this.tokenManager)!=null&&s.hasRefreshToken())try{return await this.performRefresh(),((r=this.tokenManager)==null?void 0:r.getAccessToken())??null}catch{return this.clearSession(),null}return null}async getUser(){if(this.cachedUser)return this.cachedUser;const t=await this.getToken();if(!t)return null;try{return this.cachedUser=await this.transport.get("/auth/me",{Authorization:`Bearer ${t}`}),this.cachedUser}catch{return null}}getUserSync(){return this.cachedUser}isLoaded(){return this.loaded}async getSettings(){if(this.cachedSettings)return this.cachedSettings;try{return this.cachedSettings=await this.transport.get("/auth/settings"),this.cachedSettings}catch{return null}}onAuthStateChange(t){return this.emitter.on("authStateChange",t)}async updateProfile(t){const e=await this.transport.patch("/auth/me",t,this.authHeaders());return this.cachedUser=e,this.emitter.emit("authStateChange",e),e}async changePassword(t,e){await this.transport.post("/auth/change-password",{currentPassword:t,newPassword:e},this.authHeaders())}async listOrgs(){return this.transport.get("/auth/orgs",this.authHeaders())}async createOrg(t,e){return this.transport.post("/auth/orgs",{name:t,slug:e},this.authHeaders())}async getOrg(t){return this.transport.get(`/auth/orgs/${t}`,this.authHeaders())}async updateOrg(t,e){return this.transport.patch(`/auth/orgs/${t}`,e,this.authHeaders())}async deleteOrg(t){await this.transport.del(`/auth/orgs/${t}`,this.authHeaders())}async listMembers(t){return this.transport.get(`/auth/orgs/${t}/members`,this.authHeaders())}async sendInvite(t,e,s){return this.transport.post(`/auth/orgs/${t}/invites`,{email:e,role:s},this.authHeaders())}async updateMemberRole(t,e,s){await this.transport.patch(`/auth/orgs/${t}/members/${e}`,{role:s},this.authHeaders())}async removeMember(t,e){await this.transport.del(`/auth/orgs/${t}/members/${e}`,this.authHeaders())}async acceptInvite(t){return this.transport.post(`/auth/invites/${t}/accept`,void 0,this.authHeaders())}async performRefresh(){var s;const t=(s=this.tokenManager)==null?void 0:s.getRefreshToken();if(!t)throw new Error("No refresh token");const e=await this.transport.post("/auth/refresh",{refreshToken:t});if(this.tokenManager.setTokens(e.accessToken,e.refreshToken),!this.cachedUser)try{this.cachedUser=await this.transport.get("/auth/me",{Authorization:`Bearer ${e.accessToken}`}),this.emitter.emit("authStateChange",this.cachedUser)}catch{}}setSession(t){var e;(e=this.tokenManager)==null||e.setTokens(t.accessToken,t.refreshToken),this.cachedUser=t.user,this.emitter.emit("authStateChange",t.user)}clearSession(){var t;(t=this.tokenManager)==null||t.clearTokens(),this.cachedUser=null,this.emitter.emit("authStateChange",null)}authHeaders(){var e;const t=(e=this.tokenManager)==null?void 0:e.getAccessToken();return t?{Authorization:`Bearer ${t}`}:{}}}class S{constructor(t){this.transport=t}async createCustomer(t){return this.transport.post("/billing/customers",t)}async getCustomer(t){return this.transport.get(`/billing/customers/${t}`)}async updateCustomer(t,e){return this.transport.patch(`/billing/customers/${t}`,e)}async subscribe(t,e){return this.transport.post(`/billing/customers/${t}/subscribe`,{planId:e})}async changePlan(t,e){return this.transport.patch(`/billing/customers/${t}/subscription`,{planId:e})}async cancelSubscription(t){return this.transport.del(`/billing/customers/${t}/subscription`)}async getInvoices(t){return this.transport.get(`/billing/customers/${t}/invoices`)}async ingestUsageEvent(t){return this.transport.post("/billing/events",t)}async getCurrentUsage(t){return this.transport.get(`/billing/customers/${t}/usage`)}async createPortalToken(t,e){return this.transport.post("/billing/portal-tokens",{customerId:t,expiresIn:e})}async applyCoupon(t,e){return this.transport.post(`/billing/customers/${t}/coupon`,{code:e})}}class ${constructor(t){this.transport=t}async executeQuery(t){return this.transport.post("/report/query",t)}async listQueries(t){const e=t?this.toQueryString(t):"";return this.transport.get(`/report/queries${e}`)}async saveQuery(t){return this.transport.post("/report/queries",t)}async updateQuery(t,e){return this.transport.patch(`/report/queries/${t}`,e)}async deleteQuery(t){await this.transport.del(`/report/queries/${t}`)}async listDashboards(t){const e=t?this.toQueryString(t):"";return this.transport.get(`/report/dashboards${e}`)}async createDashboard(t){return this.transport.post("/report/dashboards",t)}async getDashboard(t){return this.transport.get(`/report/dashboards/${t}`)}async updateDashboard(t,e){return this.transport.patch(`/report/dashboards/${t}`,e)}async deleteDashboard(t){await this.transport.del(`/report/dashboards/${t}`)}async createEmbedToken(t){return this.transport.post("/report/embed-tokens",t)}async listEmbedTokens(){return this.transport.get("/report/embed-tokens")}async revokeEmbedToken(t){await this.transport.del(`/report/embed-tokens/${t}`)}toQueryString(t){const e=Object.entries(t).filter(([,s])=>s!=null&&s!=="");return e.length===0?"":"?"+e.map(([s,r])=>`${s}=${encodeURIComponent(String(r))}`).join("&")}}const v="https://api.saas-support.com/v1";class E{constructor(t){if(this.tokenManager=null,this.loaded=!1,!t.publishableKey&&!t.apiKey)throw new Error("SaaSSupport: either publishableKey or apiKey is required");const e=t.baseUrl??v;this.emitter=new M;const s=t.publishableKey?new g(e,{type:"publishableKey",key:t.publishableKey}):null,r=t.apiKey?new g(e,{type:"apiKey",key:t.apiKey}):null;t.publishableKey&&(this.tokenManager=new R(t.publishableKey)),this.auth=new w(s??r,this.tokenManager,this.emitter,e),this.billing=new S(r??s),this.report=new $(r??s),this.tokenManager&&this.tokenManager.setRefreshCallback(()=>this.auth.performRefresh())}async load(){this.loaded||(await this.auth.load(),this.loaded=!0)}isLoaded(){return this.loaded}onError(t){return this.emitter.on("error",t)}destroy(){var t;(t=this.tokenManager)==null||t.destroy(),this.emitter.removeAll()}}function C(n){return"mfaRequired"in n&&n.mfaRequired===!0}exports.AuthClient=w;exports.BillingClient=S;exports.ReportClient=$;exports.SaaSError=b;exports.SaaSSupport=E;exports.Transport=g;exports.isMfaRequired=C;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class f extends Error{constructor(e,t,s="unknown"){super(t),this.name="SaaSError",this.code=e,this.domain=s}get isNotFound(){return this.code===404}get isUnauthorized(){return this.code===401}get isForbidden(){return this.code===403}get isConflict(){return this.code===409}get isRateLimited(){return this.code===429}}class y{constructor(e,t){this.onUnauthorized=null,this.baseUrl=e,this.authMode=t}setUnauthorizedHandler(e){this.onUnauthorized=e}async request(e,t,s,r){try{return await this.doRequest(e,t,s,r)}catch(i){if(i instanceof f&&i.isUnauthorized&&this.onUnauthorized&&(r!=null&&r.Authorization)){const h=await this.onUnauthorized();if(h)return this.doRequest(e,t,s,{...r,Authorization:`Bearer ${h}`})}throw i}}async get(e,t){return this.request("GET",e,void 0,t)}async post(e,t,s){return this.request("POST",e,t,s)}async patch(e,t,s){return this.request("PATCH",e,t,s)}async del(e,t){return this.request("DELETE",e,void 0,t)}async doRequest(e,t,s,r){const i={"Content-Type":"application/json",...this.getAuthHeaders(),...r},n=await(await fetch(`${this.baseUrl}${t}`,{method:e,headers:i,body:s?JSON.stringify(s):void 0})).json();if(n.code&&n.code>=400){const g=this.inferDomain(t);throw new f(n.code,n.message||"Request failed",g)}return n.data}getAuthHeaders(){switch(this.authMode.type){case"publishableKey":case"apiKey":return{"X-API-Key":this.authMode.key};case"portalToken":case"embedToken":return{Authorization:`Bearer ${this.authMode.token}`}}}inferDomain(e){return e.startsWith("/auth")?"auth":e.startsWith("/billing")?"billing":e.startsWith("/report")?"report":"unknown"}}class ${constructor(e){this.accessToken=null,this.refreshToken=null,this.refreshTimer=null,this.refreshInFlight=null,this.onRefreshNeeded=null,this.onTokensChanged=null,this.boundHandleStorage=null,this.storageKey=`ss_rt_${e.slice(0,12)}`,this.refreshToken=this.loadRefreshToken(),typeof window<"u"&&(this.boundHandleStorage=this.handleStorageEvent.bind(this),window.addEventListener("storage",this.boundHandleStorage))}setRefreshCallback(e){this.onRefreshNeeded=e}setTokensChangedCallback(e){this.onTokensChanged=e}getAccessToken(){return this.accessToken}getRefreshToken(){return this.refreshToken}hasRefreshToken(){return this.refreshToken!==null}setTokens(e,t){this.accessToken=e,this.refreshToken=t,this.saveRefreshToken(t),this.scheduleRefresh(e)}clearTokens(){this.accessToken=null,this.refreshToken=null,this.removeRefreshToken(),this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null)}async refreshOnce(){return this.refreshInFlight?this.refreshInFlight:(this.refreshInFlight=this.executeRefresh().finally(()=>{this.refreshInFlight=null}),this.refreshInFlight)}destroy(){this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null),typeof window<"u"&&this.boundHandleStorage&&(window.removeEventListener("storage",this.boundHandleStorage),this.boundHandleStorage=null)}async executeRefresh(){if(!this.onRefreshNeeded)throw new Error("No refresh callback configured");typeof navigator<"u"&&"locks"in navigator?await navigator.locks.request(`ss_refresh_lock_${this.storageKey}`,async()=>{const e=this.loadRefreshToken();e&&e!==this.refreshToken&&(this.refreshToken=e),await this.onRefreshNeeded()}):await this.onRefreshNeeded()}scheduleRefresh(e){this.refreshTimer&&clearTimeout(this.refreshTimer);const t=this.getTokenExpiry(e);if(!t)return;const s=t*1e3-Date.now()-6e4;if(s<=0){this.refreshOnce().catch(()=>{});return}this.refreshTimer=setTimeout(()=>{this.refreshOnce().catch(()=>{})},s)}handleStorageEvent(e){var t;if(e.key===this.storageKey){if(e.newValue===null){this.accessToken=null,this.refreshToken=null,this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null),(t=this.onTokensChanged)==null||t.call(this);return}e.newValue!==this.refreshToken&&(this.refreshToken=e.newValue,this.accessToken=null,this.refreshTimer&&(clearTimeout(this.refreshTimer),this.refreshTimer=null))}}getTokenExpiry(e){try{const t=e.split(".")[1];return JSON.parse(atob(t)).exp??null}catch{return null}}loadRefreshToken(){try{return localStorage.getItem(this.storageKey)}catch{return null}}saveRefreshToken(e){try{localStorage.setItem(this.storageKey,e)}catch{}}removeRefreshToken(){try{localStorage.removeItem(this.storageKey)}catch{}}}class U{constructor(){this.listeners=new Map}on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{var s;(s=this.listeners.get(e))==null||s.delete(t)}}emit(e,t){var s;(s=this.listeners.get(e))==null||s.forEach(r=>r(t))}removeAll(){this.listeners.clear()}}const T=500,w=600,M=5*60*1e3;class b{constructor(e,t,s,r){this.cachedUser=null,this.cachedSettings=null,this.loaded=!1,this.transport=e,this.tokenManager=t,this.emitter=s,this.baseUrl=r}async load(){var e,t;if(!this.loaded){try{this.cachedSettings=await this.transport.get("/auth/settings")}catch(s){console.warn("[SaaS Support] Failed to load project settings:",s)}if((e=this.tokenManager)!=null&&e.hasRefreshToken())try{await this.performRefresh()}catch{(t=this.tokenManager)==null||t.clearTokens()}this.loaded=!0}}async signIn(e,t){const s=await this.transport.post("/auth/login",{email:e,password:t});if("mfaRequired"in s&&s.mfaRequired)return s;const r=s;return this.setSession(r),r}async signUp(e,t){const s=await this.transport.post("/auth/register",{email:e,password:t});return this.setSession(s),s}async signOut(){var t;const e=(t=this.tokenManager)==null?void 0:t.getRefreshToken();if(e)try{await this.transport.post("/auth/logout",{refreshToken:e})}catch{}this.clearSession()}async signInWithOAuth(e){const t=`${this.baseUrl}/auth/oauth/${e}/popup-callback`,{authUrl:s,state:r}=await this.transport.get(`/auth/oauth/${e}?redirect_uri=${encodeURIComponent(t)}`),i=window.screenX+(window.innerWidth-T)/2,h=window.screenY+(window.innerHeight-w)/2,n=window.open(s,"saas-support-oauth",`width=${T},height=${w},left=${i},top=${h},toolbar=no,menubar=no`);return new Promise((g,c)=>{let o=!1;const l=async u=>{var m;if(((m=u.data)==null?void 0:m.type)==="saas-support:oauth-callback"&&!o){if(o=!0,window.removeEventListener("message",l),clearTimeout(k),clearInterval(p),n==null||n.close(),u.data.error){c(new Error(`OAuth error: ${u.data.error}`));return}try{const d=await this.transport.post(`/auth/oauth/${e}/callback`,{code:u.data.code,state:u.data.state||r});this.setSession(d),g(d)}catch(d){c(d)}}};window.addEventListener("message",l);const k=setTimeout(()=>{o||(o=!0,window.removeEventListener("message",l),clearInterval(p),n==null||n.close(),c(new Error("OAuth popup timed out")))},M),p=setInterval(()=>{n!=null&&n.closed&&!o&&(o=!0,clearInterval(p),clearTimeout(k),window.removeEventListener("message",l),c(new Error("OAuth popup was closed")))},500)})}async submitMfaCode(e,t){const s=await this.transport.post("/auth/login/mfa",{mfaToken:e,code:t});return this.setSession(s),s}async sendMagicLink(e,t){await this.transport.post("/auth/magic-link/send",{email:e,redirectUrl:t})}async verifyMagicLink(e){const t=await this.transport.post("/auth/magic-link/verify",{token:e});return this.setSession(t),t}async sendPasswordReset(e,t){await this.transport.post("/auth/password-reset/send",{email:e,redirectUrl:t})}async resetPassword(e,t){await this.transport.post("/auth/password-reset/verify",{token:e,newPassword:t})}async setupMfa(){return this.transport.post("/auth/mfa/setup",void 0,this.authHeaders())}async verifyMfa(e){return this.transport.post("/auth/mfa/verify",{code:e},this.authHeaders())}async disableMfa(e){await this.transport.post("/auth/mfa/disable",{code:e},this.authHeaders())}async getToken(){var t,s,r;const e=((t=this.tokenManager)==null?void 0:t.getAccessToken())??null;if(e)return e;if((s=this.tokenManager)!=null&&s.hasRefreshToken())try{return await this.tokenManager.refreshOnce(),((r=this.tokenManager)==null?void 0:r.getAccessToken())??null}catch{return this.clearSession(),null}return null}async getUser(){if(this.cachedUser)return this.cachedUser;const e=await this.getToken();if(!e)return null;try{return this.cachedUser=await this.transport.get("/auth/me",{Authorization:`Bearer ${e}`}),this.cachedUser}catch{return null}}getUserSync(){return this.cachedUser}isLoaded(){return this.loaded}async getSettings(){if(this.cachedSettings)return this.cachedSettings;try{return this.cachedSettings=await this.transport.get("/auth/settings"),this.cachedSettings}catch{return null}}onAuthStateChange(e){return this.emitter.on("authStateChange",e)}async updateProfile(e){const t=await this.transport.patch("/auth/me",e,this.authHeaders());return this.cachedUser=t,this.emitter.emit("authStateChange",t),t}async changePassword(e,t){await this.transport.post("/auth/change-password",{currentPassword:e,newPassword:t},this.authHeaders())}async listOrgs(){return this.transport.get("/auth/orgs",this.authHeaders())}async createOrg(e,t){return this.transport.post("/auth/orgs",{name:e,slug:t},this.authHeaders())}async getOrg(e){return this.transport.get(`/auth/orgs/${e}`,this.authHeaders())}async updateOrg(e,t){return this.transport.patch(`/auth/orgs/${e}`,t,this.authHeaders())}async deleteOrg(e){await this.transport.del(`/auth/orgs/${e}`,this.authHeaders())}async listMembers(e){return this.transport.get(`/auth/orgs/${e}/members`,this.authHeaders())}async sendInvite(e,t,s){return this.transport.post(`/auth/orgs/${e}/invites`,{email:t,role:s},this.authHeaders())}async updateMemberRole(e,t,s){await this.transport.patch(`/auth/orgs/${e}/members/${t}`,{role:s},this.authHeaders())}async removeMember(e,t){await this.transport.del(`/auth/orgs/${e}/members/${t}`,this.authHeaders())}async acceptInvite(e){return this.transport.post(`/auth/invites/${e}/accept`,void 0,this.authHeaders())}handleExternalLogout(){this.cachedUser=null,this.emitter.emit("authStateChange",null)}async performRefresh(){var s;const e=(s=this.tokenManager)==null?void 0:s.getRefreshToken();if(!e)throw new Error("No refresh token");const t=await this.transport.post("/auth/refresh",{refreshToken:e});if(this.tokenManager.setTokens(t.accessToken,t.refreshToken),!this.cachedUser)try{this.cachedUser=await this.transport.get("/auth/me",{Authorization:`Bearer ${t.accessToken}`}),this.emitter.emit("authStateChange",this.cachedUser)}catch{}}setSession(e){var t;(t=this.tokenManager)==null||t.setTokens(e.accessToken,e.refreshToken),this.cachedUser=e.user,this.emitter.emit("authStateChange",e.user)}clearSession(){var e;(e=this.tokenManager)==null||e.clearTokens(),this.cachedUser=null,this.emitter.emit("authStateChange",null)}authHeaders(){var t;const e=(t=this.tokenManager)==null?void 0:t.getAccessToken();return e?{Authorization:`Bearer ${e}`}:{}}}class S{constructor(e){this.transport=e}async createCustomer(e){return this.transport.post("/billing/customers",e)}async getCustomer(e){return this.transport.get(`/billing/customers/${e}`)}async updateCustomer(e,t){return this.transport.patch(`/billing/customers/${e}`,t)}async subscribe(e,t){return this.transport.post(`/billing/customers/${e}/subscribe`,{planId:t})}async changePlan(e,t){return this.transport.patch(`/billing/customers/${e}/subscription`,{planId:t})}async cancelSubscription(e){return this.transport.del(`/billing/customers/${e}/subscription`)}async getInvoices(e){return this.transport.get(`/billing/customers/${e}/invoices`)}async ingestUsageEvent(e){return this.transport.post("/billing/events",e)}async getCurrentUsage(e){return this.transport.get(`/billing/customers/${e}/usage`)}async createPortalToken(e,t){return this.transport.post("/billing/portal-tokens",{customerId:e,expiresIn:t})}async applyCoupon(e,t){return this.transport.post(`/billing/customers/${e}/coupon`,{code:t})}}class R{constructor(e){this.transport=e}async executeQuery(e){return this.transport.post("/report/query",e)}async listQueries(e){const t=e?this.toQueryString(e):"";return this.transport.get(`/report/queries${t}`)}async saveQuery(e){return this.transport.post("/report/queries",e)}async updateQuery(e,t){return this.transport.patch(`/report/queries/${e}`,t)}async deleteQuery(e){await this.transport.del(`/report/queries/${e}`)}async listDashboards(e){const t=e?this.toQueryString(e):"";return this.transport.get(`/report/dashboards${t}`)}async createDashboard(e){return this.transport.post("/report/dashboards",e)}async getDashboard(e){return this.transport.get(`/report/dashboards/${e}`)}async updateDashboard(e,t){return this.transport.patch(`/report/dashboards/${e}`,t)}async deleteDashboard(e){await this.transport.del(`/report/dashboards/${e}`)}async createEmbedToken(e){return this.transport.post("/report/embed-tokens",e)}async listEmbedTokens(){return this.transport.get("/report/embed-tokens")}async revokeEmbedToken(e){await this.transport.del(`/report/embed-tokens/${e}`)}toQueryString(e){const t=Object.entries(e).filter(([,s])=>s!=null&&s!=="");return t.length===0?"":"?"+t.map(([s,r])=>`${s}=${encodeURIComponent(String(r))}`).join("&")}}const E="https://api.saas-support.com/v1";class v{constructor(e){if(this.tokenManager=null,this.loaded=!1,!e.publishableKey&&!e.apiKey)throw new Error("SaaSSupport: either publishableKey or apiKey is required");const t=e.baseUrl??E;this.emitter=new U;const s=e.publishableKey?new y(t,{type:"publishableKey",key:e.publishableKey}):null,r=e.apiKey?new y(t,{type:"apiKey",key:e.apiKey}):null;e.publishableKey&&(this.tokenManager=new $(e.publishableKey)),this.auth=new b(s??r,this.tokenManager,this.emitter,t),this.billing=new S(r??s),this.report=new R(r??s),this.tokenManager&&(this.tokenManager.setRefreshCallback(()=>this.auth.performRefresh()),this.tokenManager.setTokensChangedCallback(()=>{this.tokenManager.hasRefreshToken()||this.auth.handleExternalLogout()})),this.tokenManager&&s&&s.setUnauthorizedHandler(async()=>{try{return await this.tokenManager.refreshOnce(),this.tokenManager.getAccessToken()}catch{return null}})}async load(){this.loaded||(await this.auth.load(),this.loaded=!0)}isLoaded(){return this.loaded}onError(e){return this.emitter.on("error",e)}destroy(){var e;(e=this.tokenManager)==null||e.destroy(),this.emitter.removeAll()}}function C(a){return"mfaRequired"in a&&a.mfaRequired===!0}exports.AuthClient=b;exports.BillingClient=S;exports.ReportClient=R;exports.SaaSError=f;exports.SaaSSupport=v;exports.Transport=y;exports.isMfaRequired=C;
|
package/dist/index.d.ts
CHANGED
|
@@ -61,6 +61,7 @@ export declare class AuthClient {
|
|
|
61
61
|
orgId: string;
|
|
62
62
|
role: string;
|
|
63
63
|
}>;
|
|
64
|
+
/* Excluded from this release type: handleExternalLogout */
|
|
64
65
|
/* Excluded from this release type: performRefresh */
|
|
65
66
|
private setSession;
|
|
66
67
|
private clearSession;
|
|
@@ -449,17 +450,28 @@ declare class TokenManager {
|
|
|
449
450
|
private accessToken;
|
|
450
451
|
private refreshToken;
|
|
451
452
|
private refreshTimer;
|
|
453
|
+
private refreshInFlight;
|
|
452
454
|
private storageKey;
|
|
453
455
|
private onRefreshNeeded;
|
|
456
|
+
private onTokensChanged;
|
|
457
|
+
private boundHandleStorage;
|
|
454
458
|
constructor(keyPrefix: string);
|
|
455
459
|
setRefreshCallback(cb: () => Promise<void>): void;
|
|
460
|
+
setTokensChangedCallback(cb: () => void): void;
|
|
456
461
|
getAccessToken(): string | null;
|
|
457
462
|
getRefreshToken(): string | null;
|
|
458
463
|
hasRefreshToken(): boolean;
|
|
459
464
|
setTokens(accessToken: string, refreshToken: string): void;
|
|
460
465
|
clearTokens(): void;
|
|
466
|
+
/**
|
|
467
|
+
* Coalesces concurrent refresh calls within this tab and coordinates
|
|
468
|
+
* across tabs via Web Locks API (when available).
|
|
469
|
+
*/
|
|
470
|
+
refreshOnce(): Promise<void>;
|
|
461
471
|
destroy(): void;
|
|
472
|
+
private executeRefresh;
|
|
462
473
|
private scheduleRefresh;
|
|
474
|
+
private handleStorageEvent;
|
|
463
475
|
private getTokenExpiry;
|
|
464
476
|
private loadRefreshToken;
|
|
465
477
|
private saveRefreshToken;
|
|
@@ -469,12 +481,16 @@ declare class TokenManager {
|
|
|
469
481
|
export declare class Transport {
|
|
470
482
|
private baseUrl;
|
|
471
483
|
private authMode;
|
|
484
|
+
private onUnauthorized;
|
|
472
485
|
constructor(baseUrl: string, authMode: AuthMode);
|
|
486
|
+
/** Register a handler that refreshes tokens and returns a new access token, or null. */
|
|
487
|
+
setUnauthorizedHandler(handler: () => Promise<string | null>): void;
|
|
473
488
|
request<T>(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
474
489
|
get<T>(path: string, headers?: Record<string, string>): Promise<T>;
|
|
475
490
|
post<T>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
476
491
|
patch<T>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
477
492
|
del<T>(path: string, headers?: Record<string, string>): Promise<T>;
|
|
493
|
+
private doRequest;
|
|
478
494
|
private getAuthHeaders;
|
|
479
495
|
private inferDomain;
|
|
480
496
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
class
|
|
2
|
-
constructor(
|
|
3
|
-
super(
|
|
1
|
+
class k extends Error {
|
|
2
|
+
constructor(e, t, s = "unknown") {
|
|
3
|
+
super(t), this.name = "SaaSError", this.code = e, this.domain = s;
|
|
4
4
|
}
|
|
5
5
|
get isNotFound() {
|
|
6
6
|
return this.code === 404;
|
|
@@ -19,36 +19,55 @@ class b extends Error {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
class m {
|
|
22
|
-
constructor(
|
|
23
|
-
this.baseUrl =
|
|
22
|
+
constructor(e, t) {
|
|
23
|
+
this.onUnauthorized = null, this.baseUrl = e, this.authMode = t;
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
/** Register a handler that refreshes tokens and returns a new access token, or null. */
|
|
26
|
+
setUnauthorizedHandler(e) {
|
|
27
|
+
this.onUnauthorized = e;
|
|
28
|
+
}
|
|
29
|
+
async request(e, t, s, r) {
|
|
30
|
+
try {
|
|
31
|
+
return await this.doRequest(e, t, s, r);
|
|
32
|
+
} catch (i) {
|
|
33
|
+
if (i instanceof k && i.isUnauthorized && this.onUnauthorized && (r != null && r.Authorization)) {
|
|
34
|
+
const h = await this.onUnauthorized();
|
|
35
|
+
if (h)
|
|
36
|
+
return this.doRequest(e, t, s, {
|
|
37
|
+
...r,
|
|
38
|
+
Authorization: `Bearer ${h}`
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
throw i;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async get(e, t) {
|
|
45
|
+
return this.request("GET", e, void 0, t);
|
|
46
|
+
}
|
|
47
|
+
async post(e, t, s) {
|
|
48
|
+
return this.request("POST", e, t, s);
|
|
49
|
+
}
|
|
50
|
+
async patch(e, t, s) {
|
|
51
|
+
return this.request("PATCH", e, t, s);
|
|
52
|
+
}
|
|
53
|
+
async del(e, t) {
|
|
54
|
+
return this.request("DELETE", e, void 0, t);
|
|
55
|
+
}
|
|
56
|
+
async doRequest(e, t, s, r) {
|
|
26
57
|
const i = {
|
|
27
58
|
"Content-Type": "application/json",
|
|
28
59
|
...this.getAuthHeaders(),
|
|
29
60
|
...r
|
|
30
|
-
},
|
|
31
|
-
method:
|
|
61
|
+
}, n = await (await fetch(`${this.baseUrl}${t}`, {
|
|
62
|
+
method: e,
|
|
32
63
|
headers: i,
|
|
33
64
|
body: s ? JSON.stringify(s) : void 0
|
|
34
65
|
})).json();
|
|
35
|
-
if (
|
|
36
|
-
const
|
|
37
|
-
throw new
|
|
66
|
+
if (n.code && n.code >= 400) {
|
|
67
|
+
const g = this.inferDomain(t);
|
|
68
|
+
throw new k(n.code, n.message || "Request failed", g);
|
|
38
69
|
}
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
async get(t, e) {
|
|
42
|
-
return this.request("GET", t, void 0, e);
|
|
43
|
-
}
|
|
44
|
-
async post(t, e, s) {
|
|
45
|
-
return this.request("POST", t, e, s);
|
|
46
|
-
}
|
|
47
|
-
async patch(t, e, s) {
|
|
48
|
-
return this.request("PATCH", t, e, s);
|
|
49
|
-
}
|
|
50
|
-
async del(t, e) {
|
|
51
|
-
return this.request("DELETE", t, void 0, e);
|
|
70
|
+
return n.data;
|
|
52
71
|
}
|
|
53
72
|
getAuthHeaders() {
|
|
54
73
|
switch (this.authMode.type) {
|
|
@@ -60,16 +79,19 @@ class m {
|
|
|
60
79
|
return { Authorization: `Bearer ${this.authMode.token}` };
|
|
61
80
|
}
|
|
62
81
|
}
|
|
63
|
-
inferDomain(
|
|
64
|
-
return
|
|
82
|
+
inferDomain(e) {
|
|
83
|
+
return e.startsWith("/auth") ? "auth" : e.startsWith("/billing") ? "billing" : e.startsWith("/report") ? "report" : "unknown";
|
|
65
84
|
}
|
|
66
85
|
}
|
|
67
|
-
class
|
|
68
|
-
constructor(
|
|
69
|
-
this.accessToken = null, this.refreshToken = null, this.refreshTimer = null, this.onRefreshNeeded = null, this.storageKey = `ss_rt_${
|
|
86
|
+
class b {
|
|
87
|
+
constructor(e) {
|
|
88
|
+
this.accessToken = null, this.refreshToken = null, this.refreshTimer = null, this.refreshInFlight = null, this.onRefreshNeeded = null, this.onTokensChanged = null, this.boundHandleStorage = null, this.storageKey = `ss_rt_${e.slice(0, 12)}`, this.refreshToken = this.loadRefreshToken(), typeof window < "u" && (this.boundHandleStorage = this.handleStorageEvent.bind(this), window.addEventListener("storage", this.boundHandleStorage));
|
|
89
|
+
}
|
|
90
|
+
setRefreshCallback(e) {
|
|
91
|
+
this.onRefreshNeeded = e;
|
|
70
92
|
}
|
|
71
|
-
|
|
72
|
-
this.
|
|
93
|
+
setTokensChangedCallback(e) {
|
|
94
|
+
this.onTokensChanged = e;
|
|
73
95
|
}
|
|
74
96
|
getAccessToken() {
|
|
75
97
|
return this.accessToken;
|
|
@@ -80,34 +102,64 @@ class w {
|
|
|
80
102
|
hasRefreshToken() {
|
|
81
103
|
return this.refreshToken !== null;
|
|
82
104
|
}
|
|
83
|
-
setTokens(
|
|
84
|
-
this.accessToken =
|
|
105
|
+
setTokens(e, t) {
|
|
106
|
+
this.accessToken = e, this.refreshToken = t, this.saveRefreshToken(t), this.scheduleRefresh(e);
|
|
85
107
|
}
|
|
86
108
|
clearTokens() {
|
|
87
109
|
this.accessToken = null, this.refreshToken = null, this.removeRefreshToken(), this.refreshTimer && (clearTimeout(this.refreshTimer), this.refreshTimer = null);
|
|
88
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Coalesces concurrent refresh calls within this tab and coordinates
|
|
113
|
+
* across tabs via Web Locks API (when available).
|
|
114
|
+
*/
|
|
115
|
+
async refreshOnce() {
|
|
116
|
+
return this.refreshInFlight ? this.refreshInFlight : (this.refreshInFlight = this.executeRefresh().finally(() => {
|
|
117
|
+
this.refreshInFlight = null;
|
|
118
|
+
}), this.refreshInFlight);
|
|
119
|
+
}
|
|
89
120
|
destroy() {
|
|
90
|
-
this.refreshTimer && (clearTimeout(this.refreshTimer), this.refreshTimer = null);
|
|
121
|
+
this.refreshTimer && (clearTimeout(this.refreshTimer), this.refreshTimer = null), typeof window < "u" && this.boundHandleStorage && (window.removeEventListener("storage", this.boundHandleStorage), this.boundHandleStorage = null);
|
|
122
|
+
}
|
|
123
|
+
async executeRefresh() {
|
|
124
|
+
if (!this.onRefreshNeeded)
|
|
125
|
+
throw new Error("No refresh callback configured");
|
|
126
|
+
typeof navigator < "u" && "locks" in navigator ? await navigator.locks.request(
|
|
127
|
+
`ss_refresh_lock_${this.storageKey}`,
|
|
128
|
+
async () => {
|
|
129
|
+
const e = this.loadRefreshToken();
|
|
130
|
+
e && e !== this.refreshToken && (this.refreshToken = e), await this.onRefreshNeeded();
|
|
131
|
+
}
|
|
132
|
+
) : await this.onRefreshNeeded();
|
|
91
133
|
}
|
|
92
|
-
scheduleRefresh(
|
|
93
|
-
var r;
|
|
134
|
+
scheduleRefresh(e) {
|
|
94
135
|
this.refreshTimer && clearTimeout(this.refreshTimer);
|
|
95
|
-
const
|
|
96
|
-
if (!
|
|
97
|
-
const s =
|
|
136
|
+
const t = this.getTokenExpiry(e);
|
|
137
|
+
if (!t) return;
|
|
138
|
+
const s = t * 1e3 - Date.now() - 6e4;
|
|
98
139
|
if (s <= 0) {
|
|
99
|
-
|
|
140
|
+
this.refreshOnce().catch(() => {
|
|
141
|
+
});
|
|
100
142
|
return;
|
|
101
143
|
}
|
|
102
144
|
this.refreshTimer = setTimeout(() => {
|
|
103
|
-
|
|
104
|
-
|
|
145
|
+
this.refreshOnce().catch(() => {
|
|
146
|
+
});
|
|
105
147
|
}, s);
|
|
106
148
|
}
|
|
107
|
-
|
|
149
|
+
handleStorageEvent(e) {
|
|
150
|
+
var t;
|
|
151
|
+
if (e.key === this.storageKey) {
|
|
152
|
+
if (e.newValue === null) {
|
|
153
|
+
this.accessToken = null, this.refreshToken = null, this.refreshTimer && (clearTimeout(this.refreshTimer), this.refreshTimer = null), (t = this.onTokensChanged) == null || t.call(this);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
e.newValue !== this.refreshToken && (this.refreshToken = e.newValue, this.accessToken = null, this.refreshTimer && (clearTimeout(this.refreshTimer), this.refreshTimer = null));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
getTokenExpiry(e) {
|
|
108
160
|
try {
|
|
109
|
-
const
|
|
110
|
-
return JSON.parse(atob(
|
|
161
|
+
const t = e.split(".")[1];
|
|
162
|
+
return JSON.parse(atob(t)).exp ?? null;
|
|
111
163
|
} catch {
|
|
112
164
|
return null;
|
|
113
165
|
}
|
|
@@ -119,9 +171,9 @@ class w {
|
|
|
119
171
|
return null;
|
|
120
172
|
}
|
|
121
173
|
}
|
|
122
|
-
saveRefreshToken(
|
|
174
|
+
saveRefreshToken(e) {
|
|
123
175
|
try {
|
|
124
|
-
localStorage.setItem(this.storageKey,
|
|
176
|
+
localStorage.setItem(this.storageKey, e);
|
|
125
177
|
} catch {
|
|
126
178
|
}
|
|
127
179
|
}
|
|
@@ -136,41 +188,41 @@ class S {
|
|
|
136
188
|
constructor() {
|
|
137
189
|
this.listeners = /* @__PURE__ */ new Map();
|
|
138
190
|
}
|
|
139
|
-
on(
|
|
140
|
-
return this.listeners.has(
|
|
191
|
+
on(e, t) {
|
|
192
|
+
return this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set()), this.listeners.get(e).add(t), () => {
|
|
141
193
|
var s;
|
|
142
|
-
(s = this.listeners.get(
|
|
194
|
+
(s = this.listeners.get(e)) == null || s.delete(t);
|
|
143
195
|
};
|
|
144
196
|
}
|
|
145
|
-
emit(
|
|
197
|
+
emit(e, t) {
|
|
146
198
|
var s;
|
|
147
|
-
(s = this.listeners.get(
|
|
199
|
+
(s = this.listeners.get(e)) == null || s.forEach((r) => r(t));
|
|
148
200
|
}
|
|
149
201
|
removeAll() {
|
|
150
202
|
this.listeners.clear();
|
|
151
203
|
}
|
|
152
204
|
}
|
|
153
|
-
const
|
|
205
|
+
const T = 500, w = 600, $ = 5 * 60 * 1e3;
|
|
154
206
|
class R {
|
|
155
|
-
constructor(
|
|
156
|
-
this.cachedUser = null, this.cachedSettings = null, this.loaded = !1, this.transport =
|
|
207
|
+
constructor(e, t, s, r) {
|
|
208
|
+
this.cachedUser = null, this.cachedSettings = null, this.loaded = !1, this.transport = e, this.tokenManager = t, this.emitter = s, this.baseUrl = r;
|
|
157
209
|
}
|
|
158
210
|
// ---------------------------------------------------------------------------
|
|
159
211
|
// Lifecycle
|
|
160
212
|
// ---------------------------------------------------------------------------
|
|
161
213
|
async load() {
|
|
162
|
-
var
|
|
214
|
+
var e, t;
|
|
163
215
|
if (!this.loaded) {
|
|
164
216
|
try {
|
|
165
217
|
this.cachedSettings = await this.transport.get("/auth/settings");
|
|
166
218
|
} catch (s) {
|
|
167
219
|
console.warn("[SaaS Support] Failed to load project settings:", s);
|
|
168
220
|
}
|
|
169
|
-
if ((
|
|
221
|
+
if ((e = this.tokenManager) != null && e.hasRefreshToken())
|
|
170
222
|
try {
|
|
171
223
|
await this.performRefresh();
|
|
172
224
|
} catch {
|
|
173
|
-
(
|
|
225
|
+
(t = this.tokenManager) == null || t.clearTokens();
|
|
174
226
|
}
|
|
175
227
|
this.loaded = !0;
|
|
176
228
|
}
|
|
@@ -178,82 +230,82 @@ class R {
|
|
|
178
230
|
// ---------------------------------------------------------------------------
|
|
179
231
|
// Core auth operations
|
|
180
232
|
// ---------------------------------------------------------------------------
|
|
181
|
-
async signIn(
|
|
182
|
-
const s = await this.transport.post("/auth/login", { email:
|
|
233
|
+
async signIn(e, t) {
|
|
234
|
+
const s = await this.transport.post("/auth/login", { email: e, password: t });
|
|
183
235
|
if ("mfaRequired" in s && s.mfaRequired)
|
|
184
236
|
return s;
|
|
185
237
|
const r = s;
|
|
186
238
|
return this.setSession(r), r;
|
|
187
239
|
}
|
|
188
|
-
async signUp(
|
|
189
|
-
const s = await this.transport.post("/auth/register", { email:
|
|
240
|
+
async signUp(e, t) {
|
|
241
|
+
const s = await this.transport.post("/auth/register", { email: e, password: t });
|
|
190
242
|
return this.setSession(s), s;
|
|
191
243
|
}
|
|
192
244
|
async signOut() {
|
|
193
|
-
var
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
245
|
+
var t;
|
|
246
|
+
const e = (t = this.tokenManager) == null ? void 0 : t.getRefreshToken();
|
|
247
|
+
if (e)
|
|
196
248
|
try {
|
|
197
|
-
await this.transport.post("/auth/logout", { refreshToken:
|
|
249
|
+
await this.transport.post("/auth/logout", { refreshToken: e });
|
|
198
250
|
} catch {
|
|
199
251
|
}
|
|
200
252
|
this.clearSession();
|
|
201
253
|
}
|
|
202
|
-
async signInWithOAuth(
|
|
203
|
-
const
|
|
204
|
-
`/auth/oauth/${
|
|
205
|
-
), i = window.screenX + (window.innerWidth -
|
|
254
|
+
async signInWithOAuth(e) {
|
|
255
|
+
const t = `${this.baseUrl}/auth/oauth/${e}/popup-callback`, { authUrl: s, state: r } = await this.transport.get(
|
|
256
|
+
`/auth/oauth/${e}?redirect_uri=${encodeURIComponent(t)}`
|
|
257
|
+
), i = window.screenX + (window.innerWidth - T) / 2, h = window.screenY + (window.innerHeight - w) / 2, n = window.open(
|
|
206
258
|
s,
|
|
207
259
|
"saas-support-oauth",
|
|
208
|
-
`width=${
|
|
260
|
+
`width=${T},height=${w},left=${i},top=${h},toolbar=no,menubar=no`
|
|
209
261
|
);
|
|
210
|
-
return new Promise((
|
|
262
|
+
return new Promise((g, c) => {
|
|
211
263
|
let o = !1;
|
|
212
|
-
const
|
|
213
|
-
var
|
|
214
|
-
if (((
|
|
215
|
-
if (o = !0, window.removeEventListener("message",
|
|
216
|
-
c(new Error(`OAuth error: ${
|
|
264
|
+
const l = async (u) => {
|
|
265
|
+
var y;
|
|
266
|
+
if (((y = u.data) == null ? void 0 : y.type) === "saas-support:oauth-callback" && !o) {
|
|
267
|
+
if (o = !0, window.removeEventListener("message", l), clearTimeout(f), clearInterval(p), n == null || n.close(), u.data.error) {
|
|
268
|
+
c(new Error(`OAuth error: ${u.data.error}`));
|
|
217
269
|
return;
|
|
218
270
|
}
|
|
219
271
|
try {
|
|
220
|
-
const
|
|
221
|
-
`/auth/oauth/${
|
|
222
|
-
{ code:
|
|
272
|
+
const d = await this.transport.post(
|
|
273
|
+
`/auth/oauth/${e}/callback`,
|
|
274
|
+
{ code: u.data.code, state: u.data.state || r }
|
|
223
275
|
);
|
|
224
|
-
this.setSession(
|
|
225
|
-
} catch (
|
|
226
|
-
c(
|
|
276
|
+
this.setSession(d), g(d);
|
|
277
|
+
} catch (d) {
|
|
278
|
+
c(d);
|
|
227
279
|
}
|
|
228
280
|
}
|
|
229
281
|
};
|
|
230
|
-
window.addEventListener("message",
|
|
231
|
-
const
|
|
232
|
-
o || (o = !0, window.removeEventListener("message",
|
|
282
|
+
window.addEventListener("message", l);
|
|
283
|
+
const f = setTimeout(() => {
|
|
284
|
+
o || (o = !0, window.removeEventListener("message", l), clearInterval(p), n == null || n.close(), c(new Error("OAuth popup timed out")));
|
|
233
285
|
}, $), p = setInterval(() => {
|
|
234
|
-
|
|
286
|
+
n != null && n.closed && !o && (o = !0, clearInterval(p), clearTimeout(f), window.removeEventListener("message", l), c(new Error("OAuth popup was closed")));
|
|
235
287
|
}, 500);
|
|
236
288
|
});
|
|
237
289
|
}
|
|
238
|
-
async submitMfaCode(
|
|
239
|
-
const s = await this.transport.post("/auth/login/mfa", { mfaToken:
|
|
290
|
+
async submitMfaCode(e, t) {
|
|
291
|
+
const s = await this.transport.post("/auth/login/mfa", { mfaToken: e, code: t });
|
|
240
292
|
return this.setSession(s), s;
|
|
241
293
|
}
|
|
242
294
|
// ---------------------------------------------------------------------------
|
|
243
295
|
// Magic link & password reset
|
|
244
296
|
// ---------------------------------------------------------------------------
|
|
245
|
-
async sendMagicLink(
|
|
246
|
-
await this.transport.post("/auth/magic-link/send", { email:
|
|
297
|
+
async sendMagicLink(e, t) {
|
|
298
|
+
await this.transport.post("/auth/magic-link/send", { email: e, redirectUrl: t });
|
|
247
299
|
}
|
|
248
|
-
async verifyMagicLink(
|
|
249
|
-
const
|
|
250
|
-
return this.setSession(
|
|
300
|
+
async verifyMagicLink(e) {
|
|
301
|
+
const t = await this.transport.post("/auth/magic-link/verify", { token: e });
|
|
302
|
+
return this.setSession(t), t;
|
|
251
303
|
}
|
|
252
|
-
async sendPasswordReset(
|
|
253
|
-
await this.transport.post("/auth/password-reset/send", { email:
|
|
304
|
+
async sendPasswordReset(e, t) {
|
|
305
|
+
await this.transport.post("/auth/password-reset/send", { email: e, redirectUrl: t });
|
|
254
306
|
}
|
|
255
|
-
async resetPassword(
|
|
256
|
-
await this.transport.post("/auth/password-reset/verify", { token:
|
|
307
|
+
async resetPassword(e, t) {
|
|
308
|
+
await this.transport.post("/auth/password-reset/verify", { token: e, newPassword: t });
|
|
257
309
|
}
|
|
258
310
|
// ---------------------------------------------------------------------------
|
|
259
311
|
// MFA management
|
|
@@ -261,22 +313,22 @@ class R {
|
|
|
261
313
|
async setupMfa() {
|
|
262
314
|
return this.transport.post("/auth/mfa/setup", void 0, this.authHeaders());
|
|
263
315
|
}
|
|
264
|
-
async verifyMfa(
|
|
265
|
-
return this.transport.post("/auth/mfa/verify", { code:
|
|
316
|
+
async verifyMfa(e) {
|
|
317
|
+
return this.transport.post("/auth/mfa/verify", { code: e }, this.authHeaders());
|
|
266
318
|
}
|
|
267
|
-
async disableMfa(
|
|
268
|
-
await this.transport.post("/auth/mfa/disable", { code:
|
|
319
|
+
async disableMfa(e) {
|
|
320
|
+
await this.transport.post("/auth/mfa/disable", { code: e }, this.authHeaders());
|
|
269
321
|
}
|
|
270
322
|
// ---------------------------------------------------------------------------
|
|
271
323
|
// Token & user access
|
|
272
324
|
// ---------------------------------------------------------------------------
|
|
273
325
|
async getToken() {
|
|
274
|
-
var
|
|
275
|
-
const
|
|
276
|
-
if (
|
|
326
|
+
var t, s, r;
|
|
327
|
+
const e = ((t = this.tokenManager) == null ? void 0 : t.getAccessToken()) ?? null;
|
|
328
|
+
if (e) return e;
|
|
277
329
|
if ((s = this.tokenManager) != null && s.hasRefreshToken())
|
|
278
330
|
try {
|
|
279
|
-
return await this.
|
|
331
|
+
return await this.tokenManager.refreshOnce(), ((r = this.tokenManager) == null ? void 0 : r.getAccessToken()) ?? null;
|
|
280
332
|
} catch {
|
|
281
333
|
return this.clearSession(), null;
|
|
282
334
|
}
|
|
@@ -284,10 +336,10 @@ class R {
|
|
|
284
336
|
}
|
|
285
337
|
async getUser() {
|
|
286
338
|
if (this.cachedUser) return this.cachedUser;
|
|
287
|
-
const
|
|
288
|
-
if (!
|
|
339
|
+
const e = await this.getToken();
|
|
340
|
+
if (!e) return null;
|
|
289
341
|
try {
|
|
290
|
-
return this.cachedUser = await this.transport.get("/auth/me", { Authorization: `Bearer ${
|
|
342
|
+
return this.cachedUser = await this.transport.get("/auth/me", { Authorization: `Bearer ${e}` }), this.cachedUser;
|
|
291
343
|
} catch {
|
|
292
344
|
return null;
|
|
293
345
|
}
|
|
@@ -306,18 +358,18 @@ class R {
|
|
|
306
358
|
return null;
|
|
307
359
|
}
|
|
308
360
|
}
|
|
309
|
-
onAuthStateChange(
|
|
310
|
-
return this.emitter.on("authStateChange",
|
|
361
|
+
onAuthStateChange(e) {
|
|
362
|
+
return this.emitter.on("authStateChange", e);
|
|
311
363
|
}
|
|
312
364
|
// ---------------------------------------------------------------------------
|
|
313
365
|
// Profile
|
|
314
366
|
// ---------------------------------------------------------------------------
|
|
315
|
-
async updateProfile(
|
|
316
|
-
const
|
|
317
|
-
return this.cachedUser =
|
|
367
|
+
async updateProfile(e) {
|
|
368
|
+
const t = await this.transport.patch("/auth/me", e, this.authHeaders());
|
|
369
|
+
return this.cachedUser = t, this.emitter.emit("authStateChange", t), t;
|
|
318
370
|
}
|
|
319
|
-
async changePassword(
|
|
320
|
-
await this.transport.post("/auth/change-password", { currentPassword:
|
|
371
|
+
async changePassword(e, t) {
|
|
372
|
+
await this.transport.post("/auth/change-password", { currentPassword: e, newPassword: t }, this.authHeaders());
|
|
321
373
|
}
|
|
322
374
|
// ---------------------------------------------------------------------------
|
|
323
375
|
// Organizations
|
|
@@ -325,180 +377,192 @@ class R {
|
|
|
325
377
|
async listOrgs() {
|
|
326
378
|
return this.transport.get("/auth/orgs", this.authHeaders());
|
|
327
379
|
}
|
|
328
|
-
async createOrg(
|
|
329
|
-
return this.transport.post("/auth/orgs", { name:
|
|
380
|
+
async createOrg(e, t) {
|
|
381
|
+
return this.transport.post("/auth/orgs", { name: e, slug: t }, this.authHeaders());
|
|
330
382
|
}
|
|
331
|
-
async getOrg(
|
|
332
|
-
return this.transport.get(`/auth/orgs/${
|
|
383
|
+
async getOrg(e) {
|
|
384
|
+
return this.transport.get(`/auth/orgs/${e}`, this.authHeaders());
|
|
333
385
|
}
|
|
334
|
-
async updateOrg(
|
|
335
|
-
return this.transport.patch(`/auth/orgs/${
|
|
386
|
+
async updateOrg(e, t) {
|
|
387
|
+
return this.transport.patch(`/auth/orgs/${e}`, t, this.authHeaders());
|
|
336
388
|
}
|
|
337
|
-
async deleteOrg(
|
|
338
|
-
await this.transport.del(`/auth/orgs/${
|
|
389
|
+
async deleteOrg(e) {
|
|
390
|
+
await this.transport.del(`/auth/orgs/${e}`, this.authHeaders());
|
|
339
391
|
}
|
|
340
392
|
// ---------------------------------------------------------------------------
|
|
341
393
|
// Members & Invites
|
|
342
394
|
// ---------------------------------------------------------------------------
|
|
343
|
-
async listMembers(
|
|
344
|
-
return this.transport.get(`/auth/orgs/${
|
|
395
|
+
async listMembers(e) {
|
|
396
|
+
return this.transport.get(`/auth/orgs/${e}/members`, this.authHeaders());
|
|
345
397
|
}
|
|
346
|
-
async sendInvite(
|
|
347
|
-
return this.transport.post(`/auth/orgs/${
|
|
398
|
+
async sendInvite(e, t, s) {
|
|
399
|
+
return this.transport.post(`/auth/orgs/${e}/invites`, { email: t, role: s }, this.authHeaders());
|
|
348
400
|
}
|
|
349
|
-
async updateMemberRole(
|
|
350
|
-
await this.transport.patch(`/auth/orgs/${
|
|
401
|
+
async updateMemberRole(e, t, s) {
|
|
402
|
+
await this.transport.patch(`/auth/orgs/${e}/members/${t}`, { role: s }, this.authHeaders());
|
|
351
403
|
}
|
|
352
|
-
async removeMember(
|
|
353
|
-
await this.transport.del(`/auth/orgs/${
|
|
404
|
+
async removeMember(e, t) {
|
|
405
|
+
await this.transport.del(`/auth/orgs/${e}/members/${t}`, this.authHeaders());
|
|
354
406
|
}
|
|
355
|
-
async acceptInvite(
|
|
356
|
-
return this.transport.post(`/auth/invites/${
|
|
407
|
+
async acceptInvite(e) {
|
|
408
|
+
return this.transport.post(`/auth/invites/${e}/accept`, void 0, this.authHeaders());
|
|
357
409
|
}
|
|
358
410
|
// ---------------------------------------------------------------------------
|
|
359
411
|
// Internal
|
|
360
412
|
// ---------------------------------------------------------------------------
|
|
413
|
+
/** @internal Called when another tab logs out (via storage event). */
|
|
414
|
+
handleExternalLogout() {
|
|
415
|
+
this.cachedUser = null, this.emitter.emit("authStateChange", null);
|
|
416
|
+
}
|
|
361
417
|
/** @internal */
|
|
362
418
|
async performRefresh() {
|
|
363
419
|
var s;
|
|
364
|
-
const
|
|
365
|
-
if (!
|
|
366
|
-
const
|
|
420
|
+
const e = (s = this.tokenManager) == null ? void 0 : s.getRefreshToken();
|
|
421
|
+
if (!e) throw new Error("No refresh token");
|
|
422
|
+
const t = await this.transport.post(
|
|
367
423
|
"/auth/refresh",
|
|
368
|
-
{ refreshToken:
|
|
424
|
+
{ refreshToken: e }
|
|
369
425
|
);
|
|
370
|
-
if (this.tokenManager.setTokens(
|
|
426
|
+
if (this.tokenManager.setTokens(t.accessToken, t.refreshToken), !this.cachedUser)
|
|
371
427
|
try {
|
|
372
|
-
this.cachedUser = await this.transport.get("/auth/me", { Authorization: `Bearer ${
|
|
428
|
+
this.cachedUser = await this.transport.get("/auth/me", { Authorization: `Bearer ${t.accessToken}` }), this.emitter.emit("authStateChange", this.cachedUser);
|
|
373
429
|
} catch {
|
|
374
430
|
}
|
|
375
431
|
}
|
|
376
|
-
setSession(
|
|
377
|
-
var
|
|
378
|
-
(
|
|
432
|
+
setSession(e) {
|
|
433
|
+
var t;
|
|
434
|
+
(t = this.tokenManager) == null || t.setTokens(e.accessToken, e.refreshToken), this.cachedUser = e.user, this.emitter.emit("authStateChange", e.user);
|
|
379
435
|
}
|
|
380
436
|
clearSession() {
|
|
381
|
-
var
|
|
382
|
-
(
|
|
437
|
+
var e;
|
|
438
|
+
(e = this.tokenManager) == null || e.clearTokens(), this.cachedUser = null, this.emitter.emit("authStateChange", null);
|
|
383
439
|
}
|
|
384
440
|
authHeaders() {
|
|
385
|
-
var
|
|
386
|
-
const
|
|
387
|
-
return
|
|
441
|
+
var t;
|
|
442
|
+
const e = (t = this.tokenManager) == null ? void 0 : t.getAccessToken();
|
|
443
|
+
return e ? { Authorization: `Bearer ${e}` } : {};
|
|
388
444
|
}
|
|
389
445
|
}
|
|
390
446
|
class U {
|
|
391
|
-
constructor(
|
|
392
|
-
this.transport =
|
|
447
|
+
constructor(e) {
|
|
448
|
+
this.transport = e;
|
|
393
449
|
}
|
|
394
450
|
// --- Customer ---
|
|
395
|
-
async createCustomer(
|
|
396
|
-
return this.transport.post("/billing/customers",
|
|
451
|
+
async createCustomer(e) {
|
|
452
|
+
return this.transport.post("/billing/customers", e);
|
|
397
453
|
}
|
|
398
|
-
async getCustomer(
|
|
399
|
-
return this.transport.get(`/billing/customers/${
|
|
454
|
+
async getCustomer(e) {
|
|
455
|
+
return this.transport.get(`/billing/customers/${e}`);
|
|
400
456
|
}
|
|
401
|
-
async updateCustomer(
|
|
402
|
-
return this.transport.patch(`/billing/customers/${
|
|
457
|
+
async updateCustomer(e, t) {
|
|
458
|
+
return this.transport.patch(`/billing/customers/${e}`, t);
|
|
403
459
|
}
|
|
404
460
|
// --- Subscription ---
|
|
405
|
-
async subscribe(
|
|
406
|
-
return this.transport.post(`/billing/customers/${
|
|
461
|
+
async subscribe(e, t) {
|
|
462
|
+
return this.transport.post(`/billing/customers/${e}/subscribe`, { planId: t });
|
|
407
463
|
}
|
|
408
|
-
async changePlan(
|
|
409
|
-
return this.transport.patch(`/billing/customers/${
|
|
464
|
+
async changePlan(e, t) {
|
|
465
|
+
return this.transport.patch(`/billing/customers/${e}/subscription`, { planId: t });
|
|
410
466
|
}
|
|
411
|
-
async cancelSubscription(
|
|
412
|
-
return this.transport.del(`/billing/customers/${
|
|
467
|
+
async cancelSubscription(e) {
|
|
468
|
+
return this.transport.del(`/billing/customers/${e}/subscription`);
|
|
413
469
|
}
|
|
414
470
|
// --- Invoices ---
|
|
415
|
-
async getInvoices(
|
|
416
|
-
return this.transport.get(`/billing/customers/${
|
|
471
|
+
async getInvoices(e) {
|
|
472
|
+
return this.transport.get(`/billing/customers/${e}/invoices`);
|
|
417
473
|
}
|
|
418
474
|
// --- Usage ---
|
|
419
|
-
async ingestUsageEvent(
|
|
420
|
-
return this.transport.post("/billing/events",
|
|
475
|
+
async ingestUsageEvent(e) {
|
|
476
|
+
return this.transport.post("/billing/events", e);
|
|
421
477
|
}
|
|
422
|
-
async getCurrentUsage(
|
|
423
|
-
return this.transport.get(`/billing/customers/${
|
|
478
|
+
async getCurrentUsage(e) {
|
|
479
|
+
return this.transport.get(`/billing/customers/${e}/usage`);
|
|
424
480
|
}
|
|
425
481
|
// --- Portal ---
|
|
426
|
-
async createPortalToken(
|
|
427
|
-
return this.transport.post("/billing/portal-tokens", { customerId:
|
|
482
|
+
async createPortalToken(e, t) {
|
|
483
|
+
return this.transport.post("/billing/portal-tokens", { customerId: e, expiresIn: t });
|
|
428
484
|
}
|
|
429
485
|
// --- Coupon ---
|
|
430
|
-
async applyCoupon(
|
|
431
|
-
return this.transport.post(`/billing/customers/${
|
|
486
|
+
async applyCoupon(e, t) {
|
|
487
|
+
return this.transport.post(`/billing/customers/${e}/coupon`, { code: t });
|
|
432
488
|
}
|
|
433
489
|
}
|
|
434
490
|
class M {
|
|
435
|
-
constructor(
|
|
436
|
-
this.transport =
|
|
491
|
+
constructor(e) {
|
|
492
|
+
this.transport = e;
|
|
437
493
|
}
|
|
438
494
|
// --- Query ---
|
|
439
|
-
async executeQuery(
|
|
440
|
-
return this.transport.post("/report/query",
|
|
495
|
+
async executeQuery(e) {
|
|
496
|
+
return this.transport.post("/report/query", e);
|
|
441
497
|
}
|
|
442
498
|
// --- Saved Queries ---
|
|
443
|
-
async listQueries(
|
|
444
|
-
const
|
|
445
|
-
return this.transport.get(`/report/queries${
|
|
499
|
+
async listQueries(e) {
|
|
500
|
+
const t = e ? this.toQueryString(e) : "";
|
|
501
|
+
return this.transport.get(`/report/queries${t}`);
|
|
446
502
|
}
|
|
447
|
-
async saveQuery(
|
|
448
|
-
return this.transport.post("/report/queries",
|
|
503
|
+
async saveQuery(e) {
|
|
504
|
+
return this.transport.post("/report/queries", e);
|
|
449
505
|
}
|
|
450
|
-
async updateQuery(
|
|
451
|
-
return this.transport.patch(`/report/queries/${
|
|
506
|
+
async updateQuery(e, t) {
|
|
507
|
+
return this.transport.patch(`/report/queries/${e}`, t);
|
|
452
508
|
}
|
|
453
|
-
async deleteQuery(
|
|
454
|
-
await this.transport.del(`/report/queries/${
|
|
509
|
+
async deleteQuery(e) {
|
|
510
|
+
await this.transport.del(`/report/queries/${e}`);
|
|
455
511
|
}
|
|
456
512
|
// --- Dashboards ---
|
|
457
|
-
async listDashboards(
|
|
458
|
-
const
|
|
459
|
-
return this.transport.get(`/report/dashboards${
|
|
513
|
+
async listDashboards(e) {
|
|
514
|
+
const t = e ? this.toQueryString(e) : "";
|
|
515
|
+
return this.transport.get(`/report/dashboards${t}`);
|
|
460
516
|
}
|
|
461
|
-
async createDashboard(
|
|
462
|
-
return this.transport.post("/report/dashboards",
|
|
517
|
+
async createDashboard(e) {
|
|
518
|
+
return this.transport.post("/report/dashboards", e);
|
|
463
519
|
}
|
|
464
|
-
async getDashboard(
|
|
465
|
-
return this.transport.get(`/report/dashboards/${
|
|
520
|
+
async getDashboard(e) {
|
|
521
|
+
return this.transport.get(`/report/dashboards/${e}`);
|
|
466
522
|
}
|
|
467
|
-
async updateDashboard(
|
|
468
|
-
return this.transport.patch(`/report/dashboards/${
|
|
523
|
+
async updateDashboard(e, t) {
|
|
524
|
+
return this.transport.patch(`/report/dashboards/${e}`, t);
|
|
469
525
|
}
|
|
470
|
-
async deleteDashboard(
|
|
471
|
-
await this.transport.del(`/report/dashboards/${
|
|
526
|
+
async deleteDashboard(e) {
|
|
527
|
+
await this.transport.del(`/report/dashboards/${e}`);
|
|
472
528
|
}
|
|
473
529
|
// --- Embed Tokens ---
|
|
474
|
-
async createEmbedToken(
|
|
475
|
-
return this.transport.post("/report/embed-tokens",
|
|
530
|
+
async createEmbedToken(e) {
|
|
531
|
+
return this.transport.post("/report/embed-tokens", e);
|
|
476
532
|
}
|
|
477
533
|
async listEmbedTokens() {
|
|
478
534
|
return this.transport.get("/report/embed-tokens");
|
|
479
535
|
}
|
|
480
|
-
async revokeEmbedToken(
|
|
481
|
-
await this.transport.del(`/report/embed-tokens/${
|
|
536
|
+
async revokeEmbedToken(e) {
|
|
537
|
+
await this.transport.del(`/report/embed-tokens/${e}`);
|
|
482
538
|
}
|
|
483
|
-
toQueryString(
|
|
484
|
-
const
|
|
485
|
-
return
|
|
539
|
+
toQueryString(e) {
|
|
540
|
+
const t = Object.entries(e).filter(([, s]) => s != null && s !== "");
|
|
541
|
+
return t.length === 0 ? "" : "?" + t.map(([s, r]) => `${s}=${encodeURIComponent(String(r))}`).join("&");
|
|
486
542
|
}
|
|
487
543
|
}
|
|
488
|
-
const
|
|
489
|
-
class
|
|
490
|
-
constructor(
|
|
491
|
-
if (this.tokenManager = null, this.loaded = !1, !
|
|
544
|
+
const E = "https://api.saas-support.com/v1";
|
|
545
|
+
class v {
|
|
546
|
+
constructor(e) {
|
|
547
|
+
if (this.tokenManager = null, this.loaded = !1, !e.publishableKey && !e.apiKey)
|
|
492
548
|
throw new Error("SaaSSupport: either publishableKey or apiKey is required");
|
|
493
|
-
const
|
|
549
|
+
const t = e.baseUrl ?? E;
|
|
494
550
|
this.emitter = new S();
|
|
495
|
-
const s =
|
|
496
|
-
|
|
551
|
+
const s = e.publishableKey ? new m(t, { type: "publishableKey", key: e.publishableKey }) : null, r = e.apiKey ? new m(t, { type: "apiKey", key: e.apiKey }) : null;
|
|
552
|
+
e.publishableKey && (this.tokenManager = new b(e.publishableKey)), this.auth = new R(
|
|
497
553
|
s ?? r,
|
|
498
554
|
this.tokenManager,
|
|
499
555
|
this.emitter,
|
|
500
|
-
|
|
501
|
-
), this.billing = new U(r ?? s), this.report = new M(r ?? s), this.tokenManager && this.tokenManager.setRefreshCallback(() => this.auth.performRefresh())
|
|
556
|
+
t
|
|
557
|
+
), this.billing = new U(r ?? s), this.report = new M(r ?? s), this.tokenManager && (this.tokenManager.setRefreshCallback(() => this.auth.performRefresh()), this.tokenManager.setTokensChangedCallback(() => {
|
|
558
|
+
this.tokenManager.hasRefreshToken() || this.auth.handleExternalLogout();
|
|
559
|
+
})), this.tokenManager && s && s.setUnauthorizedHandler(async () => {
|
|
560
|
+
try {
|
|
561
|
+
return await this.tokenManager.refreshOnce(), this.tokenManager.getAccessToken();
|
|
562
|
+
} catch {
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
});
|
|
502
566
|
}
|
|
503
567
|
async load() {
|
|
504
568
|
this.loaded || (await this.auth.load(), this.loaded = !0);
|
|
@@ -506,23 +570,23 @@ class E {
|
|
|
506
570
|
isLoaded() {
|
|
507
571
|
return this.loaded;
|
|
508
572
|
}
|
|
509
|
-
onError(
|
|
510
|
-
return this.emitter.on("error",
|
|
573
|
+
onError(e) {
|
|
574
|
+
return this.emitter.on("error", e);
|
|
511
575
|
}
|
|
512
576
|
destroy() {
|
|
513
|
-
var
|
|
514
|
-
(
|
|
577
|
+
var e;
|
|
578
|
+
(e = this.tokenManager) == null || e.destroy(), this.emitter.removeAll();
|
|
515
579
|
}
|
|
516
580
|
}
|
|
517
|
-
function H(
|
|
518
|
-
return "mfaRequired" in
|
|
581
|
+
function H(a) {
|
|
582
|
+
return "mfaRequired" in a && a.mfaRequired === !0;
|
|
519
583
|
}
|
|
520
584
|
export {
|
|
521
585
|
R as AuthClient,
|
|
522
586
|
U as BillingClient,
|
|
523
587
|
M as ReportClient,
|
|
524
|
-
|
|
525
|
-
|
|
588
|
+
k as SaaSError,
|
|
589
|
+
v as SaaSSupport,
|
|
526
590
|
m as Transport,
|
|
527
591
|
H as isMfaRequired
|
|
528
592
|
};
|
package/dist/react.d.ts
CHANGED
|
@@ -67,6 +67,7 @@ declare class AuthClient {
|
|
|
67
67
|
orgId: string;
|
|
68
68
|
role: string;
|
|
69
69
|
}>;
|
|
70
|
+
/* Excluded from this release type: handleExternalLogout */
|
|
70
71
|
/* Excluded from this release type: performRefresh */
|
|
71
72
|
private setSession;
|
|
72
73
|
private clearSession;
|
|
@@ -613,17 +614,28 @@ declare class TokenManager {
|
|
|
613
614
|
private accessToken;
|
|
614
615
|
private refreshToken;
|
|
615
616
|
private refreshTimer;
|
|
617
|
+
private refreshInFlight;
|
|
616
618
|
private storageKey;
|
|
617
619
|
private onRefreshNeeded;
|
|
620
|
+
private onTokensChanged;
|
|
621
|
+
private boundHandleStorage;
|
|
618
622
|
constructor(keyPrefix: string);
|
|
619
623
|
setRefreshCallback(cb: () => Promise<void>): void;
|
|
624
|
+
setTokensChangedCallback(cb: () => void): void;
|
|
620
625
|
getAccessToken(): string | null;
|
|
621
626
|
getRefreshToken(): string | null;
|
|
622
627
|
hasRefreshToken(): boolean;
|
|
623
628
|
setTokens(accessToken: string, refreshToken: string): void;
|
|
624
629
|
clearTokens(): void;
|
|
630
|
+
/**
|
|
631
|
+
* Coalesces concurrent refresh calls within this tab and coordinates
|
|
632
|
+
* across tabs via Web Locks API (when available).
|
|
633
|
+
*/
|
|
634
|
+
refreshOnce(): Promise<void>;
|
|
625
635
|
destroy(): void;
|
|
636
|
+
private executeRefresh;
|
|
626
637
|
private scheduleRefresh;
|
|
638
|
+
private handleStorageEvent;
|
|
627
639
|
private getTokenExpiry;
|
|
628
640
|
private loadRefreshToken;
|
|
629
641
|
private saveRefreshToken;
|
|
@@ -633,12 +645,16 @@ declare class TokenManager {
|
|
|
633
645
|
declare class Transport {
|
|
634
646
|
private baseUrl;
|
|
635
647
|
private authMode;
|
|
648
|
+
private onUnauthorized;
|
|
636
649
|
constructor(baseUrl: string, authMode: AuthMode);
|
|
650
|
+
/** Register a handler that refreshes tokens and returns a new access token, or null. */
|
|
651
|
+
setUnauthorizedHandler(handler: () => Promise<string | null>): void;
|
|
637
652
|
request<T>(method: string, path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
638
653
|
get<T>(path: string, headers?: Record<string, string>): Promise<T>;
|
|
639
654
|
post<T>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
640
655
|
patch<T>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
641
656
|
del<T>(path: string, headers?: Record<string, string>): Promise<T>;
|
|
657
|
+
private doRequest;
|
|
642
658
|
private getAuthHeaders;
|
|
643
659
|
private inferDomain;
|
|
644
660
|
}
|