@vertesia/ui 1.0.0-dev.20260225.024852Z → 1.0.0-dev.20260227.112605Z
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/lib/esm/env/index.js +3 -0
- package/lib/esm/env/index.js.map +1 -1
- package/lib/esm/features/agent/chat/ModernAgentOutput/utils.js +8 -1
- package/lib/esm/features/agent/chat/ModernAgentOutput/utils.js.map +1 -1
- package/lib/esm/features/facets/CollectionsFacetsNav.js +2 -2
- package/lib/esm/features/facets/CollectionsFacetsNav.js.map +1 -1
- package/lib/esm/features/facets/DocumentsFacetsNav.js +2 -2
- package/lib/esm/features/facets/DocumentsFacetsNav.js.map +1 -1
- package/lib/esm/features/facets/utils/TypeFacet.js +2 -2
- package/lib/esm/features/facets/utils/TypeFacet.js.map +1 -1
- package/lib/esm/features/store/collections/BrowseCollectionView.js +3 -1
- package/lib/esm/features/store/collections/BrowseCollectionView.js.map +1 -1
- package/lib/esm/features/store/objects/DocumentSearchResults.js +2 -1
- package/lib/esm/features/store/objects/DocumentSearchResults.js.map +1 -1
- package/lib/esm/features/store/objects/upload/DocumentUploadModal.js +3 -1
- package/lib/esm/features/store/objects/upload/DocumentUploadModal.js.map +1 -1
- package/lib/esm/features/store/types/ContentObjectTypesSearch.js +4 -3
- package/lib/esm/features/store/types/ContentObjectTypesSearch.js.map +1 -1
- package/lib/esm/features/store/types/SelectContentType.js +8 -8
- package/lib/esm/features/store/types/SelectContentType.js.map +1 -1
- package/lib/esm/features/store/types/SelectContentTypeModal.js +2 -2
- package/lib/esm/features/store/types/SelectContentTypeModal.js.map +1 -1
- package/lib/esm/features/store/types/TypeRegistry.js.map +1 -0
- package/lib/esm/features/store/types/TypeRegistryProvider.js +53 -0
- package/lib/esm/features/store/types/TypeRegistryProvider.js.map +1 -0
- package/lib/esm/features/store/types/index.js +2 -0
- package/lib/esm/features/store/types/index.js.map +1 -1
- package/lib/esm/session/UserSession.js +1 -27
- package/lib/esm/session/UserSession.js.map +1 -1
- package/lib/esm/session/index.js +0 -1
- package/lib/esm/session/index.js.map +1 -1
- package/lib/esm/shell/SplashScreen.js +2 -2
- package/lib/esm/shell/SplashScreen.js.map +1 -1
- package/lib/esm/shell/VertesiaShell.js +2 -2
- package/lib/esm/shell/VertesiaShell.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types/env/index.d.ts +2 -0
- package/lib/types/env/index.d.ts.map +1 -1
- package/lib/types/features/agent/chat/ModernAgentOutput/utils.d.ts.map +1 -1
- package/lib/types/features/facets/CollectionsFacetsNav.d.ts.map +1 -1
- package/lib/types/features/facets/utils/TypeFacet.d.ts.map +1 -1
- package/lib/types/features/facets/utils/VTypeFacet.d.ts +1 -1
- package/lib/types/features/facets/utils/VTypeFacet.d.ts.map +1 -1
- package/lib/types/features/store/collections/BrowseCollectionView.d.ts.map +1 -1
- package/lib/types/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
- package/lib/types/features/store/objects/upload/DocumentUploadModal.d.ts.map +1 -1
- package/lib/types/features/store/types/ContentObjectTypesSearch.d.ts.map +1 -1
- package/lib/types/features/store/types/TypeRegistry.d.ts.map +1 -0
- package/lib/types/features/store/types/TypeRegistryProvider.d.ts +15 -0
- package/lib/types/features/store/types/TypeRegistryProvider.d.ts.map +1 -0
- package/lib/types/features/store/types/index.d.ts +2 -0
- package/lib/types/features/store/types/index.d.ts.map +1 -1
- package/lib/types/session/UserSession.d.ts +0 -4
- package/lib/types/session/UserSession.d.ts.map +1 -1
- package/lib/types/session/index.d.ts +0 -1
- package/lib/types/session/index.d.ts.map +1 -1
- package/lib/types/shell/VertesiaShell.d.ts.map +1 -1
- package/lib/vertesia-ui-env.js +1 -1
- package/lib/vertesia-ui-env.js.map +1 -1
- package/lib/vertesia-ui-features.js +1 -1
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-session.js +1 -1
- package/lib/vertesia-ui-session.js.map +1 -1
- package/lib/vertesia-ui-shell.js +1 -1
- package/lib/vertesia-ui-shell.js.map +1 -1
- package/package.json +15 -7
- package/src/env/index.ts +5 -0
- package/src/features/agent/chat/ModernAgentOutput/utils.ts +7 -1
- package/src/features/facets/CollectionsFacetsNav.tsx +2 -2
- package/src/features/facets/DocumentsFacetsNav.tsx +2 -2
- package/src/features/facets/utils/TypeFacet.tsx +2 -2
- package/src/features/facets/utils/VTypeFacet.tsx +1 -1
- package/src/features/store/collections/BrowseCollectionView.tsx +5 -2
- package/src/features/store/objects/DocumentSearchResults.tsx +4 -2
- package/src/features/store/objects/upload/DocumentUploadModal.tsx +3 -1
- package/src/features/store/types/ContentObjectTypesSearch.tsx +4 -3
- package/src/features/store/types/SelectContentType.tsx +8 -8
- package/src/features/store/types/SelectContentTypeModal.tsx +2 -2
- package/src/features/store/types/TypeRegistryProvider.tsx +73 -0
- package/src/features/store/types/index.ts +2 -0
- package/src/session/UserSession.ts +1 -28
- package/src/session/index.ts +0 -1
- package/src/shell/SplashScreen.tsx +4 -2
- package/src/shell/VertesiaShell.tsx +10 -8
- package/lib/esm/session/TypeRegistry.js.map +0 -1
- package/lib/types/session/TypeRegistry.d.ts.map +0 -1
- /package/lib/esm/{session → features/store/types}/TypeRegistry.js +0 -0
- /package/lib/types/{session → features/store/types}/TypeRegistry.d.ts +0 -0
- /package/src/{session → features/store/types}/TypeRegistry.ts +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jwtDecode as e}from"jwt-decode";import{Env as t}from"@vertesia/ui/env";import{getAnalytics as o,logEvent as n}from"firebase/analytics";import{initializeApp as r}from"firebase/app";import{getAuth as i,onAuthStateChanged as s}from"firebase/auth";import{useState as a,useEffect as c,useCallback as l,createContext as u,useContext as d,useRef as h}from"react";import{useUserSession as g}from"@vertesia/ui/session";import{VertesiaClient as p}from"@vertesia/client";import{jsx as f}from"react/jsx-runtime";const m="composableai.lastSelectedAccountId",w="composableai.lastSelectedProjectId";let v,k,S=null,y=null,b=null;function T(){if(!S)try{if(!t.firebase)throw new Error("Firebase configuration is not available in the environment");S=r(t.firebase)}catch(e){throw console.error("Failed to initialize Firebase app:",e),new Error("Firebase initialization failed - environment may not be properly initialized")}return S}function _(){return y||(y=o(T())),y}function j(){return b||(b=i(T())),b}async function I(e){if(e)if(t.firebase)try{e&&console.log(`Resolving tenant ID from email: ${e}`);let o=3,n=250;for(;o>0;)try{const o=await fetch("/api/resolve-tenant",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({tenantEmail:e}),signal:AbortSignal.timeout(5e3)});if(!o)throw new Error("No response received from tenant API");if(!o.ok){try{const e=await o.json();console.error("Failed to resolve tenant ID:",e.error)}catch(e){console.error(`Failed to resolve tenant ID: HTTP ${o.status}`)}if(404===o.status)return void console.warn(`Tenant not found for ${e}`);throw new Error(`HTTP error ${o.status}`)}const n=await o.json();if(n&&n.firebaseTenantId){const e=j();return e.tenantId=n.firebaseTenantId,t.firebase.providerType=n.provider??"oidc",console.log(`Tenant ID set to ${e.tenantId}`),n}return void console.error(`Invalid response format, missing tenantId for ${e}`)}catch(e){if(!(o>1))throw e;console.warn(`Tenant resolution failed, retrying in ${n}ms...`,e),await new Promise(e=>setTimeout(e,n)),n*=2,o--}}catch(e){console.error("Error setting Firebase tenant:",e instanceof Error?e.message:"Unknown error")}else console.log("Firebase configuration is not available in the environment");else console.log("No tenant name or email specified, skipping tenant setup")}async function A(e){const o=j().currentUser;return o?o.getIdToken(e).then(n=>(t.logger.info("Got Firebase token",{vertesia:{user_email:o.email,user_name:o.displayName,user_id:o.uid,refresh:e}}),n)).catch(n=>(t.logger.error("Failed to get Firebase token",{vertesia:{user_email:o.email,user_name:o.displayName,user_id:o.uid,refresh:e,error:n}}),console.error("Failed to get access token",n),null)):(t.logger.warn("No user found"),Promise.resolve(null))}async function U(o,n,r,i,s=0){console.log(`Getting/refreshing composable token for account ${n} and project ${r} `),t.logger.info("Getting/refreshing composable token",{vertesia:{account_id:n,project_id:r,retry_count:s}});const a=await o();if(!a)throw console.log("No id token found - using cookie auth"),new Error("No id token found");const c=t.endpoints.sts;console.log("Using STS for token generation:",c),t.logger.info("Using STS for token generation",{vertesia:{account_id:n,project_id:r,sts_url:c}});try{const l=new URL(c+"/token/issue"),u={type:"user",account_id:n,project_id:r,expires_at:i?Math.floor(Date.now()/1e3)+i:void 0},d=await fetch(l,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify(u)});if(a&&404===d?.status){console.log("404: User not found - calling ensure-user endpoint"),t.logger.info("404: User not found - calling ensure-user endpoint",{vertesia:{account_id:n,project_id:r,status:d?.status}});const c=await fetch(t.endpoints.studio+"/auth/ensure-user",{method:"POST",headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"}});if(412===c.status){console.log("412: No invite found - signup required"),t.logger.info("412: No invite found - signup required",{vertesia:{account_id:n,project_id:r}});const o=e(a);if(!o?.email)throw t.logger.error("No email found in id token"),new Error("No email found in id token");throw new P("User not found - signup required",o.email)}if(!c.ok)throw console.error("Failed to ensure user exists",c.status),t.logger.error("Failed to ensure user exists",{vertesia:{account_id:n,project_id:r,status:c.status}}),new Error("Failed to ensure user exists");return console.log("User ensured - retrying token generation"),t.logger.info("User ensured - retrying token generation",{vertesia:{account_id:n,project_id:r}}),U(o,n,r,i,s)}if(a&&412===d?.status){console.log("412: auth succeeded but user doesn't exist - signup required",d?.status),t.logger.error("412: auth succeeded but user doesn't exist - signup required",{vertesia:{account_id:n,project_id:r,status:d?.status}});const o=e(a);if(!o?.email)throw t.logger.error("No email found in id token"),new Error("No email found in id token");throw t.logger.error("User not found",{vertesia:{account_id:n,project_id:r,email:o.email}}),new P("User not found",o.email)}if(403===d.status){if(s>0)throw console.error("403: Access denied even without account scope - user may have no accounts"),t.logger.error("403: Access denied after retry - authorization failure",{vertesia:{account_id:n,project_id:r,status:d.status,retry_count:s}}),new Error("Access denied - user may not have access to any accounts");return console.log("403: Access denied - clearing cached account and retrying without account scope"),t.logger.warn("403: Access denied - clearing cached account and retrying",{vertesia:{account_id:n,project_id:r,status:d.status,retry_count:s}}),localStorage.removeItem(m),n&&localStorage.removeItem(w+"-"+n),U(o,void 0,void 0,i,s+1)}if(!d.ok){const e=await d.text();throw console.error("STS token generation failed:",d.status,e),t.logger.error("STS token generation failed",{vertesia:{status:d.status,error:e,account_id:n,project_id:r}}),new Error(`Failed to get token from STS: ${d.status}`)}const{token:h}=await d.json();return console.log("Successfully got token from STS"),t.logger.info("Successfully got token from STS"),h}catch(e){if(e instanceof P)throw e;throw localStorage.removeItem(m),n&&localStorage.removeItem(w+"-"+n),console.error("Failed to get composable token from STS",e),t.logger.error("Failed to get composable token from STS",{vertesia:{account_id:n,project_id:r,error:e}}),new Error("Failed to get composable token")}}async function E(e,t,o){return U(A,e,t,o)}async function F(o,n,r,i=!1,s=!1){const a=o??localStorage.getItem(m)??void 0,c=n??localStorage.getItem(w+"-"+a)??void 0;if(!i&&v&&k&&k.exp>Date.now()/1e3+300)return{rawToken:v,token:k,error:!1};if(!s&&j().currentUser?v=await E(a,c):(r||v)&&(v=await U(()=>Promise.resolve(r??v),a,c)),!v)throw t.logger.error("Cannot acquire a composable token",{vertesia:{account_id:a,project_id:c}}),new Error("Cannot acquire a composable token");if(k=e(v),!k||!k.exp||!v)throw console.error("Invalid composable token",k),t.logger.error("Invalid composable token",{vertesia:{account_id:a,project_id:c}}),new Error("Invalid composable token");return{rawToken:v,token:k,error:!1}}class P extends Error{email;constructor(e,t){super(e),this.name="UserNotFoundError",this.email=t}}function N(){const{user:e}=g(),[t,o]=a(null),[n,r]=a(!0),[i,s]=a(null);return c(()=>{(async()=>{if(!e?.email)return o(null),void r(!1);try{const t=await fetch("/api/resolve-tenant",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({tenantEmail:e.email})});if(t.ok){const e=await t.json();o(e?{tenantKey:e.name||"unknown",name:e.label||e.name||"Unknown",domain:e.domain||[],firebaseTenantId:e.firebaseTenantId,provider:e.provider,logo:e.logo}:null)}else o(null)}catch(e){console.error("Error loading current tenant:",e),s("Failed to load tenant configuration"),o(null)}finally{r(!1)}})()},[e?.email]),{currentTenant:t,isLoading:n,error:i}}const $="auth_state",L="auth_state_expiry";function C(){return{generateState:l(()=>{const e=crypto.randomUUID(),t=Date.now()+3e5;return sessionStorage.setItem($,e),sessionStorage.setItem(L,t.toString()),e},[]),verifyState:l(e=>{if(!e)return"Missing state";const t=sessionStorage.getItem($),o=parseInt(sessionStorage.getItem(L)||"0");let n;return n=t!==e?`State mismatched (${t} !== ${e})`:Date.now()>o?"State expired":void 0,n},[]),clearState:l(()=>{sessionStorage.removeItem($),sessionStorage.removeItem(L)},[])}}class R{types;map={};constructor(e){this.types=e,e.sort((e,t)=>e.name.localeCompare(t.name));for(const t of e)this.map[t.id]=t}getType(e){return this.map[e]}getTypeLayout(e){const t=this.map[e];return t?t.table_layout:void 0}getTypeName(e){const t=this.map[e];return t?t.name:void 0}}class x{isLoading=!0;client;authError;authToken;typeRegistry;setSession;lastSelectedAccount;lastSelectedProject;onboardingComplete;constructor(e,o){this.client=e||new p({serverUrl:t.endpoints.studio,storeUrl:t.endpoints.zeno,tokenServerUrl:t.endpoints.sts}),o&&(this.setSession=o),this.logout=this.logout.bind(this)}get store(){return this.client.store}get user(){return this.authToken}get account(){return this.authToken?.account}get project(){return this.authToken?.project}get accounts(){return this.authToken?.accounts}get authCallback(){return this.rawAuthToken.then(e=>`Bearer ${e}`)}get rawAuthToken(){return F().then(t=>{const o=t?.rawToken;if(!o)throw new Error("No token available");return this.authToken=e(o),o})}signOut(){this.logout()}getAccount(){return this.authToken?.account}async login(o){return this.authError=void 0,this.isLoading=!1,this.client.withAuthCallback(()=>this.authCallback),this.authToken=e(o),console.log(`Logging in as ${this.authToken?.name} with account ${this.authToken?.account.name} (${this.authToken?.account.id}, and project ${this.authToken?.project?.name} (${this.authToken?.project?.id})`),localStorage.setItem(m,this.authToken.account.id),localStorage.setItem(w+"-"+this.authToken.account.id,this.authToken.project?.id??""),t.onLogin?.(this.authToken),await Promise.all([this._loadTypes(),this.fetchOnboardingStatus()]),Promise.resolve()}isLoggedIn(){return!!this.authToken}logout(){console.log("Logging out");if(t.isDocker||[".composable.sh",".vertesia.dev","vertesia.app"].some(e=>window.location.hostname.endsWith(e))){console.log("Using central auth logout"),this.authError=void 0,this.isLoading=!1,this.authToken=void 0,this.typeRegistry=void 0,this.setSession=void 0,this.client.withAuthCallback(void 0);const e=new URL("https://internal-auth.vertesia.app/"),t=new URL(window.location.href);t.hash="",e.pathname="/logout",e.searchParams.set("redirect_uri",t.toString()),location.replace(e.toString())}else console.log("Using Firebase logout"),this.authToken&&j().signOut(),this.authError=void 0,this.isLoading=!1,this.authToken=void 0,this.typeRegistry=void 0,this.setSession=void 0,this.client.withAuthCallback(void 0)}async switchAccount(e){localStorage.setItem(m,e),this&&(this.account&&this.project?localStorage.setItem(w+"-"+this.account.id,this.project.id):this.account&&localStorage.removeItem(w+"-"+this.account.id)),window.location.replace("/?a="+e)}async switchProject(e){this.account&&localStorage.setItem(w+"-"+this.account.id,e),window.location.replace("/?a="+this.account?.id+"&p="+e)}async _loadTypes(){if(this.project)return this.store.types.catalog.list({layout:!0}).then(e=>this.typeRegistry=new R(e)).catch(e=>{throw console.error("Failed to fetch object types",e),e});console.log("No project selected")}async reloadTypes(){return this._loadTypes().then(()=>{this.setSession?.(this.clone())})}async fetchAccounts(){return this.client.accounts.list().then(e=>{if(!this.authToken)throw new Error("No token available");this.authToken.accounts=e,this.setSession?.(this.clone())}).catch(e=>{throw console.error("Failed to fetch accounts",e),e})}async fetchOnboardingStatus(){if(this.onboardingComplete)return console.log("Onboarding already completed"),!1;const e=this.onboardingComplete;try{const t=await this.client.account.onboardingProgress();if(this.onboardingComplete=Object.values(t).every(e=>!0===e),e!==this.onboardingComplete)return!0;this.setSession?.(this.clone())}catch(e){console.error("Error fetching onboarding status:",e),this.onboardingComplete=!1,this.setSession?.(this.clone())}return!1}clone(){const e=new x(this.client);return e.isLoading=this.isLoading,e.authError=this.authError,e.authToken=this.authToken,e.setSession=this.setSession,e.lastSelectedAccount=this.lastSelectedAccount,e.switchAccount=this.switchAccount,e.typeRegistry=this.typeRegistry,e.onboardingComplete=this.onboardingComplete,e}}const D=u(void 0);function O(){const e=d(D);if(!e)throw new Error("useUserSession must be used within a UserSessionProvider");return e}const q=[".composable.sh",".vertesia.dev","vertesia.app"];function z(){return!!t.isDocker||q.some(e=>window.location.hostname.endsWith(e))}function B({children:e}){const o=new URLSearchParams(location.hash.substring(1)),n=o.get("token"),r=o.get("state"),[i,l]=a(new x),{generateState:u,verifyState:d,clearState:g}=C(),p=h(!1),v=(e,o)=>{const n=new URL(`https://internal-auth.vertesia.app/?sts=${t.endpoints.sts??"https://sts.vertesia.io"}`),r=new URL(window.location.href);r.hash="",n.searchParams.set("redirect_uri",r.toString()),n.searchParams.set("state",u()),location.replace(n.toString())};return c(()=>{if(p.current)return void console.log("Auth: skipping duplicate auth flow initiation");p.current=!0,console.log("Auth: starting auth flow"),t.logger.info("Starting auth flow");const e=new URL(window.location.href),o=e.searchParams.get("a")??localStorage.getItem(m)??void 0,a=e.searchParams.get("p")??localStorage.getItem(w+"-"+o)??void 0;if(console.log("Auth: selected account",o),console.log("Auth: selected project",a),t.logger.info("Selected account and project",{vertesia:{account_id:o,project_id:a}}),n&&r){const e=d(r);return e?(console.error(`Auth: invalid state: ${e}`),t.logger.error(`Invalid state: ${e}`,{vertesia:{state:r}}),v()):g(),void F(o,a,n,!1,z()).then(e=>{i.login(e.rawToken).then(()=>{l(i.clone()),window.location.hash=""})}).catch(e=>{if(e instanceof P)return console.log("User not found - will trigger signup flow",e),i.isLoading=!1,i.authError=e,void l(i.clone());console.error("Failed to fetch user token from studio, redirecting to central auth",e),t.logger.error("Failed to fetch user token from studio, redirecting to central auth",{vertesia:{error:e}}),v()})}if(!i.isLoggedIn()){if(console.log("Auth: not logged in & no token/state"),t.logger.info("Not logged in & no token/state",{vertesia:{account_id:o,project_id:a}}),z())return console.log("Auth: on dev domain, redirecting to central auth with selection",o,a),t.logger.info("Redirecting to central auth with selection",{vertesia:{account_id:o,project_id:a}}),void v();console.log("Auth: not on dev domain"),t.logger.info("Not on dev domain",{vertesia:{account_id:o,project_id:a}})}return s(j(),async e=>{e?(console.log("Auth: successful login with firebase"),t.logger.info("Successful login with firebase",{vertesia:{account_id:o,project_id:a}}),i.setSession=l,await F(o,a,void 0,!1,z()).then(e=>{i.login(e.rawToken).then(()=>l(i.clone()))}).catch(e=>{console.error("Failed to fetch user token from studio",e),t.logger.error("Failed to fetch user token from studio",{vertesia:{account_id:o,project_id:a,error:e}}),e instanceof P||i.logout(),i.isLoading=!1,i.authError=e,l(i.clone())})):(console.log("Auth: using anonymous user"),t.logger.info("Using anonymous user",{vertesia:{account_id:o,project_id:a}}),i.client.withAuthCallback(void 0),i.logout(),l(i.clone()))})},[]),f(D.Provider,{value:i,children:e})}function G(){return{tagUserSession:async e=>{const t=window.localStorage.getItem("composableSignupData");e?t&&window.localStorage.removeItem("composableSignupData"):console.error("No user found -- skipping tagging")},trackEvent:(e,o)=>{t.isProd||console.debug("track event",e,o),n(_(),e,{...o,debug_mode:!t.isProd})}}}export{m as LastSelectedAccountId_KEY,w as LastSelectedProjectId_KEY,R as TypeRegistry,P as UserNotFoundError,x as UserSession,D as UserSessionContext,B as UserSessionProvider,U as fetchComposableToken,E as fetchComposableTokenFromFirebaseToken,F as getComposableToken,_ as getFirebaseAnalytics,T as getFirebaseApp,j as getFirebaseAuth,A as getFirebaseAuthToken,I as setFirebaseTenant,z as shouldRedirectToCentralAuth,C as useAuthState,N as useCurrentTenant,G as useUXTracking,O as useUserSession};
|
|
1
|
+
import{jwtDecode as e}from"jwt-decode";import{Env as t}from"@vertesia/ui/env";import{getAnalytics as o,logEvent as n}from"firebase/analytics";import{initializeApp as r}from"firebase/app";import{getAuth as i,onAuthStateChanged as s}from"firebase/auth";import{useState as a,useEffect as c,useCallback as l,createContext as u,useContext as d,useRef as g}from"react";import{useUserSession as h}from"@vertesia/ui/session";import{VertesiaClient as f}from"@vertesia/client";import{jsx as m}from"react/jsx-runtime";const p="composableai.lastSelectedAccountId",w="composableai.lastSelectedProjectId";let v,k,S=null,b=null,T=null;function y(){if(!S)try{if(!t.firebase)throw new Error("Firebase configuration is not available in the environment");S=r(t.firebase)}catch(e){throw console.error("Failed to initialize Firebase app:",e),new Error("Firebase initialization failed - environment may not be properly initialized")}return S}function _(){return b||(b=o(y())),b}function j(){return T||(T=i(y())),T}async function I(e){if(e)if(t.firebase)try{e&&console.log(`Resolving tenant ID from email: ${e}`);let o=3,n=250;for(;o>0;)try{const o=await fetch("/api/resolve-tenant",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({tenantEmail:e}),signal:AbortSignal.timeout(5e3)});if(!o)throw new Error("No response received from tenant API");if(!o.ok){try{const e=await o.json();console.error("Failed to resolve tenant ID:",e.error)}catch(e){console.error(`Failed to resolve tenant ID: HTTP ${o.status}`)}if(404===o.status)return void console.warn(`Tenant not found for ${e}`);throw new Error(`HTTP error ${o.status}`)}const n=await o.json();if(n&&n.firebaseTenantId){const e=j();return e.tenantId=n.firebaseTenantId,t.firebase.providerType=n.provider??"oidc",console.log(`Tenant ID set to ${e.tenantId}`),n}return void console.error(`Invalid response format, missing tenantId for ${e}`)}catch(e){if(!(o>1))throw e;console.warn(`Tenant resolution failed, retrying in ${n}ms...`,e),await new Promise(e=>setTimeout(e,n)),n*=2,o--}}catch(e){console.error("Error setting Firebase tenant:",e instanceof Error?e.message:"Unknown error")}else console.log("Firebase configuration is not available in the environment");else console.log("No tenant name or email specified, skipping tenant setup")}async function A(e){const o=j().currentUser;return o?o.getIdToken(e).then(n=>(t.logger.info("Got Firebase token",{vertesia:{user_email:o.email,user_name:o.displayName,user_id:o.uid,refresh:e}}),n)).catch(n=>(t.logger.error("Failed to get Firebase token",{vertesia:{user_email:o.email,user_name:o.displayName,user_id:o.uid,refresh:e,error:n}}),console.error("Failed to get access token",n),null)):(t.logger.warn("No user found"),Promise.resolve(null))}async function U(o,n,r,i,s=0){console.log(`Getting/refreshing composable token for account ${n} and project ${r} `),t.logger.info("Getting/refreshing composable token",{vertesia:{account_id:n,project_id:r,retry_count:s}});const a=await o();if(!a)throw console.log("No id token found - using cookie auth"),new Error("No id token found");const c=t.endpoints.sts;console.log("Using STS for token generation:",c),t.logger.info("Using STS for token generation",{vertesia:{account_id:n,project_id:r,sts_url:c}});try{const l=new URL(c+"/token/issue"),u={type:"user",account_id:n,project_id:r,expires_at:i?Math.floor(Date.now()/1e3)+i:void 0},d=await fetch(l,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify(u)});if(a&&404===d?.status){console.log("404: User not found - calling ensure-user endpoint"),t.logger.info("404: User not found - calling ensure-user endpoint",{vertesia:{account_id:n,project_id:r,status:d?.status}});const c=await fetch(t.endpoints.studio+"/auth/ensure-user",{method:"POST",headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"}});if(412===c.status){console.log("412: No invite found - signup required"),t.logger.info("412: No invite found - signup required",{vertesia:{account_id:n,project_id:r}});const o=e(a);if(!o?.email)throw t.logger.error("No email found in id token"),new Error("No email found in id token");throw new P("User not found - signup required",o.email)}if(!c.ok)throw console.error("Failed to ensure user exists",c.status),t.logger.error("Failed to ensure user exists",{vertesia:{account_id:n,project_id:r,status:c.status}}),new Error("Failed to ensure user exists");return console.log("User ensured - retrying token generation"),t.logger.info("User ensured - retrying token generation",{vertesia:{account_id:n,project_id:r}}),U(o,n,r,i,s)}if(a&&412===d?.status){console.log("412: auth succeeded but user doesn't exist - signup required",d?.status),t.logger.error("412: auth succeeded but user doesn't exist - signup required",{vertesia:{account_id:n,project_id:r,status:d?.status}});const o=e(a);if(!o?.email)throw t.logger.error("No email found in id token"),new Error("No email found in id token");throw t.logger.error("User not found",{vertesia:{account_id:n,project_id:r,email:o.email}}),new P("User not found",o.email)}if(403===d.status){if(s>0)throw console.error("403: Access denied even without account scope - user may have no accounts"),t.logger.error("403: Access denied after retry - authorization failure",{vertesia:{account_id:n,project_id:r,status:d.status,retry_count:s}}),new Error("Access denied - user may not have access to any accounts");return console.log("403: Access denied - clearing cached account and retrying without account scope"),t.logger.warn("403: Access denied - clearing cached account and retrying",{vertesia:{account_id:n,project_id:r,status:d.status,retry_count:s}}),localStorage.removeItem(p),n&&localStorage.removeItem(w+"-"+n),U(o,void 0,void 0,i,s+1)}if(!d.ok){const e=await d.text();throw console.error("STS token generation failed:",d.status,e),t.logger.error("STS token generation failed",{vertesia:{status:d.status,error:e,account_id:n,project_id:r}}),new Error(`Failed to get token from STS: ${d.status}`)}const{token:g}=await d.json();return console.log("Successfully got token from STS"),t.logger.info("Successfully got token from STS"),g}catch(e){if(e instanceof P)throw e;throw localStorage.removeItem(p),n&&localStorage.removeItem(w+"-"+n),console.error("Failed to get composable token from STS",e),t.logger.error("Failed to get composable token from STS",{vertesia:{account_id:n,project_id:r,error:e}}),new Error("Failed to get composable token")}}async function E(e,t,o){return U(A,e,t,o)}async function F(o,n,r,i=!1,s=!1){const a=o??localStorage.getItem(p)??void 0,c=n??localStorage.getItem(w+"-"+a)??void 0;if(!i&&v&&k&&k.exp>Date.now()/1e3+300)return{rawToken:v,token:k,error:!1};if(!s&&j().currentUser?v=await E(a,c):(r||v)&&(v=await U(()=>Promise.resolve(r??v),a,c)),!v)throw t.logger.error("Cannot acquire a composable token",{vertesia:{account_id:a,project_id:c}}),new Error("Cannot acquire a composable token");if(k=e(v),!k||!k.exp||!v)throw console.error("Invalid composable token",k),t.logger.error("Invalid composable token",{vertesia:{account_id:a,project_id:c}}),new Error("Invalid composable token");return{rawToken:v,token:k,error:!1}}class P extends Error{email;constructor(e,t){super(e),this.name="UserNotFoundError",this.email=t}}function $(){const{user:e}=h(),[t,o]=a(null),[n,r]=a(!0),[i,s]=a(null);return c(()=>{(async()=>{if(!e?.email)return o(null),void r(!1);try{const t=await fetch("/api/resolve-tenant",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({tenantEmail:e.email})});if(t.ok){const e=await t.json();o(e?{tenantKey:e.name||"unknown",name:e.label||e.name||"Unknown",domain:e.domain||[],firebaseTenantId:e.firebaseTenantId,provider:e.provider,logo:e.logo}:null)}else o(null)}catch(e){console.error("Error loading current tenant:",e),s("Failed to load tenant configuration"),o(null)}finally{r(!1)}})()},[e?.email]),{currentTenant:t,isLoading:n,error:i}}const N="auth_state",L="auth_state_expiry";function C(){return{generateState:l(()=>{const e=crypto.randomUUID(),t=Date.now()+3e5;return sessionStorage.setItem(N,e),sessionStorage.setItem(L,t.toString()),e},[]),verifyState:l(e=>{if(!e)return"Missing state";const t=sessionStorage.getItem(N),o=parseInt(sessionStorage.getItem(L)||"0");let n;return n=t!==e?`State mismatched (${t} !== ${e})`:Date.now()>o?"State expired":void 0,n},[]),clearState:l(()=>{sessionStorage.removeItem(N),sessionStorage.removeItem(L)},[])}}class x{isLoading=!0;client;authError;authToken;setSession;lastSelectedAccount;lastSelectedProject;onboardingComplete;constructor(e,o){this.client=e||new f({serverUrl:t.endpoints.studio,storeUrl:t.endpoints.zeno,tokenServerUrl:t.endpoints.sts}),o&&(this.setSession=o),this.logout=this.logout.bind(this)}get store(){return this.client.store}get user(){return this.authToken}get account(){return this.authToken?.account}get project(){return this.authToken?.project}get accounts(){return this.authToken?.accounts}get authCallback(){return this.rawAuthToken.then(e=>`Bearer ${e}`)}get rawAuthToken(){return F().then(t=>{const o=t?.rawToken;if(!o)throw new Error("No token available");return this.authToken=e(o),o})}signOut(){this.logout()}getAccount(){return this.authToken?.account}async login(o){return this.authError=void 0,this.isLoading=!1,this.client.withAuthCallback(()=>this.authCallback),this.authToken=e(o),console.log(`Logging in as ${this.authToken?.name} with account ${this.authToken?.account.name} (${this.authToken?.account.id}, and project ${this.authToken?.project?.name} (${this.authToken?.project?.id})`),localStorage.setItem(p,this.authToken.account.id),localStorage.setItem(w+"-"+this.authToken.account.id,this.authToken.project?.id??""),t.onLogin?.(this.authToken),await this.fetchOnboardingStatus(),Promise.resolve()}isLoggedIn(){return!!this.authToken}logout(){console.log("Logging out");if(t.isDocker||[".composable.sh",".vertesia.dev","vertesia.app"].some(e=>window.location.hostname.endsWith(e))){console.log("Using central auth logout"),this.authError=void 0,this.isLoading=!1,this.authToken=void 0,this.setSession=void 0,this.client.withAuthCallback(void 0);const e=new URL("https://internal-auth.vertesia.app/"),t=new URL(window.location.href);t.hash="",e.pathname="/logout",e.searchParams.set("redirect_uri",t.toString()),location.replace(e.toString())}else console.log("Using Firebase logout"),this.authToken&&j().signOut(),this.authError=void 0,this.isLoading=!1,this.authToken=void 0,this.setSession=void 0,this.client.withAuthCallback(void 0)}async switchAccount(e){localStorage.setItem(p,e),this&&(this.account&&this.project?localStorage.setItem(w+"-"+this.account.id,this.project.id):this.account&&localStorage.removeItem(w+"-"+this.account.id)),window.location.replace("/?a="+e)}async switchProject(e){this.account&&localStorage.setItem(w+"-"+this.account.id,e),window.location.replace("/?a="+this.account?.id+"&p="+e)}async fetchAccounts(){return this.client.accounts.list().then(e=>{if(!this.authToken)throw new Error("No token available");this.authToken.accounts=e,this.setSession?.(this.clone())}).catch(e=>{throw console.error("Failed to fetch accounts",e),e})}async fetchOnboardingStatus(){if(this.onboardingComplete)return console.log("Onboarding already completed"),!1;const e=this.onboardingComplete;try{const t=await this.client.account.onboardingProgress();if(this.onboardingComplete=Object.values(t).every(e=>!0===e),e!==this.onboardingComplete)return!0;this.setSession?.(this.clone())}catch(e){console.error("Error fetching onboarding status:",e),this.onboardingComplete=!1,this.setSession?.(this.clone())}return!1}clone(){const e=new x(this.client);return e.isLoading=this.isLoading,e.authError=this.authError,e.authToken=this.authToken,e.setSession=this.setSession,e.lastSelectedAccount=this.lastSelectedAccount,e.switchAccount=this.switchAccount,e.onboardingComplete=this.onboardingComplete,e}}const D=u(void 0);function O(){const e=d(D);if(!e)throw new Error("useUserSession must be used within a UserSessionProvider");return e}const R=[".composable.sh",".vertesia.dev","vertesia.app"];function q(){return!!t.isDocker||R.some(e=>window.location.hostname.endsWith(e))}function z({children:e}){const o=new URLSearchParams(location.hash.substring(1)),n=o.get("token"),r=o.get("state"),[i,l]=a(new x),{generateState:u,verifyState:d,clearState:h}=C(),f=g(!1),v=(e,o)=>{const n=new URL(`https://internal-auth.vertesia.app/?sts=${t.endpoints.sts??"https://sts.vertesia.io"}`),r=new URL(window.location.href);r.hash="",n.searchParams.set("redirect_uri",r.toString()),n.searchParams.set("state",u()),location.replace(n.toString())};return c(()=>{if(f.current)return void console.log("Auth: skipping duplicate auth flow initiation");f.current=!0,console.log("Auth: starting auth flow"),t.logger.info("Starting auth flow");const e=new URL(window.location.href),o=e.searchParams.get("a")??localStorage.getItem(p)??void 0,a=e.searchParams.get("p")??localStorage.getItem(w+"-"+o)??void 0;if(console.log("Auth: selected account",o),console.log("Auth: selected project",a),t.logger.info("Selected account and project",{vertesia:{account_id:o,project_id:a}}),n&&r){const e=d(r);return e?(console.error(`Auth: invalid state: ${e}`),t.logger.error(`Invalid state: ${e}`,{vertesia:{state:r}}),v()):h(),void F(o,a,n,!1,q()).then(e=>{i.login(e.rawToken).then(()=>{l(i.clone()),window.location.hash=""})}).catch(e=>{if(e instanceof P)return console.log("User not found - will trigger signup flow",e),i.isLoading=!1,i.authError=e,void l(i.clone());console.error("Failed to fetch user token from studio, redirecting to central auth",e),t.logger.error("Failed to fetch user token from studio, redirecting to central auth",{vertesia:{error:e}}),v()})}if(!i.isLoggedIn()){if(console.log("Auth: not logged in & no token/state"),t.logger.info("Not logged in & no token/state",{vertesia:{account_id:o,project_id:a}}),q())return console.log("Auth: on dev domain, redirecting to central auth with selection",o,a),t.logger.info("Redirecting to central auth with selection",{vertesia:{account_id:o,project_id:a}}),void v();console.log("Auth: not on dev domain"),t.logger.info("Not on dev domain",{vertesia:{account_id:o,project_id:a}})}return s(j(),async e=>{e?(console.log("Auth: successful login with firebase"),t.logger.info("Successful login with firebase",{vertesia:{account_id:o,project_id:a}}),i.setSession=l,await F(o,a,void 0,!1,q()).then(e=>{i.login(e.rawToken).then(()=>l(i.clone()))}).catch(e=>{console.error("Failed to fetch user token from studio",e),t.logger.error("Failed to fetch user token from studio",{vertesia:{account_id:o,project_id:a,error:e}}),e instanceof P||i.logout(),i.isLoading=!1,i.authError=e,l(i.clone())})):(console.log("Auth: using anonymous user"),t.logger.info("Using anonymous user",{vertesia:{account_id:o,project_id:a}}),i.client.withAuthCallback(void 0),i.logout(),l(i.clone()))})},[]),m(D.Provider,{value:i,children:e})}function B(){return{tagUserSession:async e=>{const t=window.localStorage.getItem("composableSignupData");e?t&&window.localStorage.removeItem("composableSignupData"):console.error("No user found -- skipping tagging")},trackEvent:(e,o)=>{t.isProd||console.debug("track event",e,o),n(_(),e,{...o,debug_mode:!t.isProd})}}}export{p as LastSelectedAccountId_KEY,w as LastSelectedProjectId_KEY,P as UserNotFoundError,x as UserSession,D as UserSessionContext,z as UserSessionProvider,U as fetchComposableToken,E as fetchComposableTokenFromFirebaseToken,F as getComposableToken,_ as getFirebaseAnalytics,y as getFirebaseApp,j as getFirebaseAuth,A as getFirebaseAuthToken,I as setFirebaseTenant,q as shouldRedirectToCentralAuth,C as useAuthState,$ as useCurrentTenant,B as useUXTracking,O as useUserSession};
|
|
2
2
|
//# sourceMappingURL=vertesia-ui-session.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vertesia-ui-session.js","sources":["esm/session/constants.js","esm/session/auth/firebase.js","esm/session/auth/composable.js","esm/session/auth/useCurrentTenant.js","esm/session/auth/useAuthState.js","esm/session/TypeRegistry.js","esm/session/UserSession.js","esm/session/UserSessionProvider.js","esm/session/useUXTracking.js"],"sourcesContent":["export const LastSelectedAccountId_KEY = 'composableai.lastSelectedAccountId';\nexport const LastSelectedProjectId_KEY = 'composableai.lastSelectedProjectId';\n//# sourceMappingURL=constants.js.map","import { Env } from \"@vertesia/ui/env\";\nimport { getAnalytics } from \"firebase/analytics\";\nimport { initializeApp } from \"firebase/app\";\nimport { getAuth } from \"firebase/auth\";\n// Use lazy initialization to avoid accessing Env before it's initialized\nlet _firebaseApp = null;\nlet _analytics = null;\nlet _firebaseAuth = null;\n// Getters that lazily initialize Firebase components when first accessed\nexport function getFirebaseApp() {\n if (!_firebaseApp) {\n try {\n if (!Env.firebase) {\n throw new Error(\"Firebase configuration is not available in the environment\");\n }\n _firebaseApp = initializeApp(Env.firebase);\n }\n catch (error) {\n console.error(\"Failed to initialize Firebase app:\", error);\n throw new Error(\"Firebase initialization failed - environment may not be properly initialized\");\n }\n }\n return _firebaseApp;\n}\nexport function getFirebaseAnalytics() {\n if (!_analytics) {\n _analytics = getAnalytics(getFirebaseApp());\n }\n return _analytics;\n}\nexport function getFirebaseAuth() {\n if (!_firebaseAuth) {\n _firebaseAuth = getAuth(getFirebaseApp());\n }\n return _firebaseAuth;\n}\nexport async function setFirebaseTenant(tenantEmail) {\n if (!tenantEmail) {\n console.log(\"No tenant name or email specified, skipping tenant setup\");\n return;\n }\n if (!Env.firebase) {\n console.log(\"Firebase configuration is not available in the environment\");\n return;\n }\n try {\n if (tenantEmail)\n console.log(`Resolving tenant ID from email: ${tenantEmail}`);\n // Add retry logic with exponential backoff\n let retries = 3;\n let retryDelay = 250; // Start with 250ms delay\n while (retries > 0) {\n try {\n // Call the API endpoint to resolve the tenant ID\n const response = await fetch(\"/api/resolve-tenant\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n tenantEmail: tenantEmail,\n }),\n // Add timeout to prevent hanging requests\n signal: AbortSignal.timeout(5000),\n });\n // Check for network errors\n if (!response) {\n throw new Error(\"No response received from tenant API\");\n }\n // Handle HTTP error responses\n if (!response.ok) {\n // Try to parse the error response\n try {\n const errorData = await response.json();\n console.error(\"Failed to resolve tenant ID:\", errorData.error);\n }\n catch (parseError) {\n console.error(`Failed to resolve tenant ID: HTTP ${response.status}`);\n }\n // If the error is 404 Not Found, no need to retry\n if (response.status === 404) {\n console.warn(`Tenant not found for ${tenantEmail}`);\n return;\n }\n throw new Error(`HTTP error ${response.status}`);\n }\n // Successfully got a response, parse it\n const data = (await response.json());\n if (data && data.firebaseTenantId) {\n const auth = getFirebaseAuth();\n auth.tenantId = data.firebaseTenantId;\n Env.firebase.providerType = data.provider ?? \"oidc\";\n console.log(`Tenant ID set to ${auth.tenantId}`);\n return data;\n }\n else {\n console.error(`Invalid response format, missing tenantId for ${tenantEmail}`);\n return; // No need to retry for invalid response format\n }\n }\n catch (fetchError) {\n // Only retry for network-related errors\n if (retries > 1) {\n console.warn(`Tenant resolution failed, retrying in ${retryDelay}ms...`, fetchError);\n await new Promise((resolve) => setTimeout(resolve, retryDelay));\n retryDelay *= 2; // Exponential backoff\n retries--;\n }\n else {\n throw fetchError; // Last retry failed, propagate error\n }\n }\n }\n }\n catch (error) {\n // Final error handler\n console.error(\"Error setting Firebase tenant:\", error instanceof Error ? error.message : \"Unknown error\");\n // Continue without tenant ID - authentication will work without multi-tenancy\n // but the user will access the default tenant\n }\n}\nexport async function getFirebaseAuthToken(refresh) {\n const auth = getFirebaseAuth();\n const user = auth.currentUser;\n if (user) {\n return user\n .getIdToken(refresh)\n .then((token) => {\n Env.logger.info(\"Got Firebase token\", {\n vertesia: {\n user_email: user.email,\n user_name: user.displayName,\n user_id: user.uid,\n refresh: refresh,\n },\n });\n return token;\n })\n .catch((err) => {\n Env.logger.error(\"Failed to get Firebase token\", {\n vertesia: {\n user_email: user.email,\n user_name: user.displayName,\n user_id: user.uid,\n refresh: refresh,\n error: err,\n },\n });\n console.error(\"Failed to get access token\", err);\n return null;\n });\n }\n else {\n Env.logger.warn(\"No user found\");\n return Promise.resolve(null);\n }\n}\n//# sourceMappingURL=firebase.js.map","import { jwtDecode } from \"jwt-decode\";\nimport { Env } from '@vertesia/ui/env';\nimport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY } from '../constants';\nimport { getFirebaseAuth, getFirebaseAuthToken } from './firebase';\nlet AUTH_TOKEN_RAW;\nlet AUTH_TOKEN;\nexport async function fetchComposableToken(getIdToken, accountId, projectId, ttl, retryCount = 0) {\n console.log(`Getting/refreshing composable token for account ${accountId} and project ${projectId} `);\n Env.logger.info('Getting/refreshing composable token', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n retry_count: retryCount,\n },\n });\n const idToken = await getIdToken(); //get from firebase\n if (!idToken) {\n console.log('No id token found - using cookie auth');\n throw new Error('No id token found');\n }\n // Use STS endpoint - either configured or default to sts.vertesia.io\n const stsEndpoint = Env.endpoints.sts;\n console.log('Using STS for token generation:', stsEndpoint);\n Env.logger.info('Using STS for token generation', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n sts_url: stsEndpoint,\n },\n });\n try {\n // Call STS to generate a user token\n const stsUrl = new URL(stsEndpoint + '/token/issue');\n const requestBody = {\n type: 'user',\n account_id: accountId,\n project_id: projectId,\n expires_at: ttl ? Math.floor(Date.now() / 1000) + ttl : undefined,\n };\n const stsRes = await fetch(stsUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${idToken}` // Firebase token for authentication\n },\n body: JSON.stringify(requestBody)\n });\n if (idToken && stsRes?.status === 404) {\n // User not found in token-server - call ensure-user endpoint\n console.log('404: User not found - calling ensure-user endpoint');\n Env.logger.info('404: User not found - calling ensure-user endpoint', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes?.status\n },\n });\n const ensureResponse = await fetch(Env.endpoints.studio + '/auth/ensure-user', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${idToken}`,\n 'Content-Type': 'application/json'\n }\n });\n if (ensureResponse.status === 412) {\n // No invite - trigger signup\n console.log('412: No invite found - signup required');\n Env.logger.info('412: No invite found - signup required', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n }\n });\n const idTokenDecoded = jwtDecode(idToken);\n if (!idTokenDecoded?.email) {\n Env.logger.error('No email found in id token');\n throw new Error('No email found in id token');\n }\n throw new UserNotFoundError('User not found - signup required', idTokenDecoded.email);\n }\n if (!ensureResponse.ok) {\n console.error('Failed to ensure user exists', ensureResponse.status);\n Env.logger.error('Failed to ensure user exists', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: ensureResponse.status,\n },\n });\n throw new Error('Failed to ensure user exists');\n }\n // User created/exists - retry token generation\n console.log('User ensured - retrying token generation');\n Env.logger.info('User ensured - retrying token generation', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n }\n });\n return fetchComposableToken(getIdToken, accountId, projectId, ttl, retryCount);\n }\n if (idToken && stsRes?.status === 412) {\n console.log(\"412: auth succeeded but user doesn't exist - signup required\", stsRes?.status);\n Env.logger.error(\"412: auth succeeded but user doesn't exist - signup required\", {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes?.status\n },\n });\n const idTokenDecoded = jwtDecode(idToken);\n if (!idTokenDecoded?.email) {\n Env.logger.error('No email found in id token');\n throw new Error('No email found in id token');\n }\n Env.logger.error('User not found', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n email: idTokenDecoded.email\n }\n });\n throw new UserNotFoundError('User not found', idTokenDecoded.email);\n }\n if (stsRes.status === 403) {\n // User doesn't have access to the requested account/project, or has no accounts\n // This can happen with:\n // 1. Stale localStorage from previous user\n // 2. User invited to a new account (doesn't have access yet)\n // 3. User exists but has no accounts at all\n if (retryCount > 0) {\n // Already retried without account scope - this is a real authorization failure\n console.error('403: Access denied even without account scope - user may have no accounts');\n Env.logger.error('403: Access denied after retry - authorization failure', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes.status,\n retry_count: retryCount\n },\n });\n throw new Error('Access denied - user may not have access to any accounts');\n }\n console.log('403: Access denied - clearing cached account and retrying without account scope');\n Env.logger.warn('403: Access denied - clearing cached account and retrying', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes.status,\n retry_count: retryCount\n },\n });\n // Clear any stale account/project from localStorage\n localStorage.removeItem(LastSelectedAccountId_KEY);\n if (accountId) {\n localStorage.removeItem(LastSelectedProjectId_KEY + '-' + accountId);\n }\n // Retry without account/project scope - let user log in to their default account\n return fetchComposableToken(getIdToken, undefined, undefined, ttl, retryCount + 1);\n }\n if (!stsRes.ok) {\n const errorText = await stsRes.text();\n console.error('STS token generation failed:', stsRes.status, errorText);\n Env.logger.error('STS token generation failed', {\n vertesia: {\n status: stsRes.status,\n error: errorText,\n account_id: accountId,\n project_id: projectId,\n },\n });\n throw new Error(`Failed to get token from STS: ${stsRes.status}`);\n }\n const { token } = await stsRes.json();\n console.log('Successfully got token from STS');\n Env.logger.info('Successfully got token from STS');\n return token;\n }\n catch (error) {\n if (error instanceof UserNotFoundError) {\n throw error; // Re-throw UserNotFoundError\n }\n // Clear any stale account/project from localStorage on error\n localStorage.removeItem(LastSelectedAccountId_KEY);\n if (accountId) {\n localStorage.removeItem(LastSelectedProjectId_KEY + '-' + accountId);\n }\n console.error('Failed to get composable token from STS', error);\n Env.logger.error('Failed to get composable token from STS', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n error: error,\n },\n });\n throw new Error('Failed to get composable token');\n }\n}\n/**\n *\n * @param accountId\n * @param projectId\n * @param ttl time to live for the token in seconds\n * @returns\n */\nexport async function fetchComposableTokenFromFirebaseToken(accountId, projectId, ttl) {\n return fetchComposableToken(getFirebaseAuthToken, accountId, projectId, ttl);\n}\nexport async function getComposableToken(accountId, projectId, initToken, forceRefresh = false, useInternalAuth = false) {\n const selectedAccount = accountId ?? localStorage.getItem(LastSelectedAccountId_KEY) ?? undefined;\n const selectedProject = projectId ?? localStorage.getItem(LastSelectedProjectId_KEY + '-' + selectedAccount) ?? undefined;\n //token is still valid for more than 5 minutes\n if (!forceRefresh && AUTH_TOKEN_RAW && AUTH_TOKEN && AUTH_TOKEN.exp > (Date.now() / 1000 + 300)) {\n return { rawToken: AUTH_TOKEN_RAW, token: AUTH_TOKEN, error: false };\n }\n //token is close to expire, refresh it\n if (!useInternalAuth && getFirebaseAuth().currentUser) {\n //we have a firebase user, get the token from there\n AUTH_TOKEN_RAW = await fetchComposableTokenFromFirebaseToken(selectedAccount, selectedProject);\n }\n else if (initToken || AUTH_TOKEN_RAW) {\n // we have a token already and no firebase user, refresh it\n AUTH_TOKEN_RAW = await fetchComposableToken(() => Promise.resolve(initToken ?? AUTH_TOKEN_RAW), selectedAccount, selectedProject);\n }\n if (!AUTH_TOKEN_RAW) {\n Env.logger.error('Cannot acquire a composable token', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n throw new Error('Cannot acquire a composable token');\n }\n AUTH_TOKEN = jwtDecode(AUTH_TOKEN_RAW);\n if (!AUTH_TOKEN || !AUTH_TOKEN.exp || !AUTH_TOKEN_RAW) {\n console.error('Invalid composable token', AUTH_TOKEN);\n Env.logger.error('Invalid composable token', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n throw new Error('Invalid composable token');\n }\n return { rawToken: AUTH_TOKEN_RAW, token: AUTH_TOKEN, error: false };\n}\nexport class UserNotFoundError extends Error {\n email;\n constructor(message, email) {\n super(message);\n this.name = 'UserNotFoundError';\n this.email = email;\n }\n}\n//# sourceMappingURL=composable.js.map","import { useState, useEffect } from 'react';\nimport { useUserSession } from \"@vertesia/ui/session\";\nexport function useCurrentTenant() {\n const { user } = useUserSession();\n const [currentTenant, setCurrentTenant] = useState(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState(null);\n useEffect(() => {\n const loadCurrentTenant = async () => {\n if (!user?.email) {\n setCurrentTenant(null);\n setIsLoading(false);\n return;\n }\n try {\n const response = await fetch('/api/resolve-tenant', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n tenantEmail: user.email\n })\n });\n if (response.ok) {\n const tenantData = await response.json();\n if (tenantData) {\n // Convert the resolved tenant data to our TenantConfig format\n setCurrentTenant({\n tenantKey: tenantData.name || 'unknown',\n name: tenantData.label || tenantData.name || 'Unknown',\n domain: tenantData.domain || [],\n firebaseTenantId: tenantData.firebaseTenantId,\n provider: tenantData.provider,\n logo: tenantData.logo\n });\n }\n else {\n setCurrentTenant(null);\n }\n }\n else {\n setCurrentTenant(null);\n }\n }\n catch (error) {\n console.error('Error loading current tenant:', error);\n setError('Failed to load tenant configuration');\n setCurrentTenant(null);\n }\n finally {\n setIsLoading(false);\n }\n };\n loadCurrentTenant();\n }, [user?.email]);\n return {\n currentTenant,\n isLoading,\n error\n };\n}\n//# sourceMappingURL=useCurrentTenant.js.map","/**\n * This hook is used to generate and verify state for OAuth2 authorization requests.\n * @returns\n */\nimport { useCallback } from \"react\";\nconst AUTH_STATE_KEY = 'auth_state';\nconst STATE_EXPIRY_KEY = 'auth_state_expiry';\nconst STATE_TTL = 5 * 60 * 1000; // 5 min\nexport function useAuthState() {\n // Generate new state\n const generateState = useCallback(() => {\n const state = crypto.randomUUID();\n const expiryTime = Date.now() + STATE_TTL;\n // Store state and expiry\n sessionStorage.setItem(AUTH_STATE_KEY, state);\n sessionStorage.setItem(STATE_EXPIRY_KEY, expiryTime.toString());\n return state;\n }, []);\n // Verify returned state\n const verifyState = useCallback((returnedState) => {\n if (!returnedState) {\n return 'Missing state';\n }\n const savedState = sessionStorage.getItem(AUTH_STATE_KEY);\n const expiryTime = parseInt(sessionStorage.getItem(STATE_EXPIRY_KEY) || '0');\n let reason;\n // Verify state matches and hasn't expired\n if (savedState !== returnedState) {\n reason = `State mismatched (${savedState} !== ${returnedState})`;\n }\n else if (Date.now() > expiryTime) {\n reason = 'State expired';\n }\n else {\n reason = undefined; // No errors\n }\n return reason;\n }, []);\n // Clear state (useful for cleanup)\n const clearState = useCallback(() => {\n sessionStorage.removeItem(AUTH_STATE_KEY);\n sessionStorage.removeItem(STATE_EXPIRY_KEY);\n }, []);\n return { generateState, verifyState, clearState };\n}\n//# sourceMappingURL=useAuthState.js.map","export class TypeRegistry {\n types;\n map = {};\n constructor(types) {\n this.types = types;\n //sort types\n types.sort((a, b) => a.name.localeCompare(b.name));\n for (const type of types) {\n this.map[type.id] = type;\n }\n }\n getType(id) {\n return this.map[id];\n }\n getTypeLayout(id) {\n const type = this.map[id];\n return type ? type.table_layout : undefined;\n }\n getTypeName(id) {\n const type = this.map[id];\n return type ? type.name : undefined;\n }\n}\n//# sourceMappingURL=TypeRegistry.js.map","import { jwtDecode } from 'jwt-decode';\nimport { createContext, useContext } from 'react';\nimport { VertesiaClient } from '@vertesia/client';\nimport { Env } from '@vertesia/ui/env';\nimport { getComposableToken } from './auth/composable';\nimport { getFirebaseAuth } from './auth/firebase';\nimport { TypeRegistry } from './TypeRegistry';\nimport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY } from './constants';\nexport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY };\nconst CENTRAL_AUTH_REDIRECT = \"https://internal-auth.vertesia.app/\";\nclass UserSession {\n isLoading = true;\n client;\n authError;\n authToken;\n typeRegistry;\n setSession;\n lastSelectedAccount;\n lastSelectedProject;\n onboardingComplete;\n constructor(client, setSession) {\n if (client) {\n this.client = client;\n }\n else {\n this.client = new VertesiaClient({\n serverUrl: Env.endpoints.studio,\n storeUrl: Env.endpoints.zeno,\n tokenServerUrl: Env.endpoints.sts\n });\n }\n if (setSession) {\n this.setSession = setSession;\n }\n this.logout = this.logout.bind(this);\n }\n get store() {\n return this.client.store;\n }\n get user() {\n return this.authToken;\n }\n get account() {\n return this.authToken?.account;\n }\n get project() {\n return this.authToken?.project;\n }\n get accounts() {\n return this.authToken?.accounts;\n }\n get authCallback() {\n return this.rawAuthToken.then(token => `Bearer ${token}`);\n }\n get rawAuthToken() {\n return getComposableToken().then(res => {\n const token = res?.rawToken;\n if (!token) {\n throw new Error('No token available');\n }\n this.authToken = jwtDecode(token);\n return token;\n });\n }\n signOut() {\n this.logout();\n }\n getAccount() {\n return this.authToken?.account;\n }\n async login(token) {\n this.authError = undefined;\n this.isLoading = false;\n this.client.withAuthCallback(() => this.authCallback);\n this.authToken = jwtDecode(token);\n console.log(`Logging in as ${this.authToken?.name} with account ${this.authToken?.account.name} (${this.authToken?.account.id}, and project ${this.authToken?.project?.name} (${this.authToken?.project?.id})`);\n //store selected account in local storage\n localStorage.setItem(LastSelectedAccountId_KEY, this.authToken.account.id);\n localStorage.setItem(LastSelectedProjectId_KEY + '-' + this.authToken.account.id, this.authToken.project?.id ?? '');\n // notify the host app of the login\n Env.onLogin?.(this.authToken);\n // Independent async calls\n await Promise.all([\n this._loadTypes(),\n this.fetchOnboardingStatus(),\n ]);\n return Promise.resolve();\n }\n isLoggedIn() {\n return !!this.authToken;\n }\n logout() {\n console.log('Logging out');\n // Check if we should use central auth for logout\n const devDomains = [\".composable.sh\", \".vertesia.dev\", \"vertesia.app\"];\n const shouldUseCentralAuth = Env.isDocker || devDomains.some((domain) => window.location.hostname.endsWith(domain));\n if (shouldUseCentralAuth) {\n // Redirect to central auth for logout\n // Central auth will handle Firebase logout\n console.log('Using central auth logout');\n this.authError = undefined;\n this.isLoading = false;\n this.authToken = undefined;\n this.typeRegistry = undefined;\n this.setSession = undefined;\n this.client.withAuthCallback(undefined);\n const logoutUrl = new URL(CENTRAL_AUTH_REDIRECT);\n const currentUrl = new URL(window.location.href);\n currentUrl.hash = \"\";\n logoutUrl.pathname = \"/logout\";\n logoutUrl.searchParams.set(\"redirect_uri\", currentUrl.toString());\n location.replace(logoutUrl.toString());\n }\n else {\n // Use Firebase logout directly\n console.log('Using Firebase logout');\n if (this.authToken) {\n getFirebaseAuth().signOut();\n }\n this.authError = undefined;\n this.isLoading = false;\n this.authToken = undefined;\n this.typeRegistry = undefined;\n this.setSession = undefined;\n this.client.withAuthCallback(undefined);\n }\n }\n async switchAccount(targetAccountId) {\n localStorage.setItem(LastSelectedAccountId_KEY, targetAccountId);\n if (this) {\n if (this.account && this.project) {\n localStorage.setItem(LastSelectedProjectId_KEY + '-' + this.account.id, this.project.id);\n }\n else if (this.account) {\n localStorage.removeItem(LastSelectedProjectId_KEY + '-' + this.account.id);\n }\n }\n window.location.replace('/?a=' + targetAccountId);\n }\n async switchProject(targetProjectId) {\n if (this.account) {\n localStorage.setItem(LastSelectedProjectId_KEY + '-' + this.account.id, targetProjectId);\n }\n window.location.replace('/?a=' + this.account?.id + '&p=' + targetProjectId);\n }\n async _loadTypes() {\n if (this.project) {\n return this.store.types.catalog.list({ layout: true }).then(types => this.typeRegistry = new TypeRegistry(types)).catch(err => {\n //return this.store.types.list({}, { layout: true }).then(types => this.typeRegistry = new TypeRegistry(types)).catch(err => {\n console.error('Failed to fetch object types', err);\n throw err;\n });\n }\n else {\n console.log('No project selected');\n }\n }\n async reloadTypes() {\n return this._loadTypes().then(() => {\n this.setSession?.(this.clone());\n });\n }\n async fetchAccounts() {\n return this.client.accounts.list().then(accounts => {\n if (!this.authToken) {\n throw new Error('No token available');\n }\n this.authToken.accounts = accounts;\n this.setSession?.(this.clone());\n }).catch(err => {\n console.error('Failed to fetch accounts', err);\n throw err;\n });\n }\n async fetchOnboardingStatus() {\n if (this.onboardingComplete) {\n console.log('Onboarding already completed');\n return false;\n }\n const previousStatus = this.onboardingComplete;\n try {\n const onboarding = await this.client.account.onboardingProgress();\n this.onboardingComplete = Object.values(onboarding).every(value => value === true);\n if (previousStatus !== this.onboardingComplete) {\n return true;\n }\n this.setSession?.(this.clone());\n }\n catch (error) {\n console.error('Error fetching onboarding status:', error);\n this.onboardingComplete = false;\n this.setSession?.(this.clone());\n }\n return false;\n }\n clone() {\n const session = new UserSession(this.client);\n session.isLoading = this.isLoading;\n session.authError = this.authError;\n session.authToken = this.authToken;\n session.setSession = this.setSession;\n session.lastSelectedAccount = this.lastSelectedAccount;\n session.switchAccount = this.switchAccount;\n session.typeRegistry = this.typeRegistry;\n session.onboardingComplete = this.onboardingComplete;\n return session;\n }\n}\nconst UserSessionContext = createContext(undefined);\nexport function useUserSession() {\n const session = useContext(UserSessionContext);\n if (!session) {\n throw new Error('useUserSession must be used within a UserSessionProvider');\n }\n return session;\n}\nexport { UserSession, UserSessionContext };\n//# sourceMappingURL=UserSession.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { Env } from \"@vertesia/ui/env\";\nimport { onAuthStateChanged } from \"firebase/auth\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { UserNotFoundError, getComposableToken } from \"./auth/composable\";\nimport { getFirebaseAuth } from \"./auth/firebase\";\nimport { useAuthState } from \"./auth/useAuthState\";\nimport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY, UserSession, UserSessionContext } from \"./UserSession\";\nconst devDomains = [\".composable.sh\", \".vertesia.dev\", \"vertesia.app\"];\nconst CENTRAL_AUTH_REDIRECT = \"https://internal-auth.vertesia.app/\";\nexport function shouldRedirectToCentralAuth() {\n // Authentication is not supported in Docker environment.\n // See https://github.com/vertesia/studio/wiki/Composable-UI-Hosting-Options\n if (Env.isDocker) {\n return true;\n }\n return devDomains.some((domain) => window.location.hostname.endsWith(domain));\n}\nexport function UserSessionProvider({ children }) {\n const hashParams = new URLSearchParams(location.hash.substring(1));\n const token = hashParams.get(\"token\");\n const state = hashParams.get(\"state\");\n const [session, setSession] = useState(new UserSession());\n const { generateState, verifyState, clearState } = useAuthState();\n const hasInitiatedAuthRef = useRef(false);\n const redirectToCentralAuth = (projectId, accountId) => {\n const url = new URL(`${CENTRAL_AUTH_REDIRECT}?sts=${Env.endpoints.sts ?? \"https://sts.vertesia.io\"}`);\n const currentUrl = new URL(window.location.href);\n currentUrl.hash = \"\";\n if (projectId)\n currentUrl.searchParams.set(\"p\", projectId);\n if (accountId)\n currentUrl.searchParams.set(\"a\", accountId);\n url.searchParams.set(\"redirect_uri\", currentUrl.toString());\n url.searchParams.set(\"state\", generateState());\n location.replace(url.toString());\n };\n useEffect(() => {\n // Make this effect idempotent - only run auth flow once\n if (hasInitiatedAuthRef.current) {\n console.log(\"Auth: skipping duplicate auth flow initiation\");\n return;\n }\n hasInitiatedAuthRef.current = true;\n console.log(\"Auth: starting auth flow\");\n Env.logger.info(\"Starting auth flow\");\n const currentUrl = new URL(window.location.href);\n const selectedAccount = currentUrl.searchParams.get(\"a\") ?? localStorage.getItem(LastSelectedAccountId_KEY) ?? undefined;\n const selectedProject = currentUrl.searchParams.get(\"p\") ??\n localStorage.getItem(LastSelectedProjectId_KEY + \"-\" + selectedAccount) ??\n undefined;\n console.log(\"Auth: selected account\", selectedAccount);\n console.log(\"Auth: selected project\", selectedProject);\n Env.logger.info(\"Selected account and project\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n if (token && state) {\n const validationError = verifyState(state);\n if (validationError) {\n console.error(`Auth: invalid state: ${validationError}`);\n Env.logger.error(`Invalid state: ${validationError}`, {\n vertesia: {\n state: state,\n },\n });\n redirectToCentralAuth();\n }\n else {\n clearState();\n }\n getComposableToken(selectedAccount, selectedProject, token, false, shouldRedirectToCentralAuth())\n .then((res) => {\n session.login(res.rawToken).then(() => {\n setSession(session.clone());\n //cleanup the hash\n window.location.hash = \"\";\n });\n })\n .catch((err) => {\n // Don't redirect to central auth for UserNotFoundError - let signup flow handle it\n if (err instanceof UserNotFoundError) {\n console.log(\"User not found - will trigger signup flow\", err);\n session.isLoading = false;\n session.authError = err;\n setSession(session.clone());\n return;\n }\n console.error(\"Failed to fetch user token from studio, redirecting to central auth\", err);\n Env.logger.error(\"Failed to fetch user token from studio, redirecting to central auth\", {\n vertesia: {\n error: err,\n },\n });\n redirectToCentralAuth();\n });\n return;\n }\n else {\n //if on a dev domain and not logged in, redirect to central auth\n if (!session.isLoggedIn()) {\n console.log(\"Auth: not logged in & no token/state\");\n Env.logger.info(\"Not logged in & no token/state\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n if (shouldRedirectToCentralAuth()) {\n console.log(\"Auth: on dev domain, redirecting to central auth with selection\", selectedAccount, selectedProject);\n Env.logger.info(\"Redirecting to central auth with selection\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n redirectToCentralAuth();\n return; // Don't register onAuthStateChanged listener when redirecting\n }\n else {\n console.log(\"Auth: not on dev domain\");\n Env.logger.info(\"Not on dev domain\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n }\n }\n }\n return onAuthStateChanged(getFirebaseAuth(), async (firebaseUser) => {\n if (firebaseUser) {\n console.log(\"Auth: successful login with firebase\");\n Env.logger.info(\"Successful login with firebase\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n session.setSession = setSession;\n await getComposableToken(selectedAccount, selectedProject, undefined, false, shouldRedirectToCentralAuth())\n .then((res) => {\n session.login(res.rawToken).then(() => setSession(session.clone()));\n })\n .catch((err) => {\n console.error(\"Failed to fetch user token from studio\", err);\n Env.logger.error(\"Failed to fetch user token from studio\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n error: err,\n },\n });\n if (!(err instanceof UserNotFoundError))\n session.logout();\n session.isLoading = false;\n session.authError = err;\n setSession(session.clone());\n });\n }\n else {\n // anonymous user\n console.log(\"Auth: using anonymous user\");\n Env.logger.info(\"Using anonymous user\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n session.client.withAuthCallback(undefined);\n session.logout();\n setSession(session.clone());\n }\n });\n }, []);\n return _jsx(UserSessionContext.Provider, { value: session, children: children });\n}\n//# sourceMappingURL=UserSessionProvider.js.map","import { Env } from '@vertesia/ui/env';\nimport { logEvent } from \"firebase/analytics\";\nimport { getFirebaseAnalytics } from \"./auth/firebase\";\nexport function useUXTracking() {\n //identify user in monitoring and UX systems\n const tagUserSession = async (user) => {\n const signupData = window.localStorage.getItem(\"composableSignupData\");\n if (!user) {\n console.error('No user found -- skipping tagging');\n return;\n }\n if (signupData) {\n window.localStorage.removeItem(\"composableSignupData\");\n }\n };\n //send event to analytics and UX systems\n const trackEvent = (eventName, eventProperties) => {\n if (!Env.isProd) {\n console.debug('track event', eventName, eventProperties);\n }\n //GA via firebase\n logEvent(getFirebaseAnalytics(), eventName, { ...eventProperties, debug_mode: !Env.isProd });\n };\n return {\n tagUserSession,\n trackEvent\n };\n}\n//# sourceMappingURL=useUXTracking.js.map"],"names":["LastSelectedAccountId_KEY","LastSelectedProjectId_KEY","AUTH_TOKEN_RAW","AUTH_TOKEN","_firebaseApp","_analytics","_firebaseAuth","getFirebaseApp","Env","firebase","Error","initializeApp","error","console","getFirebaseAnalytics","getAnalytics","getFirebaseAuth","getAuth","async","setFirebaseTenant","tenantEmail","log","retries","retryDelay","response","fetch","method","headers","body","JSON","stringify","signal","AbortSignal","timeout","ok","errorData","json","parseError","status","warn","data","firebaseTenantId","auth","tenantId","providerType","provider","fetchError","Promise","resolve","setTimeout","message","getFirebaseAuthToken","refresh","user","currentUser","getIdToken","then","token","logger","info","vertesia","user_email","email","user_name","displayName","user_id","uid","catch","err","fetchComposableToken","accountId","projectId","ttl","retryCount","account_id","project_id","retry_count","idToken","stsEndpoint","endpoints","sts","sts_url","stsUrl","URL","requestBody","type","expires_at","Math","floor","Date","now","undefined","stsRes","Authorization","ensureResponse","studio","idTokenDecoded","jwtDecode","UserNotFoundError","localStorage","removeItem","errorText","text","fetchComposableTokenFromFirebaseToken","getComposableToken","initToken","forceRefresh","useInternalAuth","selectedAccount","getItem","selectedProject","exp","rawToken","constructor","super","this","name","useCurrentTenant","useUserSession","currentTenant","setCurrentTenant","useState","isLoading","setIsLoading","setError","useEffect","tenantData","tenantKey","label","domain","logo","loadCurrentTenant","AUTH_STATE_KEY","STATE_EXPIRY_KEY","useAuthState","generateState","useCallback","state","crypto","randomUUID","expiryTime","sessionStorage","setItem","toString","verifyState","returnedState","savedState","parseInt","reason","clearState","TypeRegistry","types","map","sort","a","b","localeCompare","id","getType","getTypeLayout","table_layout","getTypeName","UserSession","client","authError","authToken","typeRegistry","setSession","lastSelectedAccount","lastSelectedProject","onboardingComplete","VertesiaClient","serverUrl","storeUrl","zeno","tokenServerUrl","logout","bind","store","account","project","accounts","authCallback","rawAuthToken","res","signOut","getAccount","login","withAuthCallback","onLogin","all","_loadTypes","fetchOnboardingStatus","isLoggedIn","isDocker","some","window","location","hostname","endsWith","logoutUrl","currentUrl","href","hash","pathname","searchParams","set","replace","switchAccount","targetAccountId","switchProject","targetProjectId","catalog","list","layout","reloadTypes","clone","fetchAccounts","previousStatus","onboarding","onboardingProgress","Object","values","every","value","session","UserSessionContext","createContext","useContext","devDomains","shouldRedirectToCentralAuth","UserSessionProvider","children","hashParams","URLSearchParams","substring","get","hasInitiatedAuthRef","useRef","redirectToCentralAuth","url","current","validationError","onAuthStateChanged","firebaseUser","_jsx","Provider","useUXTracking","tagUserSession","signupData","trackEvent","eventName","eventProperties","isProd","debug","logEvent","debug_mode"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vertesia-ui-session.js","sources":["esm/session/constants.js","esm/session/auth/firebase.js","esm/session/auth/composable.js","esm/session/auth/useCurrentTenant.js","esm/session/auth/useAuthState.js","esm/session/UserSession.js","esm/session/UserSessionProvider.js","esm/session/useUXTracking.js"],"sourcesContent":["export const LastSelectedAccountId_KEY = 'composableai.lastSelectedAccountId';\nexport const LastSelectedProjectId_KEY = 'composableai.lastSelectedProjectId';\n//# sourceMappingURL=constants.js.map","import { Env } from \"@vertesia/ui/env\";\nimport { getAnalytics } from \"firebase/analytics\";\nimport { initializeApp } from \"firebase/app\";\nimport { getAuth } from \"firebase/auth\";\n// Use lazy initialization to avoid accessing Env before it's initialized\nlet _firebaseApp = null;\nlet _analytics = null;\nlet _firebaseAuth = null;\n// Getters that lazily initialize Firebase components when first accessed\nexport function getFirebaseApp() {\n if (!_firebaseApp) {\n try {\n if (!Env.firebase) {\n throw new Error(\"Firebase configuration is not available in the environment\");\n }\n _firebaseApp = initializeApp(Env.firebase);\n }\n catch (error) {\n console.error(\"Failed to initialize Firebase app:\", error);\n throw new Error(\"Firebase initialization failed - environment may not be properly initialized\");\n }\n }\n return _firebaseApp;\n}\nexport function getFirebaseAnalytics() {\n if (!_analytics) {\n _analytics = getAnalytics(getFirebaseApp());\n }\n return _analytics;\n}\nexport function getFirebaseAuth() {\n if (!_firebaseAuth) {\n _firebaseAuth = getAuth(getFirebaseApp());\n }\n return _firebaseAuth;\n}\nexport async function setFirebaseTenant(tenantEmail) {\n if (!tenantEmail) {\n console.log(\"No tenant name or email specified, skipping tenant setup\");\n return;\n }\n if (!Env.firebase) {\n console.log(\"Firebase configuration is not available in the environment\");\n return;\n }\n try {\n if (tenantEmail)\n console.log(`Resolving tenant ID from email: ${tenantEmail}`);\n // Add retry logic with exponential backoff\n let retries = 3;\n let retryDelay = 250; // Start with 250ms delay\n while (retries > 0) {\n try {\n // Call the API endpoint to resolve the tenant ID\n const response = await fetch(\"/api/resolve-tenant\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n tenantEmail: tenantEmail,\n }),\n // Add timeout to prevent hanging requests\n signal: AbortSignal.timeout(5000),\n });\n // Check for network errors\n if (!response) {\n throw new Error(\"No response received from tenant API\");\n }\n // Handle HTTP error responses\n if (!response.ok) {\n // Try to parse the error response\n try {\n const errorData = await response.json();\n console.error(\"Failed to resolve tenant ID:\", errorData.error);\n }\n catch (parseError) {\n console.error(`Failed to resolve tenant ID: HTTP ${response.status}`);\n }\n // If the error is 404 Not Found, no need to retry\n if (response.status === 404) {\n console.warn(`Tenant not found for ${tenantEmail}`);\n return;\n }\n throw new Error(`HTTP error ${response.status}`);\n }\n // Successfully got a response, parse it\n const data = (await response.json());\n if (data && data.firebaseTenantId) {\n const auth = getFirebaseAuth();\n auth.tenantId = data.firebaseTenantId;\n Env.firebase.providerType = data.provider ?? \"oidc\";\n console.log(`Tenant ID set to ${auth.tenantId}`);\n return data;\n }\n else {\n console.error(`Invalid response format, missing tenantId for ${tenantEmail}`);\n return; // No need to retry for invalid response format\n }\n }\n catch (fetchError) {\n // Only retry for network-related errors\n if (retries > 1) {\n console.warn(`Tenant resolution failed, retrying in ${retryDelay}ms...`, fetchError);\n await new Promise((resolve) => setTimeout(resolve, retryDelay));\n retryDelay *= 2; // Exponential backoff\n retries--;\n }\n else {\n throw fetchError; // Last retry failed, propagate error\n }\n }\n }\n }\n catch (error) {\n // Final error handler\n console.error(\"Error setting Firebase tenant:\", error instanceof Error ? error.message : \"Unknown error\");\n // Continue without tenant ID - authentication will work without multi-tenancy\n // but the user will access the default tenant\n }\n}\nexport async function getFirebaseAuthToken(refresh) {\n const auth = getFirebaseAuth();\n const user = auth.currentUser;\n if (user) {\n return user\n .getIdToken(refresh)\n .then((token) => {\n Env.logger.info(\"Got Firebase token\", {\n vertesia: {\n user_email: user.email,\n user_name: user.displayName,\n user_id: user.uid,\n refresh: refresh,\n },\n });\n return token;\n })\n .catch((err) => {\n Env.logger.error(\"Failed to get Firebase token\", {\n vertesia: {\n user_email: user.email,\n user_name: user.displayName,\n user_id: user.uid,\n refresh: refresh,\n error: err,\n },\n });\n console.error(\"Failed to get access token\", err);\n return null;\n });\n }\n else {\n Env.logger.warn(\"No user found\");\n return Promise.resolve(null);\n }\n}\n//# sourceMappingURL=firebase.js.map","import { jwtDecode } from \"jwt-decode\";\nimport { Env } from '@vertesia/ui/env';\nimport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY } from '../constants';\nimport { getFirebaseAuth, getFirebaseAuthToken } from './firebase';\nlet AUTH_TOKEN_RAW;\nlet AUTH_TOKEN;\nexport async function fetchComposableToken(getIdToken, accountId, projectId, ttl, retryCount = 0) {\n console.log(`Getting/refreshing composable token for account ${accountId} and project ${projectId} `);\n Env.logger.info('Getting/refreshing composable token', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n retry_count: retryCount,\n },\n });\n const idToken = await getIdToken(); //get from firebase\n if (!idToken) {\n console.log('No id token found - using cookie auth');\n throw new Error('No id token found');\n }\n // Use STS endpoint - either configured or default to sts.vertesia.io\n const stsEndpoint = Env.endpoints.sts;\n console.log('Using STS for token generation:', stsEndpoint);\n Env.logger.info('Using STS for token generation', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n sts_url: stsEndpoint,\n },\n });\n try {\n // Call STS to generate a user token\n const stsUrl = new URL(stsEndpoint + '/token/issue');\n const requestBody = {\n type: 'user',\n account_id: accountId,\n project_id: projectId,\n expires_at: ttl ? Math.floor(Date.now() / 1000) + ttl : undefined,\n };\n const stsRes = await fetch(stsUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${idToken}` // Firebase token for authentication\n },\n body: JSON.stringify(requestBody)\n });\n if (idToken && stsRes?.status === 404) {\n // User not found in token-server - call ensure-user endpoint\n console.log('404: User not found - calling ensure-user endpoint');\n Env.logger.info('404: User not found - calling ensure-user endpoint', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes?.status\n },\n });\n const ensureResponse = await fetch(Env.endpoints.studio + '/auth/ensure-user', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${idToken}`,\n 'Content-Type': 'application/json'\n }\n });\n if (ensureResponse.status === 412) {\n // No invite - trigger signup\n console.log('412: No invite found - signup required');\n Env.logger.info('412: No invite found - signup required', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n }\n });\n const idTokenDecoded = jwtDecode(idToken);\n if (!idTokenDecoded?.email) {\n Env.logger.error('No email found in id token');\n throw new Error('No email found in id token');\n }\n throw new UserNotFoundError('User not found - signup required', idTokenDecoded.email);\n }\n if (!ensureResponse.ok) {\n console.error('Failed to ensure user exists', ensureResponse.status);\n Env.logger.error('Failed to ensure user exists', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: ensureResponse.status,\n },\n });\n throw new Error('Failed to ensure user exists');\n }\n // User created/exists - retry token generation\n console.log('User ensured - retrying token generation');\n Env.logger.info('User ensured - retrying token generation', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n }\n });\n return fetchComposableToken(getIdToken, accountId, projectId, ttl, retryCount);\n }\n if (idToken && stsRes?.status === 412) {\n console.log(\"412: auth succeeded but user doesn't exist - signup required\", stsRes?.status);\n Env.logger.error(\"412: auth succeeded but user doesn't exist - signup required\", {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes?.status\n },\n });\n const idTokenDecoded = jwtDecode(idToken);\n if (!idTokenDecoded?.email) {\n Env.logger.error('No email found in id token');\n throw new Error('No email found in id token');\n }\n Env.logger.error('User not found', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n email: idTokenDecoded.email\n }\n });\n throw new UserNotFoundError('User not found', idTokenDecoded.email);\n }\n if (stsRes.status === 403) {\n // User doesn't have access to the requested account/project, or has no accounts\n // This can happen with:\n // 1. Stale localStorage from previous user\n // 2. User invited to a new account (doesn't have access yet)\n // 3. User exists but has no accounts at all\n if (retryCount > 0) {\n // Already retried without account scope - this is a real authorization failure\n console.error('403: Access denied even without account scope - user may have no accounts');\n Env.logger.error('403: Access denied after retry - authorization failure', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes.status,\n retry_count: retryCount\n },\n });\n throw new Error('Access denied - user may not have access to any accounts');\n }\n console.log('403: Access denied - clearing cached account and retrying without account scope');\n Env.logger.warn('403: Access denied - clearing cached account and retrying', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n status: stsRes.status,\n retry_count: retryCount\n },\n });\n // Clear any stale account/project from localStorage\n localStorage.removeItem(LastSelectedAccountId_KEY);\n if (accountId) {\n localStorage.removeItem(LastSelectedProjectId_KEY + '-' + accountId);\n }\n // Retry without account/project scope - let user log in to their default account\n return fetchComposableToken(getIdToken, undefined, undefined, ttl, retryCount + 1);\n }\n if (!stsRes.ok) {\n const errorText = await stsRes.text();\n console.error('STS token generation failed:', stsRes.status, errorText);\n Env.logger.error('STS token generation failed', {\n vertesia: {\n status: stsRes.status,\n error: errorText,\n account_id: accountId,\n project_id: projectId,\n },\n });\n throw new Error(`Failed to get token from STS: ${stsRes.status}`);\n }\n const { token } = await stsRes.json();\n console.log('Successfully got token from STS');\n Env.logger.info('Successfully got token from STS');\n return token;\n }\n catch (error) {\n if (error instanceof UserNotFoundError) {\n throw error; // Re-throw UserNotFoundError\n }\n // Clear any stale account/project from localStorage on error\n localStorage.removeItem(LastSelectedAccountId_KEY);\n if (accountId) {\n localStorage.removeItem(LastSelectedProjectId_KEY + '-' + accountId);\n }\n console.error('Failed to get composable token from STS', error);\n Env.logger.error('Failed to get composable token from STS', {\n vertesia: {\n account_id: accountId,\n project_id: projectId,\n error: error,\n },\n });\n throw new Error('Failed to get composable token');\n }\n}\n/**\n *\n * @param accountId\n * @param projectId\n * @param ttl time to live for the token in seconds\n * @returns\n */\nexport async function fetchComposableTokenFromFirebaseToken(accountId, projectId, ttl) {\n return fetchComposableToken(getFirebaseAuthToken, accountId, projectId, ttl);\n}\nexport async function getComposableToken(accountId, projectId, initToken, forceRefresh = false, useInternalAuth = false) {\n const selectedAccount = accountId ?? localStorage.getItem(LastSelectedAccountId_KEY) ?? undefined;\n const selectedProject = projectId ?? localStorage.getItem(LastSelectedProjectId_KEY + '-' + selectedAccount) ?? undefined;\n //token is still valid for more than 5 minutes\n if (!forceRefresh && AUTH_TOKEN_RAW && AUTH_TOKEN && AUTH_TOKEN.exp > (Date.now() / 1000 + 300)) {\n return { rawToken: AUTH_TOKEN_RAW, token: AUTH_TOKEN, error: false };\n }\n //token is close to expire, refresh it\n if (!useInternalAuth && getFirebaseAuth().currentUser) {\n //we have a firebase user, get the token from there\n AUTH_TOKEN_RAW = await fetchComposableTokenFromFirebaseToken(selectedAccount, selectedProject);\n }\n else if (initToken || AUTH_TOKEN_RAW) {\n // we have a token already and no firebase user, refresh it\n AUTH_TOKEN_RAW = await fetchComposableToken(() => Promise.resolve(initToken ?? AUTH_TOKEN_RAW), selectedAccount, selectedProject);\n }\n if (!AUTH_TOKEN_RAW) {\n Env.logger.error('Cannot acquire a composable token', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n throw new Error('Cannot acquire a composable token');\n }\n AUTH_TOKEN = jwtDecode(AUTH_TOKEN_RAW);\n if (!AUTH_TOKEN || !AUTH_TOKEN.exp || !AUTH_TOKEN_RAW) {\n console.error('Invalid composable token', AUTH_TOKEN);\n Env.logger.error('Invalid composable token', {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n throw new Error('Invalid composable token');\n }\n return { rawToken: AUTH_TOKEN_RAW, token: AUTH_TOKEN, error: false };\n}\nexport class UserNotFoundError extends Error {\n email;\n constructor(message, email) {\n super(message);\n this.name = 'UserNotFoundError';\n this.email = email;\n }\n}\n//# sourceMappingURL=composable.js.map","import { useState, useEffect } from 'react';\nimport { useUserSession } from \"@vertesia/ui/session\";\nexport function useCurrentTenant() {\n const { user } = useUserSession();\n const [currentTenant, setCurrentTenant] = useState(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState(null);\n useEffect(() => {\n const loadCurrentTenant = async () => {\n if (!user?.email) {\n setCurrentTenant(null);\n setIsLoading(false);\n return;\n }\n try {\n const response = await fetch('/api/resolve-tenant', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n tenantEmail: user.email\n })\n });\n if (response.ok) {\n const tenantData = await response.json();\n if (tenantData) {\n // Convert the resolved tenant data to our TenantConfig format\n setCurrentTenant({\n tenantKey: tenantData.name || 'unknown',\n name: tenantData.label || tenantData.name || 'Unknown',\n domain: tenantData.domain || [],\n firebaseTenantId: tenantData.firebaseTenantId,\n provider: tenantData.provider,\n logo: tenantData.logo\n });\n }\n else {\n setCurrentTenant(null);\n }\n }\n else {\n setCurrentTenant(null);\n }\n }\n catch (error) {\n console.error('Error loading current tenant:', error);\n setError('Failed to load tenant configuration');\n setCurrentTenant(null);\n }\n finally {\n setIsLoading(false);\n }\n };\n loadCurrentTenant();\n }, [user?.email]);\n return {\n currentTenant,\n isLoading,\n error\n };\n}\n//# sourceMappingURL=useCurrentTenant.js.map","/**\n * This hook is used to generate and verify state for OAuth2 authorization requests.\n * @returns\n */\nimport { useCallback } from \"react\";\nconst AUTH_STATE_KEY = 'auth_state';\nconst STATE_EXPIRY_KEY = 'auth_state_expiry';\nconst STATE_TTL = 5 * 60 * 1000; // 5 min\nexport function useAuthState() {\n // Generate new state\n const generateState = useCallback(() => {\n const state = crypto.randomUUID();\n const expiryTime = Date.now() + STATE_TTL;\n // Store state and expiry\n sessionStorage.setItem(AUTH_STATE_KEY, state);\n sessionStorage.setItem(STATE_EXPIRY_KEY, expiryTime.toString());\n return state;\n }, []);\n // Verify returned state\n const verifyState = useCallback((returnedState) => {\n if (!returnedState) {\n return 'Missing state';\n }\n const savedState = sessionStorage.getItem(AUTH_STATE_KEY);\n const expiryTime = parseInt(sessionStorage.getItem(STATE_EXPIRY_KEY) || '0');\n let reason;\n // Verify state matches and hasn't expired\n if (savedState !== returnedState) {\n reason = `State mismatched (${savedState} !== ${returnedState})`;\n }\n else if (Date.now() > expiryTime) {\n reason = 'State expired';\n }\n else {\n reason = undefined; // No errors\n }\n return reason;\n }, []);\n // Clear state (useful for cleanup)\n const clearState = useCallback(() => {\n sessionStorage.removeItem(AUTH_STATE_KEY);\n sessionStorage.removeItem(STATE_EXPIRY_KEY);\n }, []);\n return { generateState, verifyState, clearState };\n}\n//# sourceMappingURL=useAuthState.js.map","import { jwtDecode } from 'jwt-decode';\nimport { createContext, useContext } from 'react';\nimport { VertesiaClient } from '@vertesia/client';\nimport { Env } from '@vertesia/ui/env';\nimport { getComposableToken } from './auth/composable';\nimport { getFirebaseAuth } from './auth/firebase';\nimport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY } from './constants';\nexport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY };\nconst CENTRAL_AUTH_REDIRECT = \"https://internal-auth.vertesia.app/\";\nclass UserSession {\n isLoading = true;\n client;\n authError;\n authToken;\n setSession;\n lastSelectedAccount;\n lastSelectedProject;\n onboardingComplete;\n constructor(client, setSession) {\n if (client) {\n this.client = client;\n }\n else {\n this.client = new VertesiaClient({\n serverUrl: Env.endpoints.studio,\n storeUrl: Env.endpoints.zeno,\n tokenServerUrl: Env.endpoints.sts\n });\n }\n if (setSession) {\n this.setSession = setSession;\n }\n this.logout = this.logout.bind(this);\n }\n get store() {\n return this.client.store;\n }\n get user() {\n return this.authToken;\n }\n get account() {\n return this.authToken?.account;\n }\n get project() {\n return this.authToken?.project;\n }\n get accounts() {\n return this.authToken?.accounts;\n }\n get authCallback() {\n return this.rawAuthToken.then(token => `Bearer ${token}`);\n }\n get rawAuthToken() {\n return getComposableToken().then(res => {\n const token = res?.rawToken;\n if (!token) {\n throw new Error('No token available');\n }\n this.authToken = jwtDecode(token);\n return token;\n });\n }\n signOut() {\n this.logout();\n }\n getAccount() {\n return this.authToken?.account;\n }\n async login(token) {\n this.authError = undefined;\n this.isLoading = false;\n this.client.withAuthCallback(() => this.authCallback);\n this.authToken = jwtDecode(token);\n console.log(`Logging in as ${this.authToken?.name} with account ${this.authToken?.account.name} (${this.authToken?.account.id}, and project ${this.authToken?.project?.name} (${this.authToken?.project?.id})`);\n //store selected account in local storage\n localStorage.setItem(LastSelectedAccountId_KEY, this.authToken.account.id);\n localStorage.setItem(LastSelectedProjectId_KEY + '-' + this.authToken.account.id, this.authToken.project?.id ?? '');\n // notify the host app of the login\n Env.onLogin?.(this.authToken);\n await this.fetchOnboardingStatus();\n return Promise.resolve();\n }\n isLoggedIn() {\n return !!this.authToken;\n }\n logout() {\n console.log('Logging out');\n // Check if we should use central auth for logout\n const devDomains = [\".composable.sh\", \".vertesia.dev\", \"vertesia.app\"];\n const shouldUseCentralAuth = Env.isDocker || devDomains.some((domain) => window.location.hostname.endsWith(domain));\n if (shouldUseCentralAuth) {\n // Redirect to central auth for logout\n // Central auth will handle Firebase logout\n console.log('Using central auth logout');\n this.authError = undefined;\n this.isLoading = false;\n this.authToken = undefined;\n this.setSession = undefined;\n this.client.withAuthCallback(undefined);\n const logoutUrl = new URL(CENTRAL_AUTH_REDIRECT);\n const currentUrl = new URL(window.location.href);\n currentUrl.hash = \"\";\n logoutUrl.pathname = \"/logout\";\n logoutUrl.searchParams.set(\"redirect_uri\", currentUrl.toString());\n location.replace(logoutUrl.toString());\n }\n else {\n // Use Firebase logout directly\n console.log('Using Firebase logout');\n if (this.authToken) {\n getFirebaseAuth().signOut();\n }\n this.authError = undefined;\n this.isLoading = false;\n this.authToken = undefined;\n this.setSession = undefined;\n this.client.withAuthCallback(undefined);\n }\n }\n async switchAccount(targetAccountId) {\n localStorage.setItem(LastSelectedAccountId_KEY, targetAccountId);\n if (this) {\n if (this.account && this.project) {\n localStorage.setItem(LastSelectedProjectId_KEY + '-' + this.account.id, this.project.id);\n }\n else if (this.account) {\n localStorage.removeItem(LastSelectedProjectId_KEY + '-' + this.account.id);\n }\n }\n window.location.replace('/?a=' + targetAccountId);\n }\n async switchProject(targetProjectId) {\n if (this.account) {\n localStorage.setItem(LastSelectedProjectId_KEY + '-' + this.account.id, targetProjectId);\n }\n window.location.replace('/?a=' + this.account?.id + '&p=' + targetProjectId);\n }\n async fetchAccounts() {\n return this.client.accounts.list().then(accounts => {\n if (!this.authToken) {\n throw new Error('No token available');\n }\n this.authToken.accounts = accounts;\n this.setSession?.(this.clone());\n }).catch(err => {\n console.error('Failed to fetch accounts', err);\n throw err;\n });\n }\n async fetchOnboardingStatus() {\n if (this.onboardingComplete) {\n console.log('Onboarding already completed');\n return false;\n }\n const previousStatus = this.onboardingComplete;\n try {\n const onboarding = await this.client.account.onboardingProgress();\n this.onboardingComplete = Object.values(onboarding).every(value => value === true);\n if (previousStatus !== this.onboardingComplete) {\n return true;\n }\n this.setSession?.(this.clone());\n }\n catch (error) {\n console.error('Error fetching onboarding status:', error);\n this.onboardingComplete = false;\n this.setSession?.(this.clone());\n }\n return false;\n }\n clone() {\n const session = new UserSession(this.client);\n session.isLoading = this.isLoading;\n session.authError = this.authError;\n session.authToken = this.authToken;\n session.setSession = this.setSession;\n session.lastSelectedAccount = this.lastSelectedAccount;\n session.switchAccount = this.switchAccount;\n session.onboardingComplete = this.onboardingComplete;\n return session;\n }\n}\nconst UserSessionContext = createContext(undefined);\nexport function useUserSession() {\n const session = useContext(UserSessionContext);\n if (!session) {\n throw new Error('useUserSession must be used within a UserSessionProvider');\n }\n return session;\n}\nexport { UserSession, UserSessionContext };\n//# sourceMappingURL=UserSession.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { Env } from \"@vertesia/ui/env\";\nimport { onAuthStateChanged } from \"firebase/auth\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { UserNotFoundError, getComposableToken } from \"./auth/composable\";\nimport { getFirebaseAuth } from \"./auth/firebase\";\nimport { useAuthState } from \"./auth/useAuthState\";\nimport { LastSelectedAccountId_KEY, LastSelectedProjectId_KEY, UserSession, UserSessionContext } from \"./UserSession\";\nconst devDomains = [\".composable.sh\", \".vertesia.dev\", \"vertesia.app\"];\nconst CENTRAL_AUTH_REDIRECT = \"https://internal-auth.vertesia.app/\";\nexport function shouldRedirectToCentralAuth() {\n // Authentication is not supported in Docker environment.\n // See https://github.com/vertesia/studio/wiki/Composable-UI-Hosting-Options\n if (Env.isDocker) {\n return true;\n }\n return devDomains.some((domain) => window.location.hostname.endsWith(domain));\n}\nexport function UserSessionProvider({ children }) {\n const hashParams = new URLSearchParams(location.hash.substring(1));\n const token = hashParams.get(\"token\");\n const state = hashParams.get(\"state\");\n const [session, setSession] = useState(new UserSession());\n const { generateState, verifyState, clearState } = useAuthState();\n const hasInitiatedAuthRef = useRef(false);\n const redirectToCentralAuth = (projectId, accountId) => {\n const url = new URL(`${CENTRAL_AUTH_REDIRECT}?sts=${Env.endpoints.sts ?? \"https://sts.vertesia.io\"}`);\n const currentUrl = new URL(window.location.href);\n currentUrl.hash = \"\";\n if (projectId)\n currentUrl.searchParams.set(\"p\", projectId);\n if (accountId)\n currentUrl.searchParams.set(\"a\", accountId);\n url.searchParams.set(\"redirect_uri\", currentUrl.toString());\n url.searchParams.set(\"state\", generateState());\n location.replace(url.toString());\n };\n useEffect(() => {\n // Make this effect idempotent - only run auth flow once\n if (hasInitiatedAuthRef.current) {\n console.log(\"Auth: skipping duplicate auth flow initiation\");\n return;\n }\n hasInitiatedAuthRef.current = true;\n console.log(\"Auth: starting auth flow\");\n Env.logger.info(\"Starting auth flow\");\n const currentUrl = new URL(window.location.href);\n const selectedAccount = currentUrl.searchParams.get(\"a\") ?? localStorage.getItem(LastSelectedAccountId_KEY) ?? undefined;\n const selectedProject = currentUrl.searchParams.get(\"p\") ??\n localStorage.getItem(LastSelectedProjectId_KEY + \"-\" + selectedAccount) ??\n undefined;\n console.log(\"Auth: selected account\", selectedAccount);\n console.log(\"Auth: selected project\", selectedProject);\n Env.logger.info(\"Selected account and project\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n if (token && state) {\n const validationError = verifyState(state);\n if (validationError) {\n console.error(`Auth: invalid state: ${validationError}`);\n Env.logger.error(`Invalid state: ${validationError}`, {\n vertesia: {\n state: state,\n },\n });\n redirectToCentralAuth();\n }\n else {\n clearState();\n }\n getComposableToken(selectedAccount, selectedProject, token, false, shouldRedirectToCentralAuth())\n .then((res) => {\n session.login(res.rawToken).then(() => {\n setSession(session.clone());\n //cleanup the hash\n window.location.hash = \"\";\n });\n })\n .catch((err) => {\n // Don't redirect to central auth for UserNotFoundError - let signup flow handle it\n if (err instanceof UserNotFoundError) {\n console.log(\"User not found - will trigger signup flow\", err);\n session.isLoading = false;\n session.authError = err;\n setSession(session.clone());\n return;\n }\n console.error(\"Failed to fetch user token from studio, redirecting to central auth\", err);\n Env.logger.error(\"Failed to fetch user token from studio, redirecting to central auth\", {\n vertesia: {\n error: err,\n },\n });\n redirectToCentralAuth();\n });\n return;\n }\n else {\n //if on a dev domain and not logged in, redirect to central auth\n if (!session.isLoggedIn()) {\n console.log(\"Auth: not logged in & no token/state\");\n Env.logger.info(\"Not logged in & no token/state\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n if (shouldRedirectToCentralAuth()) {\n console.log(\"Auth: on dev domain, redirecting to central auth with selection\", selectedAccount, selectedProject);\n Env.logger.info(\"Redirecting to central auth with selection\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n redirectToCentralAuth();\n return; // Don't register onAuthStateChanged listener when redirecting\n }\n else {\n console.log(\"Auth: not on dev domain\");\n Env.logger.info(\"Not on dev domain\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n }\n }\n }\n return onAuthStateChanged(getFirebaseAuth(), async (firebaseUser) => {\n if (firebaseUser) {\n console.log(\"Auth: successful login with firebase\");\n Env.logger.info(\"Successful login with firebase\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n session.setSession = setSession;\n await getComposableToken(selectedAccount, selectedProject, undefined, false, shouldRedirectToCentralAuth())\n .then((res) => {\n session.login(res.rawToken).then(() => setSession(session.clone()));\n })\n .catch((err) => {\n console.error(\"Failed to fetch user token from studio\", err);\n Env.logger.error(\"Failed to fetch user token from studio\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n error: err,\n },\n });\n if (!(err instanceof UserNotFoundError))\n session.logout();\n session.isLoading = false;\n session.authError = err;\n setSession(session.clone());\n });\n }\n else {\n // anonymous user\n console.log(\"Auth: using anonymous user\");\n Env.logger.info(\"Using anonymous user\", {\n vertesia: {\n account_id: selectedAccount,\n project_id: selectedProject,\n },\n });\n session.client.withAuthCallback(undefined);\n session.logout();\n setSession(session.clone());\n }\n });\n }, []);\n return _jsx(UserSessionContext.Provider, { value: session, children: children });\n}\n//# sourceMappingURL=UserSessionProvider.js.map","import { Env } from '@vertesia/ui/env';\nimport { logEvent } from \"firebase/analytics\";\nimport { getFirebaseAnalytics } from \"./auth/firebase\";\nexport function useUXTracking() {\n //identify user in monitoring and UX systems\n const tagUserSession = async (user) => {\n const signupData = window.localStorage.getItem(\"composableSignupData\");\n if (!user) {\n console.error('No user found -- skipping tagging');\n return;\n }\n if (signupData) {\n window.localStorage.removeItem(\"composableSignupData\");\n }\n };\n //send event to analytics and UX systems\n const trackEvent = (eventName, eventProperties) => {\n if (!Env.isProd) {\n console.debug('track event', eventName, eventProperties);\n }\n //GA via firebase\n logEvent(getFirebaseAnalytics(), eventName, { ...eventProperties, debug_mode: !Env.isProd });\n };\n return {\n tagUserSession,\n trackEvent\n };\n}\n//# sourceMappingURL=useUXTracking.js.map"],"names":["LastSelectedAccountId_KEY","LastSelectedProjectId_KEY","AUTH_TOKEN_RAW","AUTH_TOKEN","_firebaseApp","_analytics","_firebaseAuth","getFirebaseApp","Env","firebase","Error","initializeApp","error","console","getFirebaseAnalytics","getAnalytics","getFirebaseAuth","getAuth","async","setFirebaseTenant","tenantEmail","log","retries","retryDelay","response","fetch","method","headers","body","JSON","stringify","signal","AbortSignal","timeout","ok","errorData","json","parseError","status","warn","data","firebaseTenantId","auth","tenantId","providerType","provider","fetchError","Promise","resolve","setTimeout","message","getFirebaseAuthToken","refresh","user","currentUser","getIdToken","then","token","logger","info","vertesia","user_email","email","user_name","displayName","user_id","uid","catch","err","fetchComposableToken","accountId","projectId","ttl","retryCount","account_id","project_id","retry_count","idToken","stsEndpoint","endpoints","sts","sts_url","stsUrl","URL","requestBody","type","expires_at","Math","floor","Date","now","undefined","stsRes","Authorization","ensureResponse","studio","idTokenDecoded","jwtDecode","UserNotFoundError","localStorage","removeItem","errorText","text","fetchComposableTokenFromFirebaseToken","getComposableToken","initToken","forceRefresh","useInternalAuth","selectedAccount","getItem","selectedProject","exp","rawToken","constructor","super","this","name","useCurrentTenant","useUserSession","currentTenant","setCurrentTenant","useState","isLoading","setIsLoading","setError","useEffect","tenantData","tenantKey","label","domain","logo","loadCurrentTenant","AUTH_STATE_KEY","STATE_EXPIRY_KEY","useAuthState","generateState","useCallback","state","crypto","randomUUID","expiryTime","sessionStorage","setItem","toString","verifyState","returnedState","savedState","parseInt","reason","clearState","UserSession","client","authError","authToken","setSession","lastSelectedAccount","lastSelectedProject","onboardingComplete","VertesiaClient","serverUrl","storeUrl","zeno","tokenServerUrl","logout","bind","store","account","project","accounts","authCallback","rawAuthToken","res","signOut","getAccount","login","withAuthCallback","id","onLogin","fetchOnboardingStatus","isLoggedIn","isDocker","some","window","location","hostname","endsWith","logoutUrl","currentUrl","href","hash","pathname","searchParams","set","replace","switchAccount","targetAccountId","switchProject","targetProjectId","fetchAccounts","list","clone","previousStatus","onboarding","onboardingProgress","Object","values","every","value","session","UserSessionContext","createContext","useContext","devDomains","shouldRedirectToCentralAuth","UserSessionProvider","children","hashParams","URLSearchParams","substring","get","hasInitiatedAuthRef","useRef","redirectToCentralAuth","url","current","validationError","onAuthStateChanged","firebaseUser","_jsx","Provider","useUXTracking","tagUserSession","signupData","trackEvent","eventName","eventProperties","isProd","debug","logEvent","debug_mode"],"mappings":"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"}
|
package/lib/vertesia-ui-shell.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsxs as e,jsx as t,Fragment as n}from"react/jsx-runtime";import{Button as o,Modal as i,ModalTitle as a,ModalBody as r,useToast as l,Input as c,Spinner as s,SelectStack as d,SelectBox as u,useSafeLayoutEffect as m,ErrorBox as h,Center as p,useFetch as f,ModalFooter as g,Tabs as v,TabsBar as x,TabsPanel as y,VTooltip as b,Avatar as N,ModeToggle as w,MenuList as k,ToastProvider as j,ThemeProvider as C}from"@vertesia/ui/core";import{useState as S,useEffect as P,Fragment as z,createContext as I,useContext as L,useMemo as T}from"react";import{useUserSession as A,useUXTracking as O,setFirebaseTenant as _,getFirebaseAuth as U,UserNotFoundError as E,fetchComposableTokenFromFirebaseToken as D,UserSessionProvider as q,LastSelectedAccountId_KEY as R,LastSelectedProjectId_KEY as W}from"@vertesia/ui/session";import{Env as M}from"@vertesia/ui/env";import F from"clsx";import{signInWithRedirect as V,OAuthProvider as B,GoogleAuthProvider as Y,GithubAuthProvider as G}from"firebase/auth";import{useLocation as J,useNavigate as H}from"@vertesia/ui/router";import{getTenantIdFromProject as K,Permission as $}from"@vertesia/common";import{Popover as Q}from"@vertesia/ui/widgets";import{Check as X,CopyIcon as Z,LockIcon as ee}from"lucide-react";import{useUserPermissions as te,UserPermissionProvider as ne}from"@vertesia/ui/features";import{Transition as oe}from"@headlessui/react";function ie(){const n=A(),{client:l,account:c}=n,[s,d]=S(!1),[u,m]=S([]);P(()=>{l.account.listInvites().then(e=>{if(e.length>0){const t=e.filter(e=>e.data.account);if(0===t.length)return void console.log("No valid invites found, closing modal");console.log("Found valid invites",t.length),d(!0),m(t)}else console.log("No invites found, closing modal"),d(!1)}).catch(e=>{console.error("Error fetching invites",e)})},[c?.id]);const h=()=>d(!1),p=u.map(i=>i.data.account?e("div",{className:"flex flex-row w-full justify-between border rounded-sm px-2 py-2 ",children:[e("div",{className:"flex flex-col",children:[t("div",{className:"w-full font-semibold",children:i.data.account.name??i.data.account}),i.data.project&&e("div",{className:"w-full text-base",children:["- ",i.data.project.name]}),e("div",{className:"text-xs",children:["Role: ",i.data.role]}),i.data.invited_by&&e("div",{className:"text-xs",children:["by ",i.data.invited_by.name??i.data.invited_by]})]}),e("div",{className:"flex flex-col gap-4",children:[t(o,{size:"xs",onClick:()=>(async e=>{await l.account.acceptInvite(e.id),await n.fetchAccounts();const t=u.filter(t=>t.id!==e.id).filter(e=>e.data.account);t.length>0?m(t):h()})(i),children:"Accept"})," ",t(o,{size:"xs",variant:"secondary",onClick:()=>(async e=>{await l.account.rejectInvite(e.id);const t=u.filter(t=>t.id!==e.id);m(t),0===t.length&&h()})(i),children:"Reject"})]})]},i.id):(console.warn("Invite has no account data",i),null));return t("div",{children:e(i,{isOpen:s,onClose:h,children:[t(a,{children:"Review Invites"}),e(r,{children:[t("div",{className:"text-sm pb-4",children:"You have received the following invites to join other accounts. Please review and accept or declined them."}),p]})]})})}function ae({redirectTo:i}){const[a,r]=S(!1),{trackEvent:d}=O(),[u,m]=S(""),h=l();return e(n,{children:[t(c,{value:u,onChange:m,placeholder:"Enter your enterprise email",type:"email"}),a?t("div",{className:"w-full flex justify-center",children:t(s,{})}):t(o,{variant:"outline",onClick:async()=>{if(!u)return;/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(u)?(r(!0),_(u).then(e=>{if(!e)return h({title:"Tenant not found",status:"error",duration:5e3}),void r(!1);localStorage.setItem("tenantName",e.name??"");const t=function(e){if(!M.firebase)throw new Error("Firebase configuration is not available in the environment");switch(M.firebase.providerType){case"oidc":default:return new B("oidc.main");case"google":{let t=e||window.location.pathname||"/";"/"!==t[0]&&(t="/"+t);const n=new Y;return n.addScope("profile"),n.addScope("email"),n.setCustomParameters({prompt:"select_account",redirect_uri:window.location.origin+t}),n}case"microsoft":return new B("microsoft.com");case"github":return new B("github.com")}}(i);d("enterprise_signin",{firebaseTenantName:e.name}),M.logger.info("Enterprise single sign-in",{vertesia:{email:u,firebaseTenantName:e.name,firebaseTenantId:e.firebaseTenantId}}),V(U(),t),r(!1)})):h({title:"Invalid email address",status:"error",duration:5e3})},className:"w-full mt-2 py-4 flex rounded-lg hover:shadow-sm transition duration-150 text-center",children:t("span",{className:"text-sm font-semibold",children:"Continue with Enterprise SSO"})})]})}function re({}){return e(o,{variant:"outline",onClick:()=>{localStorage.removeItem("tenantName");let e="https://dengenlabs.firebaseapp.com/__/auth/handler"+window.location.pathname;"/"!==e[0]&&(e="/"+e);const t=new G;t.addScope("profile"),t.addScope("email"),V(U(),t)},className:"w-full py-5 flex rounded-lg hover:shadow-sm transition duration-150 text-center mb-2",children:[t("img",{className:"size-6 bg-white rounded-full",src:"https://www.svgrepo.com/show/503359/github.svg",loading:"lazy",alt:"github logo"}),t("span",{className:"text-sm font-semibold",children:"Continue with GitHub"})]})}function le({redirectTo:n}){return e(o,{variant:"outline",onClick:()=>{localStorage.removeItem("tenantName");let e=n||window.location.pathname||"/";"/"!==e[0]&&(e="/"+e);const t=new Y;t.addScope("profile"),t.addScope("email"),t.setCustomParameters({prompt:"select_account",redirect_uri:window.location.origin+e}),V(U(),t)},className:"w-full py-5 flex rounded-lg hover:shadow-sm transition duration-150 text-center mb-2",children:[t("img",{className:"size-6",src:"https://www.svgrepo.com/show/475656/google-color.svg",loading:"lazy",alt:"google logo"}),t("span",{className:"text-sm font-semibold",children:"Continue with Google"})]})}function ce({redirectTo:n}){return e(o,{variant:"outline",onClick:()=>{localStorage.removeItem("tenantName");let e=n||window.location.pathname||"/";"/"!==e[0]&&(e="/"+e);const t=new B("microsoft.com");t.addScope("profile"),t.addScope("email"),V(U(),t)},className:"w-full py-5 flex rounded-lg hover:shadow-sm transition duration-150 text-center",children:[t("img",{className:"size-6",src:"https://learn.microsoft.com/en-us/entra/identity-platform/media/howto-add-branding-in-apps/ms-symbollockup_mssymbol_19.svg",loading:"lazy",alt:"microsoft logo"}),t("span",{className:"text-sm font-semibold",children:"Continue with Microsoft"})]})}const se=[{id:1,label:"1-10 employees"},{id:11,label:"11-100 employees"},{id:101,label:"101-1000 employees"},{id:1001,label:"1001-5000 employees"},{id:5001,label:"5000+ employees"}],de=[{id:"personal",label:"Personal",description:"For personal use, or for a small team."},{id:"company",label:"Company",description:"For a company or organization."}],ue=[{id:"testing",label:"Just Testing or Evaluating LLMs"},{id:"exploring",label:"Actively Exploring LLMs on a Project"},{id:"using",label:"Already Using LLMs in Production"},{id:"migrating",label:"Migrating to different LLMs"},{id:"other",label:"Other"}];function me({onSignup:i,goBack:a}){const[r,l]=S(void 0),[s,m]=S(void 0),[h,p]=S(void 0),[f,g]=S(void 0),[v,x]=S(void 0),[y,b]=S(void 0),[N,w]=S(void 0),k="company"===r;P(()=>{const e=U().currentUser;e?b(e):console.error("No user found")},[y]);return e("div",{className:"flex flex-col space-y-2",children:[e("div",{className:"prose",children:[e("p",{className:"prose text-sm text-muted pt-4",children:["Welcome to Vertesia, ",y?.displayName," (",y?.email,"). Please tell us a little bit about yourself and you'll be on your way. No credit card is required."]}),N&&t("div",{className:"text-destructive",children:N})]}),t(he,{label:"Account Type",children:t(d,{options:de,selected:de.find(e=>e.id===r),onSelect:e=>l(e.id)})}),k&&e(n,{children:[t(he,{label:"Company Size",children:t(u,{className:"w-full border border-accent bg-muted",value:s,options:se,onChange:m,optionLabel:e=>e?.label,placeholder:"Select Company Size"})}),t(he,{label:"Company Name",children:t(c,{value:h,onChange:p,type:"text",required:!0})}),t(he,{label:"Company Website",children:t(c,{value:f,onChange:g,type:"text"})})]}),t(he,{label:"Project Maturity",children:t(u,{className:"w-full border border-accent bg-muted",options:ue,value:ue.find(e=>e.id===v),optionLabel:e=>e?.label,placeholder:"Select Project Maturity",onChange:e=>x(e?.id)})}),e("div",{className:"pt-8 flex flex-col",children:[t(o,{variant:"primary",onClick:async()=>{if(!(r?k&&!h?(w("Please enter an organization name"),0):!k||s||(w("Please select a company size"),0):(w("Please select an account type"),0)))return;if(!r)return;const e={accountType:r,companyName:h,companySize:s?.id,companyWebsite:f,maturity:v};window.localStorage.setItem("composableSignupData",JSON.stringify(e));const t=await(U().currentUser?.getIdToken());console.log("Got firebase token",U(),t),t?i(e,t):console.error("No firebase token found")},size:"xl",children:t("span",{className:"text-lg",children:"Sign Up"})}),t(o,{variant:"ghost",size:"xl",className:"mt-4",onClick:a,children:t("span",{className:"",children:"Wrong account, go back"})})]})]})}function he({label:n,children:o}){return e("div",{className:"flex flex-col space-y-2 pt-4",children:[t("div",{className:"text-sm text-muted",children:n}),o]})}function pe({allowedPrefix:e,isNested:n=!1,lightLogo:o,darkLogo:i}){const[a,r]=S(!1);return m(()=>{e&&r(window.location.href.startsWith(e))},[]),a?null:t(fe,{isNested:n,lightLogo:o,darkLogo:i})}function fe({isNested:n=!1,lightLogo:o,darkLogo:i}){const{isLoading:a,user:r,authError:l}=A();return a||r?null:t("div",{style:{zIndex:999998},className:(n?"absolute":"fixed")+"overflow-y-auto ",children:e("div",{className:F("flex flex-col items-center justify-center py-14 px-4"),children:[t(ge,{authError:l,lightLogo:o,darkLogo:i}),e("div",{className:"flex gap-x-6 mt-10 justify-center text-muted",children:[t("a",{href:"https://vertesiahq.com/privacy",className:"text-sm",children:"Privacy Policy"}),t("a",{href:"https://vertesiahq.com/terms",className:"text-sm",children:"Terms of Service"})]})]})})}function ge({authError:i,darkLogo:a,lightLogo:r}){const[l,c]=S(void 0),[s,d]=S(!1),{signOut:u}=A(),{trackEvent:m}=O();history.replaceState({},"","/");const h=()=>{c(void 0),d(!0)};P(()=>{i instanceof E&&(console.log("User not found, redirecting to signup"),h())},[i]);return e(n,{children:[r&&t("img",{src:r,alt:"logo",className:"h-15 block dark:hidden"}),a&&t("img",{src:a,alt:"logo",className:"h-15 hidden dark:block"}),l&&e("div",{className:"my-6",children:["Need to make a change?"," ",t(o,{onClick:h,children:" Go back"})]}),t("div",{className:"flex flex-col space-y-2",children:s&&!localStorage.getItem("tenantName")?t(me,{onSignup:(e,t)=>{console.log("Got Signup data",e),c(e);const n={signupData:e,firebaseToken:t};fetch(M.endpoints.studio+"/auth/signup",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)}).then(e=>{console.log("Signup successful",n,e),m("sign_up"),window.location.href="/"})},goBack:()=>{console.log("Going back, signing out"),c(void 0),d(!1),u()}}):e("div",{className:"flex flex-col",children:[t("div",{className:"my-4",children:t("h2",{className:"text-2xl font-bold text-center",children:"Log in or Sign up"})}),e("div",{className:"max-w-2xl text-center my-2 px-2",children:["First time here? No problem, it's free to try!",t("br",{}),"We'll just ask you a couple of questions next and you'll be on your way."]}),e("div",{className:"flex items-center flex-col",children:[e("div",{className:"py-4 w-70",children:[t(le,{}),t(re,{}),t(ce,{})]}),e("div",{className:"flex items-center flex-row w-70 text-muted",children:[t("hr",{className:"w-full"}),t("div",{className:"px-2 text-xs",children:"OR"}),t("hr",{className:"w-full"})]}),t("div",{className:"py-4 w-70",children:t(ae,{})})]}),i&&!(i instanceof E)&&t("div",{className:"text-center",children:e("div",{className:"",children:["Sorry, we have not been able to sign you in.",t("br",{}),"Please try again or contact",t("a",{className:"text-info mx-1",href:"mailto:support@vertesiahq.com",children:"support@vertesiahq.com"}),"if it persists.",e("pre",{className:"mt-2",children:["Error: ",i.message]})]})})]})})]})}const ve=new Set(["127.0.0.1","localhost"]);function xe(e){const t=new URLSearchParams(e.search),n=function(e){if(!e)return null;let t,n;try{t=decodeURIComponent(e)}catch{return null}try{n=new URL(t)}catch{return null}return"http:"!==n.protocol?null:n.port?n.username||n.password?null:ve.has(n.hostname)?n.toString():null:null}(t.get("redirect_uri")),o=t.get("code");if(!n||!o)return null;return{redirect:n,code:o,profile:t.get("profile")??"default",project:t.get("project")??void 0,account:t.get("account")??void 0}}function ye(){const[e,n]=S(),[o,i]=S(),a=xe(J()),r=l(),c=async e=>{if(!a)return;if(!e.profile)return void r({title:"Profile is required",description:"Please enter a profile name to save the client authorization",status:"error",duration:2e3});if(!e.account)return void r({title:"Account is required",description:"Please select an account to authorize the client to access the ComposablePrompts servers",status:"error",duration:2e3});if(!e.project)return void r({title:"Project is required",description:"Please select a project to authorize the client to access the ComposablePrompts servers",status:"error",duration:2e3});let t;try{const o=await D(e.account,e.project,86400);o?(t={...e,studio_server_url:M.endpoints.studio,zeno_server_url:M.endpoints.zeno,token:o},await fetch(a.redirect,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),n(t)):r({title:"Failed to get composable token",status:"error",duration:5e3})}catch(e){t?(i(e),n(t)):r({title:"Error authorizing client",description:e.message,status:"error",duration:5e3})}},s=a?e?t(Ne,{payload:e,error:o}):t(be,{clientInfo:a,onAccept:c}):t(h,{title:"Invalid request",children:"This page should be called by a terminal client to authenticate against the ComposablePrompts servers"});return t("div",{className:"w-full flex flex-col items-center gap-4 mt-24",children:s})}function be({onAccept:o,clientInfo:i}){const{client:a,user:r}=A(),{data:l,error:c}=f(()=>r?a.projects.list():Promise.resolve([]),[r]);if(c)return t(h,{title:"Error loading projects",children:c.message});const d=M.isLocalDev?"Local Dev":M.isDev?"Staging":"Production";return r&&l?e(n,{children:[e("div",{className:"w-1/3",children:[e("div",{className:"mb-4 text-xl font-semibold text-info",children:["Authorizing client on ",d," environment."]}),e("div",{className:"mb-2 text-md text-muted-foreground",children:[t("div",{children:"A client app wants authorization to access the composable prompt servers in your name."}),e("div",{children:["The client app code is ",t("b",{className:"text-foreground",children:i.code}),". You can check if the code is correct in the terminal."]})]}),e("div",{className:"mb-2 text-sm text-muted-foreground",children:[t("div",{children:"You must choose the target account and project for the client to access."}),t("div",{children:"Also, enter a profile name that will be used to save the authorization in your client configuration."})]})]}),t(we,{onAccept:o,allProjects:l,data:i})]}):t(s,{size:"lg"})}function Ne({payload:n,error:i}){const a=l();return e("div",{children:[i?e("div",{children:[e(h,{title:"Failed to send the authorization token to the cli tool",children:['This can happen due to security checks on Safari. The error is "',i.message,'"']}),t("div",{children:"Don't worry, you can still authenticate the cli tool by pasting the authentication token in the terminal. You can close this page."})]}):t("div",{children:"The client is authenticated. You can close this page."}),t(p,{className:"mt-4",children:t(o,{variant:"secondary",onClick:()=>{n&&(navigator.clipboard.writeText(JSON.stringify(n)),a({title:"Authentication Payload copied",description:i?"You can paste the authentication payload in the terminal to authenticate the client.":"You can close the page now.",status:"success",duration:5e3}))},children:"Copy the Authentication Payload"})})]})}function we({allProjects:n,data:i,onAccept:a}){const{accounts:r,account:l,project:s}=A(),[d,u]=S(()=>({profile:i.profile,account:i.account??l?.id,project:i.project??s?.id})),m=n.filter(e=>e.account===d.account);return e("div",{className:"w-1/3",children:[e("div",{className:"mb-4 flex flex-col gap-2",children:[t("span",{className:"font-semibold text-muted-foreground",children:"Profile Name"}),t(c,{type:"text",value:d.profile,onChange:e=>{u({...d,profile:e})}})]}),e("div",{className:"mb-4 flex flex-col gap-2",children:[t("span",{className:"font-semibold text-muted-foreground",children:"Account"}),t(ke,{value:d.account,onChange:e=>{u({...d,account:e.id,project:void 0})},accounts:r||[]})]}),e("div",{className:"mb-4 flex flex-col gap-2",children:[t("span",{className:"font-semibold text-muted-foreground",children:"Project"}),t(je,{value:d.project,onChange:e=>{u({...d,project:e.id})},projects:m})]}),e("div",{className:"mb-4 text-sm text-attention",children:[t("b",{children:"Note:"})," If your browser asks for permission to access your local network, please allow it. This is required to send the authorization token back to the CLI tool. If not, you will need to copy/paste the token manually in your terminal."]}),t("div",{children:t(o,{size:"xl",onClick:()=>a(d),children:"Authorize Client"})})]})}function ke({value:e,accounts:n,onChange:o}){return t(u,{options:n,value:n?.find(t=>t.id===e),onChange:e=>{o(e)},by:"id",optionLabel:e=>e.name,placeholder:"Select Account"})}function je({value:e,projects:n,onChange:o}){return t(u,{by:"id",value:n.find(t=>t.id===e),options:n,optionLabel:e=>e.name,placeholder:"Select Project",onChange:e=>{o(e)}})}function Ce({isOpen:n,onClose:l}){return e(i,{isOpen:n,onClose:l,children:[t(a,{children:"Sign In"}),e(r,{className:"flex justify-center",children:[t(le,{}),t(re,{}),t(ce,{})]}),t(g,{align:"right",children:t(o,{variant:"ghost",onClick:l,children:"Cancel"})})]})}function Se({title:n,value:o}){const[i,a]=S(!1);return e("div",{className:"w-full flex justify-between items-center mb-1",children:[e("div",{className:"flex flex-col w-[calc(100%-3rem)]",children:[t("div",{className:"text-sm px-2 dark:text-slate-200",children:n}),t(b,{description:o,size:"xs",placement:"left",children:e("div",{className:"text-xs truncate text-muted w-full text-left px-2",children:[o," "]})})]}),i?t(X,{className:"size-4 cursor-pointer text-success"}):t(Z,{className:"size-4 cursor-pointer text-gray-400 dark:text-slate-400",onClick:()=>function(e){navigator.clipboard.writeText(e),a(!0),setTimeout(()=>a(!1),2e3)}(o)})]})}function Pe(){const n=A(),{account:o,project:i,client:a,authToken:r}=n,l=new URL(a.baseUrl).hostname,c=new URL(a.store.baseUrl).hostname,s=i?K(i):"",d=[{name:"user",label:"User",content:e("div",{className:"space-y-1 p-2",children:[t(Se,{title:"Organization ID",value:o?.id??"Unknown"}),t(Se,{title:"Project ID",value:i?.id??"Unknown"}),t(Se,{title:"User ID",value:r?.sub??"Unknown"}),t(Se,{title:"Organization Roles",value:r?.account_roles?.join(",")??"Unknown"}),t(Se,{title:"Project Roles",value:r?.project_roles?.join(",")??"Unknown"})]})},{name:"environment",label:"Environment",content:e("div",{className:"space-y-1 p-2",children:[t(Se,{title:"Tenant ID",value:s}),t(Se,{title:"Environment",value:M.type}),t(Se,{title:"Server",value:l}),t(Se,{title:"Store",value:c}),t(Se,{title:"App Version",value:M.version}),t(Se,{title:"SDK Version",value:M.sdkVersion||"unknown"})]})}];return t("div",{className:"w-full",children:e(v,{defaultValue:"user",tabs:d,fullWidth:!0,updateHash:!1,children:[t(x,{}),t(y,{})]})})}function ze({}){const{user:i,isLoading:a}=A(),[r,l]=S(!1);return a?t(s,{}):i?t("div",{className:"px-3",children:t(Ie,{asMenuTrigger:!0})}):e(n,{children:[t(o,{onClick:()=>l(!0),children:"Sign In"}),t(Ce,{isOpen:r,onClose:()=>l(!1)})]})}function Ie({className:n,asMenuTrigger:o=!1}){const i=A(),a=H(),r=te(),{user:l}=i;if(!i||!l)return null;const c=r.hasPermission($.project_admin);return e(Q,{strategy:"fixed",placement:"bottom-start",zIndex:100,children:[t(Q.Trigger,{click:!0,children:t("div",{className:F(n,"flex items-center justify-start",o&&"cursor-pointer"),children:t(N,{size:"sm",color:"bg-amber-500",shape:"circle",name:l?.name})})}),t(Q.Content,{className:"w-[280px] mx-2 my-1",children:t("div",{className:"bg-white dark:bg-slate-900 shadow-lg rounded-md ring-1 ring-gray-200 dark:ring-slate-700",children:e("div",{className:"divide-y divide-gray-200 dark:divide-slate-700",children:[e("div",{className:"py-2 pl-2",children:[t("p",{className:"px-4 dark:text-white mb-1",children:l?.name??"Unknown"}),t("p",{className:"px-4 text-xs text-gray-500",children:l?.email??""})]}),t("div",{className:"w-full p-1",children:t(Pe,{})}),t("div",{className:"py-2 pl-2",children:t(w,{})}),t("div",{className:"py-2",children:e(k,{children:[c&&t(k.Item,{className:"px-2",onClick:()=>a("/settings",{replace:!0}),children:"Settings"}),t(k.Item,{className:"px-2",onClick:()=>i.logout(),children:"Sign out"})]})})]})})})]})}function Le(e){return!!e&&(e.endsWith("@vertesiahq.com")||e.endsWith("@becomposable.com")||e.endsWith("@composableprompts.com"))}function Te({icon:e}){const{isLoading:n}=A(),[o,i]=S(!0);return P(()=>{n||i(!1)},[n]),t(oe,{appear:!0,show:o,as:z,unmount:!0,leave:"transition ease-in duration-500",leaveFrom:"opacity-100",leaveTo:"opacity-0",children:t("div",{style:{zIndex:999999},className:"fixed inset-x-0 inset-y-0",children:t("div",{className:"flex w-full h-full items-center justify-center",children:t("div",{className:"animate-[spin_4s_linear_infinite]",children:t("div",{className:"animate-pulse rounded-full bg-transparent",children:e||t(Ae,{})})})})})})}function Ae(){return e("svg",{className:"w-8 h-8 text-indigo-600",viewBox:"0 0 50 50",xmlns:"http://www.w3.org/2000/svg",children:[t("defs",{children:e("linearGradient",{id:"spinner-gradient",x1:"1",y1:"0",x2:"0",y2:"1",children:[t("stop",{offset:"0%",stopColor:"currentColor",stopOpacity:"1"}),t("stop",{offset:"100%",stopColor:"currentColor",stopOpacity:"0"})]})}),t("circle",{cx:"25",cy:"25",r:"20",stroke:"url(#spinner-gradient)",strokeWidth:"5",fill:"none",strokeLinecap:"round"})]})}function Oe({children:n,lightLogo:o,darkLogo:i,loadingIcon:a}){return t(j,{children:t(q,{children:e(C,{defaultTheme:"system",storageKey:"vite-ui-theme",children:[t(Te,{icon:a}),t(pe,{allowedPrefix:"/shared/",lightLogo:o,darkLogo:i}),t(ne,{children:n})]})})})}const _e=I(null);function Ue({installation:e,children:n}){return t(_e.Provider,{value:e,children:n})}function Ee(){return L(_e)}function De({app:n,onChange:o,placeholder:i}){const{client:a,project:r}=A(),{data:l,error:c}=f(()=>a.apps.getAppInstallationProjects(n),[n.id,n.name]);return c?e("span",{className:"text-red-600",children:["Error: failed to fetch projects: ",c.message]}):t(qe,{placeholder:i,initialValue:r?.id,projects:l||[],onChange:e=>{o&&!o(e)||(localStorage.setItem(R,e.account),localStorage.setItem(W+"-"+e.account,e.id),window.location.reload())}})}function qe({initialValue:e,projects:n,onChange:o,placeholder:i="Select Project"}){const[a,r]=S();let l=!a&&e?n.find(t=>t.id===e):a;return t(u,{by:"id",value:l,options:n,optionLabel:e=>e.name,placeholder:i,onChange:e=>{r(e),o(e)}})}function Re({name:e,AccessDenied:n=Me,children:o}){return e?t(We,{name:e,AccessDenied:n,children:o}):t(Fe,{})}function We({name:e,AccessDenied:n=Me,children:o}){const{authToken:i,client:a}=A(),[r,l]=S(null),[c,s]=S("loading");return P(()=>{if(i){i.apps.includes(e)?a.apps.getAppInstallationByName(e).then(t=>{t?(s("loaded"),l(t)):(console.log(`App ${e} not found!`),s("error"))}):s("error")}else s("loading")},[e,i]),"loading"===c?null:"error"===c?t(n,{name:e}):r?t(Ue,{installation:r,children:o}):void 0}function Me({name:n}){const{project:o,accounts:i,client:a}=A(),[r,l]=S(),{data:c}=f(()=>a.apps.getAppInstallationProjects({name:n}),[n]),{projectsByOrg:s,orgOptions:d}=T(()=>{if(!c||!i)return{projectsByOrg:{},orgOptions:[]};const e={};for(const t of c)e[t.account]||(e[t.account]=[]),e[t.account].push(t);const t=i.filter(t=>e[t.id]?.length>0);return{projectsByOrg:e,orgOptions:t}},[c,i]);P(()=>{!r&&d.length>0&&l(d[0].id)},[d,r]);const m=r&&s[r]||[],h=d.find(e=>e.id===r);return e(p,{className:"pt-10 flex flex-col items-center text-center text-gray-700",children:[t(ee,{className:"w-10 h-10 mb-4 text-gray-500"}),t("div",{className:"text-xl font-semibold",children:"Access Denied"}),e("div",{className:"mt-2 text-sm text-gray-500",children:["You don't have permission to view the ",t("span",{className:"font-semibold",children:n})," app in project: ",e("span",{className:"font-semibold",children:["«",o?.name,"»"]}),"."]}),0===d.length&&void 0!==c&&t("div",{className:"mt-4 text-sm text-gray-500",children:"This app is not installed in any project you have access to."}),d.length>0&&e("div",{className:"mt-4 flex flex-row gap-4 items-end",children:[d.length>1&&e("div",{children:[t("div",{className:"text-sm text-gray-500 mb-2",children:"Organization"}),t(u,{by:"id",value:h,options:d,optionLabel:e=>e.name,placeholder:"Select Organization",onChange:e=>l(e.id)})]}),e("div",{children:[d.length>1&&t("div",{className:"text-sm text-gray-500 mb-2",children:"Project"}),t(u,{by:"id",value:void 0,options:m,optionLabel:e=>e.name,placeholder:"Select Project",onChange:e=>{localStorage.setItem(R,e.account),localStorage.setItem(W+"-"+e.account,e.id),window.location.reload()}})]})]})]})}function Fe(){return e(p,{className:"pt-10 flex flex-col items-center text-center text-gray-700",children:[t(ee,{className:"w-10 h-10 mb-4 text-gray-500"}),t("div",{className:"text-xl font-semibold",children:"Application not registered"}),e("div",{className:"mt-2 text-sm text-gray-500",children:["Before starting to code a Vertesia application you must register an application manifest in Vertesia Studio then install it in one or more projects.",t("p",{}),"Then use the created app name as a parameter to ",t("code",{children:'<StandaloneApp name="your-app-name">'})," in the ",t("code",{children:"src/main.tsx"})," file."]})]})}export{_e as AppInstallationContext,Ue as AppInstallationProvider,De as AppProjectSelector,ie as InviteAcceptModal,pe as SigninScreen,Re as StandaloneApp,We as StandaloneAppImpl,ye as TerminalLogin,ze as UserSessionMenu,Oe as VertesiaShell,Le as isVertesiaEmail,Ee as useAppInstallation};
|
|
1
|
+
import{jsxs as e,jsx as t,Fragment as n}from"react/jsx-runtime";import{Button as i,Modal as o,ModalTitle as a,ModalBody as r,useToast as l,Input as c,Spinner as s,SelectStack as d,SelectBox as u,useSafeLayoutEffect as m,ErrorBox as h,Center as p,useFetch as f,ModalFooter as g,Tabs as v,TabsBar as x,TabsPanel as y,VTooltip as b,Avatar as N,ModeToggle as w,MenuList as k,ToastProvider as j,ThemeProvider as C}from"@vertesia/ui/core";import{useState as S,useEffect as P,Fragment as I,createContext as z,useContext as L,useMemo as T}from"react";import{useUserSession as A,useUXTracking as O,setFirebaseTenant as _,getFirebaseAuth as U,UserNotFoundError as E,fetchComposableTokenFromFirebaseToken as D,UserSessionProvider as q,LastSelectedAccountId_KEY as R,LastSelectedProjectId_KEY as W}from"@vertesia/ui/session";import{Env as M}from"@vertesia/ui/env";import F from"clsx";import{signInWithRedirect as V,OAuthProvider as B,GoogleAuthProvider as Y,GithubAuthProvider as G}from"firebase/auth";import{useLocation as J,useNavigate as H}from"@vertesia/ui/router";import{getTenantIdFromProject as K,Permission as $}from"@vertesia/common";import{Popover as Q}from"@vertesia/ui/widgets";import{Check as X,CopyIcon as Z,LockIcon as ee}from"lucide-react";import{useUserPermissions as te,TypeRegistryProvider as ne,UserPermissionProvider as ie}from"@vertesia/ui/features";import{Transition as oe}from"@headlessui/react";function ae(){const n=A(),{client:l,account:c}=n,[s,d]=S(!1),[u,m]=S([]);P(()=>{l.account.listInvites().then(e=>{if(e.length>0){const t=e.filter(e=>e.data.account);if(0===t.length)return void console.log("No valid invites found, closing modal");console.log("Found valid invites",t.length),d(!0),m(t)}else console.log("No invites found, closing modal"),d(!1)}).catch(e=>{console.error("Error fetching invites",e)})},[c?.id]);const h=()=>d(!1),p=u.map(o=>o.data.account?e("div",{className:"flex flex-row w-full justify-between border rounded-sm px-2 py-2 ",children:[e("div",{className:"flex flex-col",children:[t("div",{className:"w-full font-semibold",children:o.data.account.name??o.data.account}),o.data.project&&e("div",{className:"w-full text-base",children:["- ",o.data.project.name]}),e("div",{className:"text-xs",children:["Role: ",o.data.role]}),o.data.invited_by&&e("div",{className:"text-xs",children:["by ",o.data.invited_by.name??o.data.invited_by]})]}),e("div",{className:"flex flex-col gap-4",children:[t(i,{size:"xs",onClick:()=>(async e=>{await l.account.acceptInvite(e.id),await n.fetchAccounts();const t=u.filter(t=>t.id!==e.id).filter(e=>e.data.account);t.length>0?m(t):h()})(o),children:"Accept"})," ",t(i,{size:"xs",variant:"secondary",onClick:()=>(async e=>{await l.account.rejectInvite(e.id);const t=u.filter(t=>t.id!==e.id);m(t),0===t.length&&h()})(o),children:"Reject"})]})]},o.id):(console.warn("Invite has no account data",o),null));return t("div",{children:e(o,{isOpen:s,onClose:h,children:[t(a,{children:"Review Invites"}),e(r,{children:[t("div",{className:"text-sm pb-4",children:"You have received the following invites to join other accounts. Please review and accept or declined them."}),p]})]})})}function re({redirectTo:o}){const[a,r]=S(!1),{trackEvent:d}=O(),[u,m]=S(""),h=l();return e(n,{children:[t(c,{value:u,onChange:m,placeholder:"Enter your enterprise email",type:"email"}),a?t("div",{className:"w-full flex justify-center",children:t(s,{})}):t(i,{variant:"outline",onClick:async()=>{if(!u)return;/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(u)?(r(!0),_(u).then(e=>{if(!e)return h({title:"Tenant not found",status:"error",duration:5e3}),void r(!1);localStorage.setItem("tenantName",e.name??"");const t=function(e){if(!M.firebase)throw new Error("Firebase configuration is not available in the environment");switch(M.firebase.providerType){case"oidc":default:return new B("oidc.main");case"google":{let t=e||window.location.pathname||"/";"/"!==t[0]&&(t="/"+t);const n=new Y;return n.addScope("profile"),n.addScope("email"),n.setCustomParameters({prompt:"select_account",redirect_uri:window.location.origin+t}),n}case"microsoft":return new B("microsoft.com");case"github":return new B("github.com")}}(o);d("enterprise_signin",{firebaseTenantName:e.name}),M.logger.info("Enterprise single sign-in",{vertesia:{email:u,firebaseTenantName:e.name,firebaseTenantId:e.firebaseTenantId}}),V(U(),t),r(!1)})):h({title:"Invalid email address",status:"error",duration:5e3})},className:"w-full mt-2 py-4 flex rounded-lg hover:shadow-sm transition duration-150 text-center",children:t("span",{className:"text-sm font-semibold",children:"Continue with Enterprise SSO"})})]})}function le({}){return e(i,{variant:"outline",onClick:()=>{localStorage.removeItem("tenantName");let e="https://dengenlabs.firebaseapp.com/__/auth/handler"+window.location.pathname;"/"!==e[0]&&(e="/"+e);const t=new G;t.addScope("profile"),t.addScope("email"),V(U(),t)},className:"w-full py-5 flex rounded-lg hover:shadow-sm transition duration-150 text-center mb-2",children:[t("img",{className:"size-6 bg-white rounded-full",src:"https://www.svgrepo.com/show/503359/github.svg",loading:"lazy",alt:"github logo"}),t("span",{className:"text-sm font-semibold",children:"Continue with GitHub"})]})}function ce({redirectTo:n}){return e(i,{variant:"outline",onClick:()=>{localStorage.removeItem("tenantName");let e=n||window.location.pathname||"/";"/"!==e[0]&&(e="/"+e);const t=new Y;t.addScope("profile"),t.addScope("email"),t.setCustomParameters({prompt:"select_account",redirect_uri:window.location.origin+e}),V(U(),t)},className:"w-full py-5 flex rounded-lg hover:shadow-sm transition duration-150 text-center mb-2",children:[t("img",{className:"size-6",src:"https://www.svgrepo.com/show/475656/google-color.svg",loading:"lazy",alt:"google logo"}),t("span",{className:"text-sm font-semibold",children:"Continue with Google"})]})}function se({redirectTo:n}){return e(i,{variant:"outline",onClick:()=>{localStorage.removeItem("tenantName");let e=n||window.location.pathname||"/";"/"!==e[0]&&(e="/"+e);const t=new B("microsoft.com");t.addScope("profile"),t.addScope("email"),V(U(),t)},className:"w-full py-5 flex rounded-lg hover:shadow-sm transition duration-150 text-center",children:[t("img",{className:"size-6",src:"https://learn.microsoft.com/en-us/entra/identity-platform/media/howto-add-branding-in-apps/ms-symbollockup_mssymbol_19.svg",loading:"lazy",alt:"microsoft logo"}),t("span",{className:"text-sm font-semibold",children:"Continue with Microsoft"})]})}const de=[{id:1,label:"1-10 employees"},{id:11,label:"11-100 employees"},{id:101,label:"101-1000 employees"},{id:1001,label:"1001-5000 employees"},{id:5001,label:"5000+ employees"}],ue=[{id:"personal",label:"Personal",description:"For personal use, or for a small team."},{id:"company",label:"Company",description:"For a company or organization."}],me=[{id:"testing",label:"Just Testing or Evaluating LLMs"},{id:"exploring",label:"Actively Exploring LLMs on a Project"},{id:"using",label:"Already Using LLMs in Production"},{id:"migrating",label:"Migrating to different LLMs"},{id:"other",label:"Other"}];function he({onSignup:o,goBack:a}){const[r,l]=S(void 0),[s,m]=S(void 0),[h,p]=S(void 0),[f,g]=S(void 0),[v,x]=S(void 0),[y,b]=S(void 0),[N,w]=S(void 0),k="company"===r;P(()=>{const e=U().currentUser;e?b(e):console.error("No user found")},[y]);return e("div",{className:"flex flex-col space-y-2",children:[e("div",{className:"prose",children:[e("p",{className:"prose text-sm text-muted pt-4",children:["Welcome to Vertesia, ",y?.displayName," (",y?.email,"). Please tell us a little bit about yourself and you'll be on your way. No credit card is required."]}),N&&t("div",{className:"text-destructive",children:N})]}),t(pe,{label:"Account Type",children:t(d,{options:ue,selected:ue.find(e=>e.id===r),onSelect:e=>l(e.id)})}),k&&e(n,{children:[t(pe,{label:"Company Size",children:t(u,{className:"w-full border border-accent bg-muted",value:s,options:de,onChange:m,optionLabel:e=>e?.label,placeholder:"Select Company Size"})}),t(pe,{label:"Company Name",children:t(c,{value:h,onChange:p,type:"text",required:!0})}),t(pe,{label:"Company Website",children:t(c,{value:f,onChange:g,type:"text"})})]}),t(pe,{label:"Project Maturity",children:t(u,{className:"w-full border border-accent bg-muted",options:me,value:me.find(e=>e.id===v),optionLabel:e=>e?.label,placeholder:"Select Project Maturity",onChange:e=>x(e?.id)})}),e("div",{className:"pt-8 flex flex-col",children:[t(i,{variant:"primary",onClick:async()=>{if(!(r?k&&!h?(w("Please enter an organization name"),0):!k||s||(w("Please select a company size"),0):(w("Please select an account type"),0)))return;if(!r)return;const e={accountType:r,companyName:h,companySize:s?.id,companyWebsite:f,maturity:v};window.localStorage.setItem("composableSignupData",JSON.stringify(e));const t=await(U().currentUser?.getIdToken());console.log("Got firebase token",U(),t),t?o(e,t):console.error("No firebase token found")},size:"xl",children:t("span",{className:"text-lg",children:"Sign Up"})}),t(i,{variant:"ghost",size:"xl",className:"mt-4",onClick:a,children:t("span",{className:"",children:"Wrong account, go back"})})]})]})}function pe({label:n,children:i}){return e("div",{className:"flex flex-col space-y-2 pt-4",children:[t("div",{className:"text-sm text-muted",children:n}),i]})}function fe({allowedPrefix:e,isNested:n=!1,lightLogo:i,darkLogo:o}){const[a,r]=S(!1);return m(()=>{e&&r(window.location.href.startsWith(e))},[]),a?null:t(ge,{isNested:n,lightLogo:i,darkLogo:o})}function ge({isNested:n=!1,lightLogo:i,darkLogo:o}){const{isLoading:a,user:r,authError:l}=A();return a||r?null:t("div",{style:{zIndex:999998},className:(n?"absolute":"fixed")+"overflow-y-auto ",children:e("div",{className:F("flex flex-col items-center justify-center py-14 px-4"),children:[t(ve,{authError:l,lightLogo:i,darkLogo:o}),e("div",{className:"flex gap-x-6 mt-10 justify-center text-muted",children:[t("a",{href:"https://vertesiahq.com/privacy",className:"text-sm",children:"Privacy Policy"}),t("a",{href:"https://vertesiahq.com/terms",className:"text-sm",children:"Terms of Service"})]})]})})}function ve({authError:o,darkLogo:a,lightLogo:r}){const[l,c]=S(void 0),[s,d]=S(!1),{signOut:u}=A(),{trackEvent:m}=O();history.replaceState({},"","/");const h=()=>{c(void 0),d(!0)};P(()=>{o instanceof E&&(console.log("User not found, redirecting to signup"),h())},[o]);return e(n,{children:[r&&t("img",{src:r,alt:"logo",className:"h-15 block dark:hidden"}),a&&t("img",{src:a,alt:"logo",className:"h-15 hidden dark:block"}),l&&e("div",{className:"my-6",children:["Need to make a change?"," ",t(i,{onClick:h,children:" Go back"})]}),t("div",{className:"flex flex-col space-y-2",children:s&&!localStorage.getItem("tenantName")?t(he,{onSignup:(e,t)=>{console.log("Got Signup data",e),c(e);const n={signupData:e,firebaseToken:t};fetch(M.endpoints.studio+"/auth/signup",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)}).then(e=>{console.log("Signup successful",n,e),m("sign_up"),window.location.href="/"})},goBack:()=>{console.log("Going back, signing out"),c(void 0),d(!1),u()}}):e("div",{className:"flex flex-col",children:[t("div",{className:"my-4",children:t("h2",{className:"text-2xl font-bold text-center",children:"Log in or Sign up"})}),e("div",{className:"max-w-2xl text-center my-2 px-2",children:["First time here? No problem, it's free to try!",t("br",{}),"We'll just ask you a couple of questions next and you'll be on your way."]}),e("div",{className:"flex items-center flex-col",children:[e("div",{className:"py-4 w-70",children:[t(ce,{}),t(le,{}),t(se,{})]}),e("div",{className:"flex items-center flex-row w-70 text-muted",children:[t("hr",{className:"w-full"}),t("div",{className:"px-2 text-xs",children:"OR"}),t("hr",{className:"w-full"})]}),t("div",{className:"py-4 w-70",children:t(re,{})})]}),o&&!(o instanceof E)&&t("div",{className:"text-center",children:e("div",{className:"",children:["Sorry, we have not been able to sign you in.",t("br",{}),"Please try again or contact",t("a",{className:"text-info mx-1",href:"mailto:support@vertesiahq.com",children:"support@vertesiahq.com"}),"if it persists.",e("pre",{className:"mt-2",children:["Error: ",o.message]})]})})]})})]})}const xe=new Set(["127.0.0.1","localhost"]);function ye(e){const t=new URLSearchParams(e.search),n=function(e){if(!e)return null;let t,n;try{t=decodeURIComponent(e)}catch{return null}try{n=new URL(t)}catch{return null}return"http:"!==n.protocol?null:n.port?n.username||n.password?null:xe.has(n.hostname)?n.toString():null:null}(t.get("redirect_uri")),i=t.get("code");if(!n||!i)return null;return{redirect:n,code:i,profile:t.get("profile")??"default",project:t.get("project")??void 0,account:t.get("account")??void 0}}function be(){const[e,n]=S(),[i,o]=S(),a=ye(J()),r=l(),c=async e=>{if(!a)return;if(!e.profile)return void r({title:"Profile is required",description:"Please enter a profile name to save the client authorization",status:"error",duration:2e3});if(!e.account)return void r({title:"Account is required",description:"Please select an account to authorize the client to access the ComposablePrompts servers",status:"error",duration:2e3});if(!e.project)return void r({title:"Project is required",description:"Please select a project to authorize the client to access the ComposablePrompts servers",status:"error",duration:2e3});let t;try{const i=await D(e.account,e.project,86400);i?(t={...e,studio_server_url:M.endpoints.studio,zeno_server_url:M.endpoints.zeno,token:i},await fetch(a.redirect,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),n(t)):r({title:"Failed to get composable token",status:"error",duration:5e3})}catch(e){t?(o(e),n(t)):r({title:"Error authorizing client",description:e.message,status:"error",duration:5e3})}},s=a?e?t(we,{payload:e,error:i}):t(Ne,{clientInfo:a,onAccept:c}):t(h,{title:"Invalid request",children:"This page should be called by a terminal client to authenticate against the ComposablePrompts servers"});return t("div",{className:"w-full flex flex-col items-center gap-4 mt-24",children:s})}function Ne({onAccept:i,clientInfo:o}){const{client:a,user:r}=A(),{data:l,error:c}=f(()=>r?a.projects.list():Promise.resolve([]),[r]);if(c)return t(h,{title:"Error loading projects",children:c.message});const d=M.isLocalDev?"Local Dev":M.isDev?"Staging":"Production";return r&&l?e(n,{children:[e("div",{className:"w-1/3",children:[e("div",{className:"mb-4 text-xl font-semibold text-info",children:["Authorizing client on ",d," environment."]}),e("div",{className:"mb-2 text-md text-muted-foreground",children:[t("div",{children:"A client app wants authorization to access the composable prompt servers in your name."}),e("div",{children:["The client app code is ",t("b",{className:"text-foreground",children:o.code}),". You can check if the code is correct in the terminal."]})]}),e("div",{className:"mb-2 text-sm text-muted-foreground",children:[t("div",{children:"You must choose the target account and project for the client to access."}),t("div",{children:"Also, enter a profile name that will be used to save the authorization in your client configuration."})]})]}),t(ke,{onAccept:i,allProjects:l,data:o})]}):t(s,{size:"lg"})}function we({payload:n,error:o}){const a=l();return e("div",{children:[o?e("div",{children:[e(h,{title:"Failed to send the authorization token to the cli tool",children:['This can happen due to security checks on Safari. The error is "',o.message,'"']}),t("div",{children:"Don't worry, you can still authenticate the cli tool by pasting the authentication token in the terminal. You can close this page."})]}):t("div",{children:"The client is authenticated. You can close this page."}),t(p,{className:"mt-4",children:t(i,{variant:"secondary",onClick:()=>{n&&(navigator.clipboard.writeText(JSON.stringify(n)),a({title:"Authentication Payload copied",description:o?"You can paste the authentication payload in the terminal to authenticate the client.":"You can close the page now.",status:"success",duration:5e3}))},children:"Copy the Authentication Payload"})})]})}function ke({allProjects:n,data:o,onAccept:a}){const{accounts:r,account:l,project:s}=A(),[d,u]=S(()=>({profile:o.profile,account:o.account??l?.id,project:o.project??s?.id})),m=n.filter(e=>e.account===d.account);return e("div",{className:"w-1/3",children:[e("div",{className:"mb-4 flex flex-col gap-2",children:[t("span",{className:"font-semibold text-muted-foreground",children:"Profile Name"}),t(c,{type:"text",value:d.profile,onChange:e=>{u({...d,profile:e})}})]}),e("div",{className:"mb-4 flex flex-col gap-2",children:[t("span",{className:"font-semibold text-muted-foreground",children:"Account"}),t(je,{value:d.account,onChange:e=>{u({...d,account:e.id,project:void 0})},accounts:r||[]})]}),e("div",{className:"mb-4 flex flex-col gap-2",children:[t("span",{className:"font-semibold text-muted-foreground",children:"Project"}),t(Ce,{value:d.project,onChange:e=>{u({...d,project:e.id})},projects:m})]}),e("div",{className:"mb-4 text-sm text-attention",children:[t("b",{children:"Note:"})," If your browser asks for permission to access your local network, please allow it. This is required to send the authorization token back to the CLI tool. If not, you will need to copy/paste the token manually in your terminal."]}),t("div",{children:t(i,{size:"xl",onClick:()=>a(d),children:"Authorize Client"})})]})}function je({value:e,accounts:n,onChange:i}){return t(u,{options:n,value:n?.find(t=>t.id===e),onChange:e=>{i(e)},by:"id",optionLabel:e=>e.name,placeholder:"Select Account"})}function Ce({value:e,projects:n,onChange:i}){return t(u,{by:"id",value:n.find(t=>t.id===e),options:n,optionLabel:e=>e.name,placeholder:"Select Project",onChange:e=>{i(e)}})}function Se({isOpen:n,onClose:l}){return e(o,{isOpen:n,onClose:l,children:[t(a,{children:"Sign In"}),e(r,{className:"flex justify-center",children:[t(ce,{}),t(le,{}),t(se,{})]}),t(g,{align:"right",children:t(i,{variant:"ghost",onClick:l,children:"Cancel"})})]})}function Pe({title:n,value:i}){const[o,a]=S(!1);return e("div",{className:"w-full flex justify-between items-center mb-1",children:[e("div",{className:"flex flex-col w-[calc(100%-3rem)]",children:[t("div",{className:"text-sm px-2 dark:text-slate-200",children:n}),t(b,{description:i,size:"xs",placement:"left",children:e("div",{className:"text-xs truncate text-muted w-full text-left px-2",children:[i," "]})})]}),o?t(X,{className:"size-4 cursor-pointer text-success"}):t(Z,{className:"size-4 cursor-pointer text-gray-400 dark:text-slate-400",onClick:()=>function(e){navigator.clipboard.writeText(e),a(!0),setTimeout(()=>a(!1),2e3)}(i)})]})}function Ie(){const n=A(),{account:i,project:o,client:a,authToken:r}=n,l=new URL(a.baseUrl).hostname,c=new URL(a.store.baseUrl).hostname,s=o?K(o):"",d=[{name:"user",label:"User",content:e("div",{className:"space-y-1 p-2",children:[t(Pe,{title:"Organization ID",value:i?.id??"Unknown"}),t(Pe,{title:"Project ID",value:o?.id??"Unknown"}),t(Pe,{title:"User ID",value:r?.sub??"Unknown"}),t(Pe,{title:"Organization Roles",value:r?.account_roles?.join(",")??"Unknown"}),t(Pe,{title:"Project Roles",value:r?.project_roles?.join(",")??"Unknown"})]})},{name:"environment",label:"Environment",content:e("div",{className:"space-y-1 p-2",children:[t(Pe,{title:"Tenant ID",value:s}),t(Pe,{title:"Environment",value:M.type}),t(Pe,{title:"Server",value:l}),t(Pe,{title:"Store",value:c}),t(Pe,{title:"App Version",value:M.version}),t(Pe,{title:"SDK Version",value:M.sdkVersion||"unknown"})]})}];return t("div",{className:"w-full",children:e(v,{defaultValue:"user",tabs:d,fullWidth:!0,updateHash:!1,children:[t(x,{}),t(y,{})]})})}function ze({}){const{user:o,isLoading:a}=A(),[r,l]=S(!1);return a?t(s,{}):o?t("div",{className:"px-3",children:t(Le,{asMenuTrigger:!0})}):e(n,{children:[t(i,{onClick:()=>l(!0),children:"Sign In"}),t(Se,{isOpen:r,onClose:()=>l(!1)})]})}function Le({className:n,asMenuTrigger:i=!1}){const o=A(),a=H(),r=te(),{user:l}=o;if(!o||!l)return null;const c=r.hasPermission($.project_admin);return e(Q,{strategy:"fixed",placement:"bottom-start",zIndex:100,children:[t(Q.Trigger,{click:!0,children:t("div",{className:F(n,"flex items-center justify-start",i&&"cursor-pointer"),children:t(N,{size:"sm",color:"bg-amber-500",shape:"circle",name:l?.name})})}),t(Q.Content,{className:"w-[280px] mx-2 my-1",children:t("div",{className:"bg-white dark:bg-slate-900 shadow-lg rounded-md ring-1 ring-gray-200 dark:ring-slate-700",children:e("div",{className:"divide-y divide-gray-200 dark:divide-slate-700",children:[e("div",{className:"py-2 pl-2",children:[t("p",{className:"px-4 dark:text-white mb-1",children:l?.name??"Unknown"}),t("p",{className:"px-4 text-xs text-gray-500",children:l?.email??""})]}),t("div",{className:"w-full p-1",children:t(Ie,{})}),t("div",{className:"py-2 pl-2",children:t(w,{})}),t("div",{className:"py-2",children:e(k,{children:[c&&t(k.Item,{className:"px-2",onClick:()=>a("/settings",{replace:!0}),children:"Settings"}),t(k.Item,{className:"px-2",onClick:()=>o.logout(),children:"Sign out"})]})})]})})})]})}function Te(e){return!!e&&(e.endsWith("@vertesiahq.com")||e.endsWith("@becomposable.com")||e.endsWith("@composableprompts.com"))}function Ae({icon:e}){const{isLoading:n}=A(),[i,o]=S(!0);return P(()=>{n||o(!1)},[n]),t(oe,{appear:!0,show:i,as:I,unmount:!0,leave:"transition ease-in duration-500",leaveFrom:"opacity-100",leaveTo:"opacity-0",children:t("div",{style:{zIndex:999999,position:"fixed",inset:0},className:"fixed inset-x-0 inset-y-0",children:t("div",{style:{display:"flex",width:"100%",height:"100%",alignItems:"center",justifyContent:"center"},className:"flex w-full h-full items-center justify-center",children:t("div",{className:"animate-[spin_4s_linear_infinite]",children:t("div",{className:"animate-pulse rounded-full bg-transparent",children:e||t(Oe,{})})})})})})}function Oe(){return e("svg",{width:"32",height:"32",className:"w-8 h-8 text-indigo-600",viewBox:"0 0 50 50",xmlns:"http://www.w3.org/2000/svg",children:[t("defs",{children:e("linearGradient",{id:"spinner-gradient",x1:"1",y1:"0",x2:"0",y2:"1",children:[t("stop",{offset:"0%",stopColor:"currentColor",stopOpacity:"1"}),t("stop",{offset:"100%",stopColor:"currentColor",stopOpacity:"0"})]})}),t("circle",{cx:"25",cy:"25",r:"20",stroke:"url(#spinner-gradient)",strokeWidth:"5",fill:"none",strokeLinecap:"round"})]})}function _e({children:n,lightLogo:i,darkLogo:o,loadingIcon:a}){return t(j,{children:t(q,{children:t(ne,{children:e(C,{defaultTheme:"system",storageKey:"vite-ui-theme",children:[t(Ae,{icon:a}),t(fe,{allowedPrefix:"/shared/",lightLogo:i,darkLogo:o}),t(ie,{children:n})]})})})})}const Ue=z(null);function Ee({installation:e,children:n}){return t(Ue.Provider,{value:e,children:n})}function De(){return L(Ue)}function qe({app:n,onChange:i,placeholder:o}){const{client:a,project:r}=A(),{data:l,error:c}=f(()=>a.apps.getAppInstallationProjects(n),[n.id,n.name]);return c?e("span",{className:"text-red-600",children:["Error: failed to fetch projects: ",c.message]}):t(Re,{placeholder:o,initialValue:r?.id,projects:l||[],onChange:e=>{i&&!i(e)||(localStorage.setItem(R,e.account),localStorage.setItem(W+"-"+e.account,e.id),window.location.reload())}})}function Re({initialValue:e,projects:n,onChange:i,placeholder:o="Select Project"}){const[a,r]=S();let l=!a&&e?n.find(t=>t.id===e):a;return t(u,{by:"id",value:l,options:n,optionLabel:e=>e.name,placeholder:o,onChange:e=>{r(e),i(e)}})}function We({name:e,AccessDenied:n=Fe,children:i}){return e?t(Me,{name:e,AccessDenied:n,children:i}):t(Ve,{})}function Me({name:e,AccessDenied:n=Fe,children:i}){const{authToken:o,client:a}=A(),[r,l]=S(null),[c,s]=S("loading");return P(()=>{if(o){o.apps.includes(e)?a.apps.getAppInstallationByName(e).then(t=>{t?(s("loaded"),l(t)):(console.log(`App ${e} not found!`),s("error"))}):s("error")}else s("loading")},[e,o]),"loading"===c?null:"error"===c?t(n,{name:e}):r?t(Ee,{installation:r,children:i}):void 0}function Fe({name:n}){const{project:i,accounts:o,client:a}=A(),[r,l]=S(),{data:c}=f(()=>a.apps.getAppInstallationProjects({name:n}),[n]),{projectsByOrg:s,orgOptions:d}=T(()=>{if(!c||!o)return{projectsByOrg:{},orgOptions:[]};const e={};for(const t of c)e[t.account]||(e[t.account]=[]),e[t.account].push(t);const t=o.filter(t=>e[t.id]?.length>0);return{projectsByOrg:e,orgOptions:t}},[c,o]);P(()=>{!r&&d.length>0&&l(d[0].id)},[d,r]);const m=r&&s[r]||[],h=d.find(e=>e.id===r);return e(p,{className:"pt-10 flex flex-col items-center text-center text-gray-700",children:[t(ee,{className:"w-10 h-10 mb-4 text-gray-500"}),t("div",{className:"text-xl font-semibold",children:"Access Denied"}),e("div",{className:"mt-2 text-sm text-gray-500",children:["You don't have permission to view the ",t("span",{className:"font-semibold",children:n})," app in project: ",e("span",{className:"font-semibold",children:["«",i?.name,"»"]}),"."]}),0===d.length&&void 0!==c&&t("div",{className:"mt-4 text-sm text-gray-500",children:"This app is not installed in any project you have access to."}),d.length>0&&e("div",{className:"mt-4 flex flex-row gap-4 items-end",children:[d.length>1&&e("div",{children:[t("div",{className:"text-sm text-gray-500 mb-2",children:"Organization"}),t(u,{by:"id",value:h,options:d,optionLabel:e=>e.name,placeholder:"Select Organization",onChange:e=>l(e.id)})]}),e("div",{children:[d.length>1&&t("div",{className:"text-sm text-gray-500 mb-2",children:"Project"}),t(u,{by:"id",value:void 0,options:m,optionLabel:e=>e.name,placeholder:"Select Project",onChange:e=>{localStorage.setItem(R,e.account),localStorage.setItem(W+"-"+e.account,e.id),window.location.reload()}})]})]})]})}function Ve(){return e(p,{className:"pt-10 flex flex-col items-center text-center text-gray-700",children:[t(ee,{className:"w-10 h-10 mb-4 text-gray-500"}),t("div",{className:"text-xl font-semibold",children:"Application not registered"}),e("div",{className:"mt-2 text-sm text-gray-500",children:["Before starting to code a Vertesia application you must register an application manifest in Vertesia Studio then install it in one or more projects.",t("p",{}),"Then use the created app name as a parameter to ",t("code",{children:'<StandaloneApp name="your-app-name">'})," in the ",t("code",{children:"src/main.tsx"})," file."]})]})}export{Ue as AppInstallationContext,Ee as AppInstallationProvider,qe as AppProjectSelector,ae as InviteAcceptModal,fe as SigninScreen,We as StandaloneApp,Me as StandaloneAppImpl,be as TerminalLogin,ze as UserSessionMenu,_e as VertesiaShell,Te as isVertesiaEmail,De as useAppInstallation};
|
|
2
2
|
//# sourceMappingURL=vertesia-ui-shell.js.map
|