@saas-support/react 0.3.2 → 0.4.0
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 +8 -0
- package/dist/index.js +79 -44
- package/dist/react.cjs +107 -2
- package/dist/react.d.ts +14 -0
- package/dist/react.js +965 -661
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class c 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(n){if(n instanceof c&&n.isUnauthorized&&this.onUnauthorized&&(r!=null&&r.Authorization)){const i=await this.onUnauthorized();if(i)return this.doRequest(e,t,s,{...r,Authorization:`Bearer ${i}`})}throw n}}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 uploadBinary(e,t,s){try{return await this.doUploadBinary(e,t,s)}catch(r){if(r instanceof c&&r.isUnauthorized&&this.onUnauthorized&&(s!=null&&s.Authorization)){const n=await this.onUnauthorized();if(n)return this.doUploadBinary(e,t,{...s,Authorization:`Bearer ${n}`})}throw r}}async doUploadBinary(e,t,s){const r={"Content-Type":"application/octet-stream",...this.getAuthHeaders(),...s},i=await(await fetch(`${this.baseUrl}${e}`,{method:"POST",headers:r,body:t})).json();if(i.code&&i.code>=400){const a=this.inferDomain(e);throw new c(i.code,i.message||"Upload failed",a)}return i.data}async doRequest(e,t,s,r){const n={"Content-Type":"application/json",...this.getAuthHeaders(),...r},a=await(await fetch(`${this.baseUrl}${t}`,{method:e,headers:n,body:s?JSON.stringify(s):void 0})).json();if(a.code&&a.code>=400){const p=this.inferDomain(t);throw new c(a.code,a.message||"Request failed",p)}return a.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 R{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)}`),n=window.screenX+(window.innerWidth-T)/2,i=window.screenY+(window.innerHeight-w)/2,a=window.open(s,"saas-support-oauth",`width=${T},height=${w},left=${n},top=${i},toolbar=no,menubar=no`);return new Promise((p,l)=>{let h=!1;const d=async u=>{var k;if(((k=u.data)==null?void 0:k.type)==="saas-support:oauth-callback"&&!h){if(h=!0,window.removeEventListener("message",d),clearTimeout(m),clearInterval(f),a==null||a.close(),u.data.error){l(new Error(`OAuth error: ${u.data.error}`));return}try{const g=await this.transport.post(`/auth/oauth/${e}/callback`,{code:u.data.code,state:u.data.state||r});this.setSession(g),p(g)}catch(g){l(g)}}};window.addEventListener("message",d);const m=setTimeout(()=>{h||(h=!0,window.removeEventListener("message",d),clearInterval(f),a==null||a.close(),l(new Error("OAuth popup timed out")))},M),f=setInterval(()=>{a!=null&&a.closed&&!h&&(h=!0,clearInterval(f),clearTimeout(m),window.removeEventListener("message",d),l(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 uploadAvatar(e){const t=await this.transport.uploadBinary("/auth/avatar",e,this.authHeaders());return this.cachedUser&&(this.cachedUser={...this.cachedUser,avatarUrl:t.avatarUrl},this.emitter.emit("authStateChange",this.cachedUser)),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 U{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 v="https://api.saas-support.com/v1";class E{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??v;this.emitter=new R;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 U(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 H(o){return"mfaRequired"in o&&o.mfaRequired===!0}exports.AuthClient=b;exports.BillingClient=S;exports.ReportClient=U;exports.SaaSError=c;exports.SaaSSupport=E;exports.Transport=y;exports.isMfaRequired=H;
|
package/dist/index.d.ts
CHANGED
|
@@ -44,6 +44,12 @@ export declare class AuthClient {
|
|
|
44
44
|
avatarUrl?: string;
|
|
45
45
|
metadata?: Record<string, unknown>;
|
|
46
46
|
}): Promise<User>;
|
|
47
|
+
uploadAvatar(imageBlob: Blob): Promise<{
|
|
48
|
+
avatarUrl: string;
|
|
49
|
+
small: string;
|
|
50
|
+
medium: string;
|
|
51
|
+
original: string;
|
|
52
|
+
}>;
|
|
47
53
|
changePassword(currentPassword: string, newPassword: string): Promise<void>;
|
|
48
54
|
listOrgs(): Promise<Org[]>;
|
|
49
55
|
createOrg(name: string, slug: string): Promise<Org>;
|
|
@@ -490,6 +496,8 @@ export declare class Transport {
|
|
|
490
496
|
post<T>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
491
497
|
patch<T>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
|
|
492
498
|
del<T>(path: string, headers?: Record<string, string>): Promise<T>;
|
|
499
|
+
uploadBinary<T>(path: string, data: Blob, headers?: Record<string, string>): Promise<T>;
|
|
500
|
+
private doUploadBinary;
|
|
493
501
|
private doRequest;
|
|
494
502
|
private getAuthHeaders;
|
|
495
503
|
private inferDomain;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class g extends Error {
|
|
2
2
|
constructor(e, t, s = "unknown") {
|
|
3
3
|
super(t), this.name = "SaaSError", this.code = e, this.domain = s;
|
|
4
4
|
}
|
|
@@ -18,7 +18,7 @@ class k extends Error {
|
|
|
18
18
|
return this.code === 429;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
class
|
|
21
|
+
class k {
|
|
22
22
|
constructor(e, t) {
|
|
23
23
|
this.onUnauthorized = null, this.baseUrl = e, this.authMode = t;
|
|
24
24
|
}
|
|
@@ -29,16 +29,16 @@ class m {
|
|
|
29
29
|
async request(e, t, s, r) {
|
|
30
30
|
try {
|
|
31
31
|
return await this.doRequest(e, t, s, r);
|
|
32
|
-
} catch (
|
|
33
|
-
if (
|
|
34
|
-
const
|
|
35
|
-
if (
|
|
32
|
+
} catch (n) {
|
|
33
|
+
if (n instanceof g && n.isUnauthorized && this.onUnauthorized && (r != null && r.Authorization)) {
|
|
34
|
+
const i = await this.onUnauthorized();
|
|
35
|
+
if (i)
|
|
36
36
|
return this.doRequest(e, t, s, {
|
|
37
37
|
...r,
|
|
38
|
-
Authorization: `Bearer ${
|
|
38
|
+
Authorization: `Bearer ${i}`
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
-
throw
|
|
41
|
+
throw n;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
async get(e, t) {
|
|
@@ -53,21 +53,52 @@ class m {
|
|
|
53
53
|
async del(e, t) {
|
|
54
54
|
return this.request("DELETE", e, void 0, t);
|
|
55
55
|
}
|
|
56
|
+
async uploadBinary(e, t, s) {
|
|
57
|
+
try {
|
|
58
|
+
return await this.doUploadBinary(e, t, s);
|
|
59
|
+
} catch (r) {
|
|
60
|
+
if (r instanceof g && r.isUnauthorized && this.onUnauthorized && (s != null && s.Authorization)) {
|
|
61
|
+
const n = await this.onUnauthorized();
|
|
62
|
+
if (n)
|
|
63
|
+
return this.doUploadBinary(e, t, {
|
|
64
|
+
...s,
|
|
65
|
+
Authorization: `Bearer ${n}`
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
throw r;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async doUploadBinary(e, t, s) {
|
|
72
|
+
const r = {
|
|
73
|
+
"Content-Type": "application/octet-stream",
|
|
74
|
+
...this.getAuthHeaders(),
|
|
75
|
+
...s
|
|
76
|
+
}, i = await (await fetch(`${this.baseUrl}${e}`, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: r,
|
|
79
|
+
body: t
|
|
80
|
+
})).json();
|
|
81
|
+
if (i.code && i.code >= 400) {
|
|
82
|
+
const a = this.inferDomain(e);
|
|
83
|
+
throw new g(i.code, i.message || "Upload failed", a);
|
|
84
|
+
}
|
|
85
|
+
return i.data;
|
|
86
|
+
}
|
|
56
87
|
async doRequest(e, t, s, r) {
|
|
57
|
-
const
|
|
88
|
+
const n = {
|
|
58
89
|
"Content-Type": "application/json",
|
|
59
90
|
...this.getAuthHeaders(),
|
|
60
91
|
...r
|
|
61
|
-
},
|
|
92
|
+
}, a = await (await fetch(`${this.baseUrl}${t}`, {
|
|
62
93
|
method: e,
|
|
63
|
-
headers:
|
|
94
|
+
headers: n,
|
|
64
95
|
body: s ? JSON.stringify(s) : void 0
|
|
65
96
|
})).json();
|
|
66
|
-
if (
|
|
67
|
-
const
|
|
68
|
-
throw new
|
|
97
|
+
if (a.code && a.code >= 400) {
|
|
98
|
+
const p = this.inferDomain(t);
|
|
99
|
+
throw new g(a.code, a.message || "Request failed", p);
|
|
69
100
|
}
|
|
70
|
-
return
|
|
101
|
+
return a.data;
|
|
71
102
|
}
|
|
72
103
|
getAuthHeaders() {
|
|
73
104
|
switch (this.authMode.type) {
|
|
@@ -202,8 +233,8 @@ class S {
|
|
|
202
233
|
this.listeners.clear();
|
|
203
234
|
}
|
|
204
235
|
}
|
|
205
|
-
const
|
|
206
|
-
class
|
|
236
|
+
const w = 500, T = 600, U = 5 * 60 * 1e3;
|
|
237
|
+
class $ {
|
|
207
238
|
constructor(e, t, s, r) {
|
|
208
239
|
this.cachedUser = null, this.cachedSettings = null, this.loaded = !1, this.transport = e, this.tokenManager = t, this.emitter = s, this.baseUrl = r;
|
|
209
240
|
}
|
|
@@ -254,17 +285,17 @@ class R {
|
|
|
254
285
|
async signInWithOAuth(e) {
|
|
255
286
|
const t = `${this.baseUrl}/auth/oauth/${e}/popup-callback`, { authUrl: s, state: r } = await this.transport.get(
|
|
256
287
|
`/auth/oauth/${e}?redirect_uri=${encodeURIComponent(t)}`
|
|
257
|
-
),
|
|
288
|
+
), n = window.screenX + (window.innerWidth - w) / 2, i = window.screenY + (window.innerHeight - T) / 2, a = window.open(
|
|
258
289
|
s,
|
|
259
290
|
"saas-support-oauth",
|
|
260
|
-
`width=${
|
|
291
|
+
`width=${w},height=${T},left=${n},top=${i},toolbar=no,menubar=no`
|
|
261
292
|
);
|
|
262
|
-
return new Promise((
|
|
263
|
-
let
|
|
293
|
+
return new Promise((p, c) => {
|
|
294
|
+
let h = !1;
|
|
264
295
|
const l = async (u) => {
|
|
265
|
-
var
|
|
266
|
-
if (((
|
|
267
|
-
if (
|
|
296
|
+
var m;
|
|
297
|
+
if (((m = u.data) == null ? void 0 : m.type) === "saas-support:oauth-callback" && !h) {
|
|
298
|
+
if (h = !0, window.removeEventListener("message", l), clearTimeout(y), clearInterval(f), a == null || a.close(), u.data.error) {
|
|
268
299
|
c(new Error(`OAuth error: ${u.data.error}`));
|
|
269
300
|
return;
|
|
270
301
|
}
|
|
@@ -273,17 +304,17 @@ class R {
|
|
|
273
304
|
`/auth/oauth/${e}/callback`,
|
|
274
305
|
{ code: u.data.code, state: u.data.state || r }
|
|
275
306
|
);
|
|
276
|
-
this.setSession(d),
|
|
307
|
+
this.setSession(d), p(d);
|
|
277
308
|
} catch (d) {
|
|
278
309
|
c(d);
|
|
279
310
|
}
|
|
280
311
|
}
|
|
281
312
|
};
|
|
282
313
|
window.addEventListener("message", l);
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
},
|
|
286
|
-
|
|
314
|
+
const y = setTimeout(() => {
|
|
315
|
+
h || (h = !0, window.removeEventListener("message", l), clearInterval(f), a == null || a.close(), c(new Error("OAuth popup timed out")));
|
|
316
|
+
}, U), f = setInterval(() => {
|
|
317
|
+
a != null && a.closed && !h && (h = !0, clearInterval(f), clearTimeout(y), window.removeEventListener("message", l), c(new Error("OAuth popup was closed")));
|
|
287
318
|
}, 500);
|
|
288
319
|
});
|
|
289
320
|
}
|
|
@@ -368,6 +399,10 @@ class R {
|
|
|
368
399
|
const t = await this.transport.patch("/auth/me", e, this.authHeaders());
|
|
369
400
|
return this.cachedUser = t, this.emitter.emit("authStateChange", t), t;
|
|
370
401
|
}
|
|
402
|
+
async uploadAvatar(e) {
|
|
403
|
+
const t = await this.transport.uploadBinary("/auth/avatar", e, this.authHeaders());
|
|
404
|
+
return this.cachedUser && (this.cachedUser = { ...this.cachedUser, avatarUrl: t.avatarUrl }, this.emitter.emit("authStateChange", this.cachedUser)), t;
|
|
405
|
+
}
|
|
371
406
|
async changePassword(e, t) {
|
|
372
407
|
await this.transport.post("/auth/change-password", { currentPassword: e, newPassword: t }, this.authHeaders());
|
|
373
408
|
}
|
|
@@ -443,7 +478,7 @@ class R {
|
|
|
443
478
|
return e ? { Authorization: `Bearer ${e}` } : {};
|
|
444
479
|
}
|
|
445
480
|
}
|
|
446
|
-
class
|
|
481
|
+
class R {
|
|
447
482
|
constructor(e) {
|
|
448
483
|
this.transport = e;
|
|
449
484
|
}
|
|
@@ -541,20 +576,20 @@ class M {
|
|
|
541
576
|
return t.length === 0 ? "" : "?" + t.map(([s, r]) => `${s}=${encodeURIComponent(String(r))}`).join("&");
|
|
542
577
|
}
|
|
543
578
|
}
|
|
544
|
-
const
|
|
545
|
-
class
|
|
579
|
+
const v = "https://api.saas-support.com/v1";
|
|
580
|
+
class H {
|
|
546
581
|
constructor(e) {
|
|
547
582
|
if (this.tokenManager = null, this.loaded = !1, !e.publishableKey && !e.apiKey)
|
|
548
583
|
throw new Error("SaaSSupport: either publishableKey or apiKey is required");
|
|
549
|
-
const t = e.baseUrl ??
|
|
584
|
+
const t = e.baseUrl ?? v;
|
|
550
585
|
this.emitter = new S();
|
|
551
|
-
const s = e.publishableKey ? new
|
|
552
|
-
e.publishableKey && (this.tokenManager = new b(e.publishableKey)), this.auth = new
|
|
586
|
+
const s = e.publishableKey ? new k(t, { type: "publishableKey", key: e.publishableKey }) : null, r = e.apiKey ? new k(t, { type: "apiKey", key: e.apiKey }) : null;
|
|
587
|
+
e.publishableKey && (this.tokenManager = new b(e.publishableKey)), this.auth = new $(
|
|
553
588
|
s ?? r,
|
|
554
589
|
this.tokenManager,
|
|
555
590
|
this.emitter,
|
|
556
591
|
t
|
|
557
|
-
), this.billing = new
|
|
592
|
+
), this.billing = new R(r ?? s), this.report = new M(r ?? s), this.tokenManager && (this.tokenManager.setRefreshCallback(() => this.auth.performRefresh()), this.tokenManager.setTokensChangedCallback(() => {
|
|
558
593
|
this.tokenManager.hasRefreshToken() || this.auth.handleExternalLogout();
|
|
559
594
|
})), this.tokenManager && s && s.setUnauthorizedHandler(async () => {
|
|
560
595
|
try {
|
|
@@ -578,15 +613,15 @@ class v {
|
|
|
578
613
|
(e = this.tokenManager) == null || e.destroy(), this.emitter.removeAll();
|
|
579
614
|
}
|
|
580
615
|
}
|
|
581
|
-
function
|
|
582
|
-
return "mfaRequired" in
|
|
616
|
+
function E(o) {
|
|
617
|
+
return "mfaRequired" in o && o.mfaRequired === !0;
|
|
583
618
|
}
|
|
584
619
|
export {
|
|
585
|
-
|
|
586
|
-
|
|
620
|
+
$ as AuthClient,
|
|
621
|
+
R as BillingClient,
|
|
587
622
|
M as ReportClient,
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
623
|
+
g as SaaSError,
|
|
624
|
+
H as SaaSSupport,
|
|
625
|
+
k as Transport,
|
|
626
|
+
E as isMfaRequired
|
|
592
627
|
};
|