@vertesia/ui 1.5.0-dev.20260910.052912Z → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/core/components/Link.js +1 -1
- package/lib/core/components/Link.js.map +1 -1
- package/lib/core/components/Spinner.js +1 -1
- package/lib/core/components/Spinner.js.map +1 -1
- package/lib/core/components/TagsInput.js +7 -7
- package/lib/core/components/TagsInput.js.map +1 -1
- package/lib/core/components/shadcn/badge.js +1 -1
- package/lib/core/components/shadcn/badge.js.map +1 -1
- package/lib/core/components/shadcn/button.d.ts.map +1 -1
- package/lib/core/components/shadcn/button.js +2 -2
- package/lib/core/components/shadcn/button.js.map +1 -1
- package/lib/core/components/shadcn/filters/comboBox/DateCombobox.js +2 -2
- package/lib/core/components/shadcn/filters/comboBox/DateCombobox.js.map +1 -1
- package/lib/core/components/shadcn/filters/comboBox/SelectCombobox.js +1 -1
- package/lib/core/components/shadcn/filters/comboBox/SelectCombobox.js.map +1 -1
- package/lib/core/components/shadcn/filters/comboBox/StringListCombobox.js +1 -1
- package/lib/core/components/shadcn/filters/comboBox/StringListCombobox.js.map +1 -1
- package/lib/core/components/shadcn/filters/comboBox/TextCombobox.js +1 -1
- package/lib/core/components/shadcn/filters/comboBox/TextCombobox.js.map +1 -1
- package/lib/core/components/shadcn/filters/filter/dateFilter.js +1 -1
- package/lib/core/components/shadcn/filters/filter/dateFilter.js.map +1 -1
- package/lib/core/components/shadcn/filters/filters.js +1 -1
- package/lib/core/components/shadcn/filters/filters.js.map +1 -1
- package/lib/core/components/shadcn/overflowTabs.js +2 -2
- package/lib/core/components/shadcn/overflowTabs.js.map +1 -1
- package/lib/core/components/shadcn/tabs.js +1 -1
- package/lib/core/components/shadcn/tabs.js.map +1 -1
- package/lib/features/activity-doc/ActivityDoc.js +1 -1
- package/lib/features/activity-doc/ActivityDoc.js.map +1 -1
- package/lib/features/agent/chat/AskUserWidget.d.ts +1 -0
- package/lib/features/agent/chat/AskUserWidget.d.ts.map +1 -1
- package/lib/features/agent/chat/AskUserWidget.js +15 -4
- package/lib/features/agent/chat/AskUserWidget.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +5 -3
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +3 -2
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
- package/lib/features/layout/GenericPageNavHeader.js +1 -1
- package/lib/features/layout/GenericPageNavHeader.js.map +1 -1
- package/lib/features/magic-pdf/AnnotatedImageSlider.js +1 -1
- package/lib/features/magic-pdf/AnnotatedImageSlider.js.map +1 -1
- package/lib/features/magic-pdf/GroundedExtractionView.js +3 -3
- package/lib/features/magic-pdf/GroundedExtractionView.js.map +1 -1
- package/lib/features/permissions/UserPermissionsProvider.js +1 -1
- package/lib/features/permissions/UserPermissionsProvider.js.map +1 -1
- package/lib/features/store/objects/DocumentPreviewPanel.js +5 -5
- package/lib/features/store/objects/DocumentPreviewPanel.js.map +1 -1
- package/lib/features/store/objects/components/DocumentInput.js +1 -1
- package/lib/features/store/objects/components/DocumentInput.js.map +1 -1
- package/lib/features/store/objects/upload/DocumentUploadModal.js +1 -1
- package/lib/features/store/objects/upload/DocumentUploadModal.js.map +1 -1
- package/lib/features/user/UserInfo.d.ts +2 -0
- package/lib/features/user/UserInfo.d.ts.map +1 -1
- package/lib/features/user/UserInfo.js +1 -1
- package/lib/features/user/UserInfo.js.map +1 -1
- package/lib/router/RouteComponent.d.ts.map +1 -1
- package/lib/router/RouteComponent.js +15 -12
- package/lib/router/RouteComponent.js.map +1 -1
- package/lib/session/UserSession.d.ts.map +1 -1
- package/lib/session/UserSession.js +4 -0
- package/lib/session/UserSession.js.map +1 -1
- package/lib/shell/SplashScreen.js +1 -1
- package/lib/shell/SplashScreen.js.map +1 -1
- package/lib/vertesia-ui-core.js +3 -3
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-features.js +32 -32
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-router.js +1 -1
- package/lib/vertesia-ui-router.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/lib/vertesia-ui-widgets.js +2 -2
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/lib/widgets/json-view/JSONCode.js +1 -1
- package/lib/widgets/json-view/JSONCode.js.map +1 -1
- package/lib/widgets/upload/DropZone.d.ts.map +1 -1
- package/lib/widgets/upload/DropZone.js +1 -1
- package/lib/widgets/upload/DropZone.js.map +1 -1
- package/package.json +7 -7
- package/src/core/components/Link.tsx +1 -1
- package/src/core/components/Spinner.tsx +1 -1
- package/src/core/components/TagsInput.tsx +7 -7
- package/src/core/components/shadcn/badge.tsx +1 -1
- package/src/core/components/shadcn/button.tsx +2 -3
- package/src/core/components/shadcn/filters/comboBox/DateCombobox.tsx +2 -2
- package/src/core/components/shadcn/filters/comboBox/SelectCombobox.tsx +1 -1
- package/src/core/components/shadcn/filters/comboBox/StringListCombobox.tsx +1 -1
- package/src/core/components/shadcn/filters/comboBox/TextCombobox.tsx +1 -1
- package/src/core/components/shadcn/filters/filter/dateFilter.tsx +1 -1
- package/src/core/components/shadcn/filters/filters.tsx +1 -1
- package/src/core/components/shadcn/overflowTabs.tsx +2 -2
- package/src/core/components/shadcn/tabs.tsx +1 -1
- package/src/css/utilities.css +7 -0
- package/src/features/activity-doc/ActivityDoc.tsx +1 -1
- package/src/features/agent/chat/AskUserWidget.test.tsx +24 -2
- package/src/features/agent/chat/AskUserWidget.tsx +19 -4
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +62 -0
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +8 -7
- package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +4 -3
- package/src/features/layout/GenericPageNavHeader.test.tsx +36 -0
- package/src/features/layout/GenericPageNavHeader.tsx +1 -1
- package/src/features/magic-pdf/AnnotatedImageSlider.tsx +1 -1
- package/src/features/magic-pdf/GroundedExtractionView.tsx +3 -3
- package/src/features/permissions/UserPermissionsProvider.tsx +1 -1
- package/src/features/store/objects/DocumentPreviewPanel.tsx +10 -10
- package/src/features/store/objects/components/DocumentInput.tsx +1 -1
- package/src/features/store/objects/upload/DocumentUploadModal.tsx +1 -1
- package/src/features/user/UserInfo.tsx +1 -1
- package/src/router/RouteComponent.test.tsx +65 -1
- package/src/router/RouteComponent.tsx +18 -15
- package/src/session/UserSession.test.ts +65 -0
- package/src/session/UserSession.ts +4 -0
- package/src/shell/SplashScreen.tsx +1 -1
- package/src/widgets/json-view/JSONCode.test.tsx +2 -2
- package/src/widgets/json-view/JSONCode.tsx +1 -1
- package/src/widgets/upload/DropZone.tsx +2 -4
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsx as e,jsxs as t}from"react/jsx-runtime";import{createContext as n,useContext as r,useEffect as i,useMemo as a,useRef as o,useState as s}from"react";import{useSafeLayoutEffect as c}from"@vertesia/ui/core";var l=Object.defineProperty,u=(e,t)=>{let n={};for(var r in e)l(n,r,{get:e[r],enumerable:!0});return t||l(n,Symbol.toStringTag,{value:`Module`}),n},d=class{path;params;hash;constructor(e){let t=e.lastIndexOf(`#`);t>-1?(this.hash=e.substring(t),e=e.substring(0,t)):this.hash=``,t=e.indexOf(`?`),t>-1?(this.path=e.substring(0,t),this.params=new URLSearchParams(e.substring(t+1))):(this.path=e,this.params=new URLSearchParams)}add(e){for(let[t,n]of Object.entries(e))this.params.set(t,n);return this}toString(){return`${this.path}?${this.params.toString()}${this.hash}`}},f=u({getMountBasename:()=>h,getPathSegments:()=>v,isRootPath:()=>p,joinPath:()=>m,stripMountBasename:()=>_,toSegments:()=>y,withMountBasename:()=>g});function p(e){return e===`/`||e===``}function m(e,t){return t=e.endsWith(`/`)&&t.startsWith(`/`)?e+t.substring(1):e.endsWith(`/`)||t.startsWith(`/`)?e+t:`${e}/${t}`,t}function h(){if(typeof document>`u`)return``;let e=document.querySelector(`base[href]`);if(!e)return``;try{return new URL(e.href,document.URL).pathname.replace(/\/+$/,``)}catch{return``}}function g(e){let t=h();return!t||!e.startsWith(`/`)||e===t||e.startsWith(`${t}/`)?e:m(t,e)}function _(e){let t=h();return t?e===t?`/`:e.startsWith(`${t}/`)?e.slice(t.length)||`/`:e:e}function v(e){if(e===``)return[];if(e===`/`)return[``];let t=0,n=e.length;return e.startsWith(`./`)?t=2:e.startsWith(`/`)&&(t=1),e.endsWith(`/`)&&(n=e.length-1),(t>0||n<e.length?e.substring(t,n):e).split(`/`)}function y(e){if(typeof e==`string`)return v(e);if(Array.isArray(e))return e;throw Error(`Unsupported path object: ${e}`)}const b=Symbol(`BASE_PATH`);var x=class{name;type;location;state;_canceled=!1;constructor(e,t,n,r){this.name=e,this.type=t,this.location=n,this.state=r}get isPageLoad(){return this.type===`initial`}get isBackForward(){return this.type===`popState`}get isLinkClick(){return this.type===`linkClick`}get isNavigation(){return this.type===`navigate`}get isCancelable(){return this.name===`beforeChange`}cancel(){if(this.name===`afterChange`)throw Error(`Cannot cancel afterChange event`);this._canceled=!0}},S=class extends x{constructor(e,t,n){super(`beforeChange`,e,t,n)}},C=class extends x{constructor(e,t,n){super(`afterChange`,e,t,n)}};function w(e){return e instanceof Element?e.closest(`a[href]`):null}function T(e){let t=e.href.trim();return t.length>0?new URL(t):null}var E=class{stickyParams;_popStateListener;_linkNavListener;_listeners=[];addListener(e){this._listeners.push(e)}fireLocationChange(e){for(let t of this._listeners)t(e)}firePageLoad(){this.fireLocationChange(new C(`initial`,new URL(window.location.href)))}addStickyParams(e){return this.stickyParams?new d(e).add(this.stickyParams).toString():e}normalizeCurrentUrl(){if(typeof window>`u`||!this.stickyParams)return;let e=window.location.pathname+window.location.search+window.location.hash,t=this.addStickyParams(e);if(t!==e){let e=new URL(t,window.location.href);window.history.replaceState(window.history.state,``,e.href)}}navigate(e,t={}){if(t.stepsBack&&t.stepsBack>0){this.stepBack(t.stepsBack,t);return}if(t.basePath){let n=t.basePath;n.startsWith(`/`)||(n=`/${n}`),e=m(n,e)}e=this.addStickyParams(e),e=g(e),this._navigate(new URL(e,window.location.href),`navigate`,t)}stepBack(e,t={}){let n=window.history.state.historyChain||[],r=n.length>=e?new URL(n[n.length-e].href,window.location.href):new URL(window.location.origin,window.location.href);this._navigate(r,`popState`,t);let i={from:window.location.href,historyChain:n.slice(0,-e),data:t.state||void 0,title:t.title||document.title};window.history.replaceState(i,``,r.href),this.fireLocationChange(new C(`popState`,r,t.state))}_navigate(e,t,n){let r=new S(t,e,n.state);if(this.fireLocationChange(r),r._canceled)return;let i=window.history.state,a={title:n.title||document.title,href:window.location.pathname+window.location.search+window.location.hash},o=[];!n.replace&&i?.historyChain&&(o=[...i.historyChain]),n.replace||(o.push(a),o.length>10&&(o=o.slice(-10)));let s={from:window.location.href,historyChain:o,data:n.state||void 0,title:n.title||document.title};window.history[n.replace?`replaceState`:`pushState`](s,``,e.href),this.fireLocationChange(new C(t,e,n.state))}start(){if(typeof window>`u`)return;let e=e=>{let t,n=new URL(window.location.href),r;e.state?(t=`popState`,r=e.state.data):t=`linkClick`,this.fireLocationChange(new C(t,n,r))},t=e=>{if(e.defaultPrevented||e.metaKey||e.altKey||e.ctrlKey||e.shiftKey||e.button!==0)return;let t=w(e.target);if(!t)return;let n=t.getAttribute(`target`);if(t.hasAttribute(`download`)||t.href.startsWith(`blob:`)||n&&n.toLowerCase()!==`_self`)return;let r=T(t);if(r&&r.origin===window.location.origin){e.preventDefault();let t=new URL(this.addStickyParams(r.href)),n=e[b]||e.target[b];n&&(t.pathname=m(n,t.pathname)),this._navigate(t,`linkClick`,{})}};this._popStateListener=e,this._linkNavListener=t,window.addEventListener(`popstate`,e),document.body.addEventListener(`click`,this._linkNavListener)}stop(){this._popStateListener&&window.removeEventListener(`popstate`,this._popStateListener),this._linkNavListener&&document.body.removeEventListener(`click`,this._linkNavListener)}};function D({basePath:t,children:n}){let r=o(null);return i(()=>{if(r.current){let e=r.current,n=e=>{e.target.tagName.toLowerCase()===`a`&&(e[b]=t)};return e.addEventListener(`click`,n),()=>{e.removeEventListener(`click`,n)}}},[t]),e(`div`,{ref:r,className:`h-full w-full`,children:n})}var O=class{tree=new A;loadMapping(e){for(let[t,n]of Object.entries(e))this.addSegments(v(t),n)}addPath(e,t){this.addSegments(v(e),t)}addSegments(e,t){let n=this.tree;for(let r=0,i=e.length;r<i;r++){let a=e[r];if(a[0]===`:`){let t=n.wildcard;if(!t)t=new M(a),n.wildcard=t;else if(!(t instanceof M))throw Error(`Failed to index path segments: ${e.join(`/`)}. A wildcard ":" segment will overwrite an existing wildcard segment at path: ${`/${e.slice(0,r).join(`/`)}`}`);n=t}else if(a===`*`){if(n.wildcard)throw Error(`Failed to index path segments: ${e.join(`/`)}. A wildcard "*" segment already exists at path: ${`/${e.slice(0,r).join(`/`)}`}`);if(n.wildcard=new N(a,t),r<i-1)throw Error(`Failed to index path segments: ${e.join(`/`)}. A wildcard segment must be the last segment`);return}else{let e=n.children[a];e||(e=new j(a),n.children[a]=e),n=e}}if(n.value!==void 0)throw Error(`Failed to index path segments: ${e.join(`/`)}. A value already exists at path: ${`/${e.join(`/`)}`}`);n.value=t}match(e){let t=y(e);if(t.length===0)return null;let n={},r=this.tree;for(let e of t){let t=r.match(e,n);if(t)r=t;else return null}if(!r.value){if(r instanceof k&&r.wildcard instanceof N&&(r=r.wildcard.match(``,n),!r.value))throw Error("Wildcard segment node `*` must have a value");if(!r.value)return null}let i,a;return n._&&n._.length>0?(i=t.slice(0,-n._.length),a=n._):i=t,{params:n,matchedSegments:i,remainingSegments:a,value:r.value}}},k=class{name;value;children={};wildcard;constructor(e,t){this.name=e,this.value=t}match(e,t){let n=this.children[e];if(n)return n;if(this.wildcard){if(this.wildcard instanceof N)return this.wildcard.match(e,t);if(this.wildcard instanceof M)return t[this.wildcard.paramName]=e,this.wildcard;throw Error(`Unknown wildcard segment node type: ${this.wildcard.constructor.name}`)}return null}},A=class extends k{constructor(){super(`#root`)}},j=class extends k{},M=class extends k{paramName;constructor(e,t){super(e,t),this.paramName=e.substring(1)}},N=class{name;value;constructor(e,t){this.name=e,this.value=t}match(e,t){return t._?e&&t._.push(e):t._=e?[e]:[],this}},P=class{index;matcher=new O;constructor(e,t){this.index=t;for(let t of e)this.matcher.addPath(t.path,t)}match(e){let t=p(e)&&this.index;return this.matcher.match(t||e)}},F=class extends P{prompt;observer;navigator=new E;constructor(e,t){super(e),this.navigator.addListener(e=>{if(!(e.isCancelable&&this.prompt?.when&&!window.confirm(this.prompt.message))&&(this.observer&&this.observer(e),e.name===`afterChange`)){let n=this.match(_(e.location.pathname));n?.value?t({...n,state:e.state}):t(null)}})}getTopRouter(){return this}setStickyParams(e){this.navigator.stickyParams=e??void 0,this.navigator.normalizeCurrentUrl()}withObserver(e){return this.observer=e,this}start(){this.navigator.start(),this.navigator.firePageLoad()}stop(){this.navigator.stop()}navigate(e,t){this.navigator.navigate(e,t)}},I=class extends P{parent;basePath;constructor(e,t,n){super(n),this.parent=e,this.basePath=t}getTopRouter(){return this.parent instanceof F?this.parent:this.parent.getTopRouter()}navigate(e,t){let n=t?.isBasePathNested??!0,r;if(n){let n=t?.basePath;r=n?m(this.basePath,n):this.basePath,this.parent.navigate(e,{...t,basePath:r})}else this.parent.navigate(e,{...t,basePath:t?.basePath})}};const L=n(void 0);function R(){let e=r(L);if(!e)throw Error(`useRouter must be used within a RouterProvider`);return e}function z(){let{navigate:e}=R();return e}function B(){let{router:e}=R(),t=h();return e instanceof I?t?e.basePath===`/`?t:m(t,e.basePath):e.basePath:t}function V(e){let{params:t}=R();return e?t[e]:t}function H(){let{location:e}=R();return e}function U(e){i(()=>{let t=document.title;return document.title=e,()=>{document.title=t}},[e])}function W(e){let{router:t}=R(),n=t.getTopRouter();i(()=>(n.prompt=e,()=>{n.prompt=void 0}),[e,n]),i(()=>{if(e.when){let t=e.when,n=e=>{t&&(e.preventDefault(),e.returnValue=``)};return window.addEventListener(`beforeunload`,n),()=>{window.removeEventListener(`beforeunload`,n)}}},[e.when])}function G(e){return e.metaKey||e.altKey||e.ctrlKey||e.shiftKey||e.button!==0}function K({children:t,to:n,onClick:r,replace:i=!0}){let a=z();return e(`span`,{onClick:e=>{let t=e.target.closest(`a`),o=t?.getAttribute(`href`),s=n??o;t&&s&&(e.stopPropagation(),e.preventDefault(),a(s,{replace:i}),r?.(e))},children:t})}function q({children:t,href:n,className:r,target:i,topLevelNav:a,clearBreadcrumbs:o=!1,replace:s,skipStickyParams:c}){let{router:l}=R(),u=!n||n.startsWith(`#`),d=/^[a-z][a-z0-9+.-]*:/i.test(n)||n.startsWith(`//`),f=!u&&!d,p=!!i&&i!==`_self`,m=f?g(c?n:l.getTopRouter().navigator.addStickyParams(n)):n;return e(`a`,{href:m,className:r,target:i,onClick:e=>{e.defaultPrevented||!f||p||G(e)||(e.stopPropagation(),e.preventDefault(),(a?l.getTopRouter():l).navigate(n,{replace:s??o}))},children:t})}const J=new Map;function Y(e){return e.LazyComponent().then(t=>{if(!t.default)throw Error(`Lazy module for ${e.path} does not have a default export`);return J.set(e.LazyComponent,t.default),t.default})}function X(e){return Promise.allSettled(e.filter(e=>!!e.LazyComponent).map(e=>Y(e)))}function Z({spinner:t}){let n=R(),r=n.route;if(r.Component){let t=r.Component;return e(t,{...n.params})}if(r.LazyComponent)return e(ee,{route:r,spinner:t},r.path);throw Error(`Invalid route for ${r.path}. Either Component or LazyCOmponent must be specified.`)}function ee({route:t,spinner:n}){let[r,a]=s(()=>J.get(t.LazyComponent)??null);return i(()=>{let e=!1;return Y(t).then(t=>{e||a(()=>t)}),()=>{e=!0}},[t]),r?e(r,{}):n||null}function te({basePath:t,isBasePathNested:n=!0,fixLinks:r=!1,children:i}){let a=R(),o=r?t=>e(D,{basePath:a.matchedRoutePath,children:t}):e=>e;return e(L.Provider,{value:{...a,navigate:(e,r)=>{if(r?.isBasePathNested===!1)return a.navigate(e,r);let i=r?.basePath?m(t,r.basePath):t;return a.navigate(e,{...r,basePath:i,isBasePathNested:n})}},children:o(i||e(Z,{}))})}function ne(){let e=R();return t(`div`,{children:[`Route not found for path `,e.matchedRoutePath]})}function Q(){return{params:{},matchedSegments:[],state:null,value:{path:`virtual:404`,Component:()=>e(ne,{})}}}function re({routes:t,index:n,children:r,fixLinks:i=!1}){let s=R(),c=o(s.matchedRoutePath),l=a(()=>{if(typeof window>`u`)return null;let e=new I(s.router,c.current,t);return e.index=n,e},[s.router,n,t]),u=a(()=>{if(l&&s.matchedRoutePath===l.basePath)return l.match(s.remainingPath||`/`)||Q()},[s.matchedRoutePath,s.remainingPath,l]),d=i?t=>e(D,{basePath:s.matchedRoutePath,children:t}):e=>e;return l&&u&&e(L.Provider,{value:{...s,router:l,route:u.value,params:u.params,matchedRoutePath:`/${u.matchedSegments.join(`/`)}`,remainingPath:u.remainingSegments?`/${u.remainingSegments.join(`/`)}`:void 0,navigate:(e,t)=>{if(l)return l.navigate(e,t)}},children:d(r||e(Z,{}))})}function $({to:e,replace:t=!0,state:n}){let r=z();return i(()=>{r(e,{replace:t,state:n})},[r,t,n,e]),null}function ie(t,n={}){return function(){return e($,{to:t,replace:n.replace,state:n.state})}}function ae({routes:t,index:n,onChange:r,children:i}){let[o,l]=s(void 0),u=a(()=>{if(typeof window>`u`)return null;let e=new F(t,t=>{t===null&&(t=Q()),l({location:window.location,route:t.value,params:t.params,state:t.state,router:e,matchedRoutePath:`/${t.matchedSegments.join(`/`)}`,remainingPath:t.remainingSegments?`/${t.remainingSegments.join(`/`)}`:void 0,navigate:(t,n)=>e.navigate(t,n)})}).withObserver(r);return e.index=n,e},[n,r,t]);return c(()=>(u?.start(),()=>{u?.stop()}),[u]),o&&e(L.Provider,{value:o,children:i||e(Z,{})})}export{C as AfterLocationChangeEvent,b as BASE_PATH,P as BaseRouter,S as BeforeLocationChangeEvent,D as FixLinks,E as HistoryNavigator,x as LocationChangeEvent,K as Nav,q as NavLink,te as NestedNavigationContext,I as NestedRouter,re as NestedRouterProvider,f as Path,O as PathMatcher,L as ReactRouterContext,$ as Redirect,Z as RouteComponent,F as Router,ae as RouterProvider,X as prefetchLazyRoutes,ie as redirectTo,H as useLocation,z as useNavigate,W as useNavigationPrompt,U as usePageTitle,V as useParams,B as useRouterBasePath,R as useRouterContext};
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import{createContext as n,useContext as r,useEffect as i,useMemo as a,useRef as o,useState as s}from"react";import{useSafeLayoutEffect as c}from"@vertesia/ui/core";var l=Object.defineProperty,u=(e,t)=>{let n={};for(var r in e)l(n,r,{get:e[r],enumerable:!0});return t||l(n,Symbol.toStringTag,{value:`Module`}),n},d=class{path;params;hash;constructor(e){let t=e.lastIndexOf(`#`);t>-1?(this.hash=e.substring(t),e=e.substring(0,t)):this.hash=``,t=e.indexOf(`?`),t>-1?(this.path=e.substring(0,t),this.params=new URLSearchParams(e.substring(t+1))):(this.path=e,this.params=new URLSearchParams)}add(e){for(let[t,n]of Object.entries(e))this.params.set(t,n);return this}toString(){return`${this.path}?${this.params.toString()}${this.hash}`}},f=u({getMountBasename:()=>h,getPathSegments:()=>v,isRootPath:()=>p,joinPath:()=>m,stripMountBasename:()=>_,toSegments:()=>y,withMountBasename:()=>g});function p(e){return e===`/`||e===``}function m(e,t){return t=e.endsWith(`/`)&&t.startsWith(`/`)?e+t.substring(1):e.endsWith(`/`)||t.startsWith(`/`)?e+t:`${e}/${t}`,t}function h(){if(typeof document>`u`)return``;let e=document.querySelector(`base[href]`);if(!e)return``;try{return new URL(e.href,document.URL).pathname.replace(/\/+$/,``)}catch{return``}}function g(e){let t=h();return!t||!e.startsWith(`/`)||e===t||e.startsWith(`${t}/`)?e:m(t,e)}function _(e){let t=h();return t?e===t?`/`:e.startsWith(`${t}/`)?e.slice(t.length)||`/`:e:e}function v(e){if(e===``)return[];if(e===`/`)return[``];let t=0,n=e.length;return e.startsWith(`./`)?t=2:e.startsWith(`/`)&&(t=1),e.endsWith(`/`)&&(n=e.length-1),(t>0||n<e.length?e.substring(t,n):e).split(`/`)}function y(e){if(typeof e==`string`)return v(e);if(Array.isArray(e))return e;throw Error(`Unsupported path object: ${e}`)}const b=Symbol(`BASE_PATH`);var x=class{name;type;location;state;_canceled=!1;constructor(e,t,n,r){this.name=e,this.type=t,this.location=n,this.state=r}get isPageLoad(){return this.type===`initial`}get isBackForward(){return this.type===`popState`}get isLinkClick(){return this.type===`linkClick`}get isNavigation(){return this.type===`navigate`}get isCancelable(){return this.name===`beforeChange`}cancel(){if(this.name===`afterChange`)throw Error(`Cannot cancel afterChange event`);this._canceled=!0}},S=class extends x{constructor(e,t,n){super(`beforeChange`,e,t,n)}},C=class extends x{constructor(e,t,n){super(`afterChange`,e,t,n)}};function w(e){return e instanceof Element?e.closest(`a[href]`):null}function T(e){let t=e.href.trim();return t.length>0?new URL(t):null}var E=class{stickyParams;_popStateListener;_linkNavListener;_listeners=[];addListener(e){this._listeners.push(e)}fireLocationChange(e){for(let t of this._listeners)t(e)}firePageLoad(){this.fireLocationChange(new C(`initial`,new URL(window.location.href)))}addStickyParams(e){return this.stickyParams?new d(e).add(this.stickyParams).toString():e}normalizeCurrentUrl(){if(typeof window>`u`||!this.stickyParams)return;let e=window.location.pathname+window.location.search+window.location.hash,t=this.addStickyParams(e);if(t!==e){let e=new URL(t,window.location.href);window.history.replaceState(window.history.state,``,e.href)}}navigate(e,t={}){if(t.stepsBack&&t.stepsBack>0){this.stepBack(t.stepsBack,t);return}if(t.basePath){let n=t.basePath;n.startsWith(`/`)||(n=`/${n}`),e=m(n,e)}e=this.addStickyParams(e),e=g(e),this._navigate(new URL(e,window.location.href),`navigate`,t)}stepBack(e,t={}){let n=window.history.state.historyChain||[],r=n.length>=e?new URL(n[n.length-e].href,window.location.href):new URL(window.location.origin,window.location.href);this._navigate(r,`popState`,t);let i={from:window.location.href,historyChain:n.slice(0,-e),data:t.state||void 0,title:t.title||document.title};window.history.replaceState(i,``,r.href),this.fireLocationChange(new C(`popState`,r,t.state))}_navigate(e,t,n){let r=new S(t,e,n.state);if(this.fireLocationChange(r),r._canceled)return;let i=window.history.state,a={title:n.title||document.title,href:window.location.pathname+window.location.search+window.location.hash},o=[];!n.replace&&i?.historyChain&&(o=[...i.historyChain]),n.replace||(o.push(a),o.length>10&&(o=o.slice(-10)));let s={from:window.location.href,historyChain:o,data:n.state||void 0,title:n.title||document.title};window.history[n.replace?`replaceState`:`pushState`](s,``,e.href),this.fireLocationChange(new C(t,e,n.state))}start(){if(typeof window>`u`)return;let e=e=>{let t,n=new URL(window.location.href),r;e.state?(t=`popState`,r=e.state.data):t=`linkClick`,this.fireLocationChange(new C(t,n,r))},t=e=>{if(e.defaultPrevented||e.metaKey||e.altKey||e.ctrlKey||e.shiftKey||e.button!==0)return;let t=w(e.target);if(!t)return;let n=t.getAttribute(`target`);if(t.hasAttribute(`download`)||t.href.startsWith(`blob:`)||n&&n.toLowerCase()!==`_self`)return;let r=T(t);if(r&&r.origin===window.location.origin){e.preventDefault();let t=new URL(this.addStickyParams(r.href)),n=e[b]||e.target[b];n&&(t.pathname=m(n,t.pathname)),this._navigate(t,`linkClick`,{})}};this._popStateListener=e,this._linkNavListener=t,window.addEventListener(`popstate`,e),document.body.addEventListener(`click`,this._linkNavListener)}stop(){this._popStateListener&&window.removeEventListener(`popstate`,this._popStateListener),this._linkNavListener&&document.body.removeEventListener(`click`,this._linkNavListener)}};function D({basePath:t,children:n}){let r=o(null);return i(()=>{if(r.current){let e=r.current,n=e=>{e.target.tagName.toLowerCase()===`a`&&(e[b]=t)};return e.addEventListener(`click`,n),()=>{e.removeEventListener(`click`,n)}}},[t]),e(`div`,{ref:r,className:`h-full w-full`,children:n})}var O=class{tree=new A;loadMapping(e){for(let[t,n]of Object.entries(e))this.addSegments(v(t),n)}addPath(e,t){this.addSegments(v(e),t)}addSegments(e,t){let n=this.tree;for(let r=0,i=e.length;r<i;r++){let a=e[r];if(a[0]===`:`){let t=n.wildcard;if(!t)t=new M(a),n.wildcard=t;else if(!(t instanceof M))throw Error(`Failed to index path segments: ${e.join(`/`)}. A wildcard ":" segment will overwrite an existing wildcard segment at path: ${`/${e.slice(0,r).join(`/`)}`}`);n=t}else if(a===`*`){if(n.wildcard)throw Error(`Failed to index path segments: ${e.join(`/`)}. A wildcard "*" segment already exists at path: ${`/${e.slice(0,r).join(`/`)}`}`);if(n.wildcard=new N(a,t),r<i-1)throw Error(`Failed to index path segments: ${e.join(`/`)}. A wildcard segment must be the last segment`);return}else{let e=n.children[a];e||(e=new j(a),n.children[a]=e),n=e}}if(n.value!==void 0)throw Error(`Failed to index path segments: ${e.join(`/`)}. A value already exists at path: ${`/${e.join(`/`)}`}`);n.value=t}match(e){let t=y(e);if(t.length===0)return null;let n={},r=this.tree;for(let e of t){let t=r.match(e,n);if(t)r=t;else return null}if(!r.value){if(r instanceof k&&r.wildcard instanceof N&&(r=r.wildcard.match(``,n),!r.value))throw Error("Wildcard segment node `*` must have a value");if(!r.value)return null}let i,a;return n._&&n._.length>0?(i=t.slice(0,-n._.length),a=n._):i=t,{params:n,matchedSegments:i,remainingSegments:a,value:r.value}}},k=class{name;value;children={};wildcard;constructor(e,t){this.name=e,this.value=t}match(e,t){let n=this.children[e];if(n)return n;if(this.wildcard){if(this.wildcard instanceof N)return this.wildcard.match(e,t);if(this.wildcard instanceof M)return t[this.wildcard.paramName]=e,this.wildcard;throw Error(`Unknown wildcard segment node type: ${this.wildcard.constructor.name}`)}return null}},A=class extends k{constructor(){super(`#root`)}},j=class extends k{},M=class extends k{paramName;constructor(e,t){super(e,t),this.paramName=e.substring(1)}},N=class{name;value;constructor(e,t){this.name=e,this.value=t}match(e,t){return t._?e&&t._.push(e):t._=e?[e]:[],this}},P=class{index;matcher=new O;constructor(e,t){this.index=t;for(let t of e)this.matcher.addPath(t.path,t)}match(e){let t=p(e)&&this.index;return this.matcher.match(t||e)}},F=class extends P{prompt;observer;navigator=new E;constructor(e,t){super(e),this.navigator.addListener(e=>{if(!(e.isCancelable&&this.prompt?.when&&!window.confirm(this.prompt.message))&&(this.observer&&this.observer(e),e.name===`afterChange`)){let n=this.match(_(e.location.pathname));n?.value?t({...n,state:e.state}):t(null)}})}getTopRouter(){return this}setStickyParams(e){this.navigator.stickyParams=e??void 0,this.navigator.normalizeCurrentUrl()}withObserver(e){return this.observer=e,this}start(){this.navigator.start(),this.navigator.firePageLoad()}stop(){this.navigator.stop()}navigate(e,t){this.navigator.navigate(e,t)}},I=class extends P{parent;basePath;constructor(e,t,n){super(n),this.parent=e,this.basePath=t}getTopRouter(){return this.parent instanceof F?this.parent:this.parent.getTopRouter()}navigate(e,t){let n=t?.isBasePathNested??!0,r;if(n){let n=t?.basePath;r=n?m(this.basePath,n):this.basePath,this.parent.navigate(e,{...t,basePath:r})}else this.parent.navigate(e,{...t,basePath:t?.basePath})}};const L=n(void 0);function R(){let e=r(L);if(!e)throw Error(`useRouter must be used within a RouterProvider`);return e}function z(){let{navigate:e}=R();return e}function B(){let{router:e}=R(),t=h();return e instanceof I?t?e.basePath===`/`?t:m(t,e.basePath):e.basePath:t}function V(e){let{params:t}=R();return e?t[e]:t}function H(){let{location:e}=R();return e}function U(e){i(()=>{let t=document.title;return document.title=e,()=>{document.title=t}},[e])}function W(e){let{router:t}=R(),n=t.getTopRouter();i(()=>(n.prompt=e,()=>{n.prompt=void 0}),[e,n]),i(()=>{if(e.when){let t=e.when,n=e=>{t&&(e.preventDefault(),e.returnValue=``)};return window.addEventListener(`beforeunload`,n),()=>{window.removeEventListener(`beforeunload`,n)}}},[e.when])}function G(e){return e.metaKey||e.altKey||e.ctrlKey||e.shiftKey||e.button!==0}function K({children:t,to:n,onClick:r,replace:i=!0}){let a=z();return e(`span`,{onClick:e=>{let t=e.target.closest(`a`),o=t?.getAttribute(`href`),s=n??o;t&&s&&(e.stopPropagation(),e.preventDefault(),a(s,{replace:i}),r?.(e))},children:t})}function q({children:t,href:n,className:r,target:i,topLevelNav:a,clearBreadcrumbs:o=!1,replace:s,skipStickyParams:c}){let{router:l}=R(),u=!n||n.startsWith(`#`),d=/^[a-z][a-z0-9+.-]*:/i.test(n)||n.startsWith(`//`),f=!u&&!d,p=!!i&&i!==`_self`,m=f?g(c?n:l.getTopRouter().navigator.addStickyParams(n)):n;return e(`a`,{href:m,className:r,target:i,onClick:e=>{e.defaultPrevented||!f||p||G(e)||(e.stopPropagation(),e.preventDefault(),(a?l.getTopRouter():l).navigate(n,{replace:s??o}))},children:t})}const J=new Map;async function Y(e){let t=await e.LazyComponent();if(!t?.default)throw Error(`Lazy module for ${e.path} does not have a default export`);return J.set(e.LazyComponent,t.default),t.default}function X(e){return Promise.allSettled(e.filter(e=>!!e.LazyComponent).map(e=>Y(e)))}function Z({spinner:t}){let n=R(),r=n.route;if(r.Component){let t=r.Component;return e(t,{...n.params})}if(r.LazyComponent)return e(ee,{route:r,spinner:t},r.path);throw Error(`Invalid route for ${r.path}. Either Component or LazyCOmponent must be specified.`)}function ee({route:t,spinner:n}){let[r,a]=s(()=>J.get(t.LazyComponent)??null),[o,c]=s();if(i(()=>{let e=!1;return Y(t).then(t=>{e||a(()=>t)},t=>{e||c(()=>t)}),()=>{e=!0}},[t]),o!==void 0)throw o;return r?e(r,{}):n||null}function te({basePath:t,isBasePathNested:n=!0,fixLinks:r=!1,children:i}){let a=R(),o=r?t=>e(D,{basePath:a.matchedRoutePath,children:t}):e=>e;return e(L.Provider,{value:{...a,navigate:(e,r)=>{if(r?.isBasePathNested===!1)return a.navigate(e,r);let i=r?.basePath?m(t,r.basePath):t;return a.navigate(e,{...r,basePath:i,isBasePathNested:n})}},children:o(i||e(Z,{}))})}function ne(){let e=R();return t(`div`,{children:[`Route not found for path `,e.matchedRoutePath]})}function Q(){return{params:{},matchedSegments:[],state:null,value:{path:`virtual:404`,Component:()=>e(ne,{})}}}function re({routes:t,index:n,children:r,fixLinks:i=!1}){let s=R(),c=o(s.matchedRoutePath),l=a(()=>{if(typeof window>`u`)return null;let e=new I(s.router,c.current,t);return e.index=n,e},[s.router,n,t]),u=a(()=>{if(l&&s.matchedRoutePath===l.basePath)return l.match(s.remainingPath||`/`)||Q()},[s.matchedRoutePath,s.remainingPath,l]),d=i?t=>e(D,{basePath:s.matchedRoutePath,children:t}):e=>e;return l&&u&&e(L.Provider,{value:{...s,router:l,route:u.value,params:u.params,matchedRoutePath:`/${u.matchedSegments.join(`/`)}`,remainingPath:u.remainingSegments?`/${u.remainingSegments.join(`/`)}`:void 0,navigate:(e,t)=>{if(l)return l.navigate(e,t)}},children:d(r||e(Z,{}))})}function $({to:e,replace:t=!0,state:n}){let r=z();return i(()=>{r(e,{replace:t,state:n})},[r,t,n,e]),null}function ie(t,n={}){return function(){return e($,{to:t,replace:n.replace,state:n.state})}}function ae({routes:t,index:n,onChange:r,children:i}){let[o,l]=s(void 0),u=a(()=>{if(typeof window>`u`)return null;let e=new F(t,t=>{t===null&&(t=Q()),l({location:window.location,route:t.value,params:t.params,state:t.state,router:e,matchedRoutePath:`/${t.matchedSegments.join(`/`)}`,remainingPath:t.remainingSegments?`/${t.remainingSegments.join(`/`)}`:void 0,navigate:(t,n)=>e.navigate(t,n)})}).withObserver(r);return e.index=n,e},[n,r,t]);return c(()=>(u?.start(),()=>{u?.stop()}),[u]),o&&e(L.Provider,{value:o,children:i||e(Z,{})})}export{C as AfterLocationChangeEvent,b as BASE_PATH,P as BaseRouter,S as BeforeLocationChangeEvent,D as FixLinks,E as HistoryNavigator,x as LocationChangeEvent,K as Nav,q as NavLink,te as NestedNavigationContext,I as NestedRouter,re as NestedRouterProvider,f as Path,O as PathMatcher,L as ReactRouterContext,$ as Redirect,Z as RouteComponent,F as Router,ae as RouterProvider,X as prefetchLazyRoutes,ie as redirectTo,H as useLocation,z as useNavigate,W as useNavigationPrompt,U as usePageTitle,V as useParams,B as useRouterBasePath,R as useRouterContext};
|
|
2
2
|
//# sourceMappingURL=vertesia-ui-router.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vertesia-ui-router.js","names":["_jsx","_jsx","_jsx","_jsx","_jsxs","_jsx","_jsx","_jsx","_jsx"],"sources":["router/PathWithParams.js","router/path.js","router/HistoryNavigator.js","router/FixLinks.js","router/PathMatcher.js","router/Router.js","router/Nav.js","router/RouteComponent.js","router/NestedNavigationContext.js","router/Route404.js","router/NestedRouterProvider.js","router/Redirect.js","router/RouterProvider.js"],"sourcesContent":["export class PathWithParams {\n path;\n params;\n hash;\n constructor(path) {\n let i = path.lastIndexOf('#');\n if (i > -1) {\n this.hash = path.substring(i);\n path = path.substring(0, i);\n }\n else {\n this.hash = '';\n }\n i = path.indexOf('?');\n if (i > -1) {\n this.path = path.substring(0, i);\n this.params = new URLSearchParams(path.substring(i + 1));\n }\n else {\n this.path = path;\n this.params = new URLSearchParams();\n }\n }\n add(params) {\n for (const [key, value] of Object.entries(params)) {\n this.params.set(key, value);\n }\n return this;\n }\n toString() {\n return `${this.path}?${this.params.toString()}${this.hash}`;\n }\n}\n//# sourceMappingURL=PathWithParams.js.map","export function isRootPath(path) {\n return path === '/' || path === '';\n}\nexport function joinPath(path1, path2) {\n if (path1.endsWith('/') && path2.startsWith('/')) {\n path2 = path1 + path2.substring(1);\n }\n else if (path1.endsWith('/')) {\n path2 = path1 + path2;\n }\n else if (path2.startsWith('/')) {\n path2 = path1 + path2;\n }\n else {\n path2 = `${path1}/${path2}`;\n }\n return path2;\n}\n/**\n * The mount base path from an explicit `<base href>` element, or `''` when there is none.\n *\n * Apps served under a deep gateway mount carry `<base href=\"/tenants/.../app/\">`, injected at serve\n * time. The router otherwise resolves absolute routes against the origin, which collapses the URL\n * off the mount (and breaks in-app links). This returns the base pathname without a trailing slash\n * so {@link withMountBasename} can keep navigation under the mount and {@link stripMountBasename}\n * can match routes app-relative. The Studio UI has no `<base>` element, so this returns `''` and\n * every caller becomes a no-op there.\n */\nexport function getMountBasename() {\n if (typeof document === 'undefined')\n return '';\n const base = document.querySelector('base[href]');\n if (!base)\n return '';\n try {\n return new URL(base.href, document.URL).pathname.replace(/\\/+$/, '');\n }\n catch {\n return '';\n }\n}\n/** Prepend the mount basename to an absolute app path (no-op when no mount, path is relative, or already prefixed). */\nexport function withMountBasename(path) {\n const base = getMountBasename();\n if (!base || !path.startsWith('/'))\n return path;\n if (path === base || path.startsWith(`${base}/`))\n return path;\n return joinPath(base, path);\n}\n/** Strip the mount basename from a browser path so route patterns match app-relative (no-op when no mount). */\nexport function stripMountBasename(path) {\n const base = getMountBasename();\n if (!base)\n return path;\n if (path === base)\n return '/';\n if (path.startsWith(`${base}/`))\n return path.slice(base.length) || '/';\n return path;\n}\nexport function getPathSegments(path) {\n if (path === '') {\n return [];\n }\n if (path === '/') {\n return [''];\n }\n let s = 0, e = path.length;\n if (path.startsWith('./')) {\n s = 2;\n }\n else if (path.startsWith('/')) {\n s = 1;\n }\n if (path.endsWith('/')) {\n e = path.length - 1;\n }\n return (s > 0 || e < path.length ? path.substring(s, e) : path).split('/');\n}\nexport function toSegments(path) {\n if (typeof path === 'string') {\n return getPathSegments(path);\n }\n else if (Array.isArray(path)) {\n return path;\n }\n else {\n throw new Error(`Unsupported path object: ${path}`);\n }\n}\n// export class Path {\n// static parse(path: string, abs = false) {\n// if (abs !== undefined) {\n// abs = path.startsWith('/');\n// }\n// return new Path(getPathSegments(path), abs);\n// }\n// constructor(public segments: string[], public isAbsolute = false) {\n// }\n// getParameters() {\n// const out = [];\n// for (const segment of this.segments) {\n// if (segment[0] === ':') {\n// out.push(segment.substring(1));\n// }\n// }\n// if (this.segments[this.segments.length - 1] === '*') {\n// out.push('_');\n// }\n// return out;\n// }\n// resolveParameters(path: string) {\n// const params: PathMatchParams = {};\n// const resolvedSegments = getPathSegments(path);\n// if (resolvedSegments.length < this.segments.length) {\n// return null;\n// }\n// const segments = this.segments;\n// for (let i = 0, l = segments.length; i < l; i++) {\n// const seg = segments[i];\n// if (seg[0] === ':') {\n// params[seg.substring(1)] = resolvedSegments[i];\n// }\n// }\n// if (resolvedSegments.length - this.segments.length) {\n// params._ = resolvedSegments.slice(this.segments.length);\n// }\n// return params;\n// }\n// match(path: string | string[] | Path) {\n// const segments = toSegments(path);\n// if (segments.length < this.segments.length) {\n// return false;\n// }\n// let params: PathMatchParams | undefined;\n// const mySegments = this.segments;\n// for (let i = 0, l = mySegments.length; i < l; i++) {\n// const segment = mySegments[i];\n// if (segment === ':') {\n// if (!params) params = {};\n// params[segment.substring(1)] = segment;\n// } else if (segment !== segments[i]) {\n// if (i === l - 1 && segment === '*') {\n// if (!params) params = {};\n// params._ = segments.slice(i);\n// return params;\n// }\n// return false;\n// }\n// }\n// return params ? params : false;\n// }\n// join(path: Path | string | string[]) {\n// const segments = toSegments(path);\n// return new Path(this.segments.concat(segments), this.isAbsolute);\n// }\n// getRelativePath(path: Path | string | string[], asAbsolute: boolean = false) {\n// const segments = toSegments(path);\n// const extraSegmentsCount = segments.length - this.segments.length;\n// if (extraSegmentsCount <= 0) {\n// return null;\n// }\n// return new Path(segments.slice(this.segments.length), asAbsolute);\n// }\n// prependSegments(segments: string[]) {\n// this.segments = segments.concat(this.segments);\n// }\n// appendSegments(segments: string[]) {\n// this.segments = this.segments.concat(segments);\n// }\n// toAbsolutePath() {\n// return '/' + this.segments.join('/');\n// }\n// toRelativePath() {\n// return this.segments.join('/');\n// }\n// toString() {\n// const path = this.segments.join('/');\n// return this.isAbsolute ? '/' + path : path;\n// }\n// }\n//# sourceMappingURL=path.js.map","import { PathWithParams } from './PathWithParams';\nimport { joinPath, withMountBasename } from './path';\nconst BASE_PATH = Symbol('BASE_PATH');\nexport { BASE_PATH };\nexport class LocationChangeEvent {\n name;\n type;\n location;\n state;\n _canceled = false;\n constructor(name, type, location, state) {\n this.name = name;\n this.type = type;\n this.location = location;\n this.state = state;\n }\n get isPageLoad() {\n return this.type === 'initial';\n }\n get isBackForward() {\n return this.type === 'popState';\n }\n get isLinkClick() {\n return this.type === 'linkClick';\n }\n get isNavigation() {\n return this.type === 'navigate';\n }\n get isCancelable() {\n return this.name === 'beforeChange';\n }\n cancel() {\n if (this.name === 'afterChange') {\n throw new Error('Cannot cancel afterChange event');\n }\n this._canceled = true;\n }\n}\nexport class BeforeLocationChangeEvent extends LocationChangeEvent {\n constructor(type, location, state) {\n super('beforeChange', type, location, state);\n }\n}\nexport class AfterLocationChangeEvent extends LocationChangeEvent {\n constructor(type, location, state) {\n super('afterChange', type, location, state);\n }\n}\nfunction getClosestAnchor(target) {\n if (!(target instanceof Element)) {\n return null;\n }\n return target.closest('a[href]');\n}\nfunction getElementHrefAsUrl(elem) {\n const href = elem.href.trim();\n return href.length > 0 ? new URL(href) : null;\n}\nexport class HistoryNavigator {\n // params to preserve in the query string when navigating\n stickyParams;\n _popStateListener;\n _linkNavListener;\n _listeners = [];\n addListener(listener) {\n this._listeners.push(listener);\n }\n fireLocationChange(event) {\n for (const listener of this._listeners) {\n listener(event);\n }\n }\n /**\n * Should be called when the page is first loaded.\n * It will fire a location change event with type `initial` and the current window.location as the location\n */\n firePageLoad() {\n this.fireLocationChange(new AfterLocationChangeEvent('initial', new URL(window.location.href)));\n }\n addStickyParams(path) {\n if (this.stickyParams) {\n return new PathWithParams(path).add(this.stickyParams).toString();\n }\n return path;\n }\n /**\n * Rewrite the current browser URL in place so that the sticky params are present, without\n * navigating. Used on load / when the params first become known so the address bar reflects\n * the active account/project before the first navigation.\n *\n * Idempotent and side-effect-light: only calls replaceState when the URL would actually\n * change, preserves the existing history state, and never fires a location-change event\n * (so there is no route re-match or component remount).\n */\n normalizeCurrentUrl() {\n if (typeof window === 'undefined' || !this.stickyParams) {\n return;\n }\n const current = window.location.pathname + window.location.search + window.location.hash;\n const normalized = this.addStickyParams(current);\n if (normalized !== current) {\n const to = new URL(normalized, window.location.href);\n window.history.replaceState(window.history.state, '', to.href);\n }\n }\n navigate(to, options = {}) {\n if (options.stepsBack && options.stepsBack > 0) {\n this.stepBack(options.stepsBack, options);\n return;\n }\n if (options.basePath) {\n let basePath = options.basePath;\n if (!basePath.startsWith('/')) {\n basePath = `/${basePath}`;\n }\n to = joinPath(basePath, to);\n }\n to = this.addStickyParams(to);\n // Keep absolute app routes under the served `<base href>` mount instead of resolving them\n // against the bare origin (which collapses the URL off the mount). No-op when origin-served.\n to = withMountBasename(to);\n this._navigate(new URL(to, window.location.href), 'navigate', options);\n }\n stepBack(steps, options = {}) {\n const historyChain = window.history.state.historyChain || [];\n const to = historyChain.length >= steps\n ? new URL(historyChain[historyChain.length - steps].href, window.location.href)\n : new URL(window.location.origin, window.location.href);\n this._navigate(to, 'popState', options);\n const stateToStore = {\n from: window.location.href,\n historyChain: historyChain.slice(0, -steps),\n data: options.state || undefined,\n title: options.title || document.title,\n };\n window.history.replaceState(stateToStore, '', to.href);\n this.fireLocationChange(new AfterLocationChangeEvent('popState', to, options.state));\n }\n _navigate(to, type, options) {\n const beforeEvent = new BeforeLocationChangeEvent(type, to, options.state);\n this.fireLocationChange(beforeEvent);\n if (beforeEvent._canceled) {\n return;\n }\n // Build navigation chain by preserving previous history\n const currentState = window.history.state;\n const currentTitle = options.title || document.title;\n // Create new history chain entry\n const newChainEntry = {\n title: currentTitle,\n href: window.location.pathname + window.location.search + window.location.hash,\n };\n // Build the history chain - clear if using replace\n let historyChain = [];\n if (!options.replace && currentState?.historyChain) {\n historyChain = [...currentState.historyChain];\n }\n // Only add to chain if not replacing\n if (!options.replace) {\n historyChain.push(newChainEntry);\n // Limit chain length to prevent memory issues (keep last 10 entries)\n if (historyChain.length > 10) {\n historyChain = historyChain.slice(-10);\n }\n }\n const stateToStore = {\n from: window.location.href,\n historyChain: historyChain,\n data: options.state || undefined,\n title: options.title || document.title,\n };\n window.history[options.replace ? 'replaceState' : 'pushState'](stateToStore, '', to.href);\n this.fireLocationChange(new AfterLocationChangeEvent(type, to, options.state));\n }\n start() {\n if (typeof window === 'undefined') {\n return;\n }\n const _popStateListener = (ev) => {\n let type;\n const to = new URL(window.location.href);\n let state;\n if (ev.state) {\n type = 'popState';\n state = ev.state.data;\n }\n else {\n type = 'linkClick';\n }\n this.fireLocationChange(new AfterLocationChangeEvent(type, to, state));\n };\n const _linkNavListener = (ev) => {\n // Let the browser handle modifier / non-primary clicks natively (open in new tab/window,\n // download). Intercepting them here would preventDefault those affordances.\n if (ev.defaultPrevented || ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey || ev.button !== 0) {\n return;\n }\n const anchor = getClosestAnchor(ev.target);\n if (!anchor) {\n return;\n }\n const anchorTarget = anchor.getAttribute('target');\n // Downloads, blob URLs, and explicitly targeted links require native browser navigation.\n if (anchor.hasAttribute('download') ||\n anchor.href.startsWith('blob:') ||\n (!!anchorTarget && anchorTarget.toLowerCase() !== '_self')) {\n return;\n }\n const url = getElementHrefAsUrl(anchor);\n if (url && url.origin === window.location.origin) {\n ev.preventDefault();\n const to = new URL(this.addStickyParams(url.href));\n const basePath = ev[BASE_PATH] ||\n ev.target[BASE_PATH];\n if (basePath) {\n to.pathname = joinPath(basePath, to.pathname);\n }\n this._navigate(to, 'linkClick', {});\n }\n };\n this._popStateListener = _popStateListener;\n this._linkNavListener = _linkNavListener;\n window.addEventListener('popstate', _popStateListener);\n document.body.addEventListener('click', this._linkNavListener);\n }\n stop() {\n if (this._popStateListener)\n window.removeEventListener('popstate', this._popStateListener);\n if (this._linkNavListener)\n document.body.removeEventListener('click', this._linkNavListener);\n }\n}\n//# sourceMappingURL=HistoryNavigator.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { useEffect, useRef } from 'react';\nimport { BASE_PATH } from './HistoryNavigator';\nexport function FixLinks({ basePath, children }) {\n const ref = useRef(null);\n useEffect(() => {\n if (ref.current) {\n const divElem = ref.current;\n const listener = (ev) => {\n const elem = ev.target;\n if (elem.tagName.toLowerCase() === 'a') {\n ev[BASE_PATH] = basePath;\n }\n };\n divElem.addEventListener('click', listener);\n return () => {\n divElem.removeEventListener('click', listener);\n };\n }\n }, [basePath]);\n return (_jsx(\"div\", { ref: ref, className: \"h-full w-full\", children: children }));\n}\n//# sourceMappingURL=FixLinks.js.map","import { getPathSegments, toSegments } from './path';\n/**\n * Path matcher which support :param and *wildcard segments.\n * The wildcard segment is only supported as the last segment\n */\nexport class PathMatcher {\n tree = new RootSegmentNode();\n loadMapping(mapping) {\n for (const [key, value] of Object.entries(mapping)) {\n this.addSegments(getPathSegments(key), value);\n }\n }\n addPath(path, value) {\n this.addSegments(getPathSegments(path), value);\n }\n addSegments(segments, value) {\n let node = this.tree;\n for (let i = 0, l = segments.length; i < l; i++) {\n const segment = segments[i];\n if (segment[0] === ':') {\n let childNode = node.wildcard;\n if (!childNode) {\n childNode = new VariableSegmentNode(segment);\n node.wildcard = childNode;\n }\n else if (!(childNode instanceof VariableSegmentNode)) {\n throw new Error(`Failed to index path segments: ${segments.join('/')}. A wildcard \":\" segment will overwrite an existing wildcard segment at path: ${`/${segments.slice(0, i).join('/')}`}`);\n }\n node = childNode;\n }\n else if (segment === '*') {\n if (node.wildcard) {\n throw new Error(`Failed to index path segments: ${segments.join('/')}. A wildcard \"*\" segment already exists at path: ${`/${segments.slice(0, i).join('/')}`}`);\n }\n node.wildcard = new WildcardSegmentNode(segment, value);\n if (i < l - 1) {\n throw new Error(`Failed to index path segments: ${segments.join('/')}. A wildcard segment must be the last segment`);\n }\n return;\n }\n else {\n let childNode = node.children[segment];\n if (!childNode) {\n childNode = new LiteralSegmentNode(segment);\n node.children[segment] = childNode;\n } // else // a literal segment already exists\n node = childNode;\n }\n }\n if (node.value !== undefined) {\n throw new Error(`Failed to index path segments: ${segments.join('/')}. A value already exists at path: ${`/${segments.join('/')}`}`);\n }\n node.value = value;\n }\n match(path) {\n const segments = toSegments(path);\n if (segments.length === 0) {\n return null;\n }\n const params = {};\n let node = this.tree;\n for (const segment of segments) {\n const match = node.match(segment, params);\n if (match) {\n node = match;\n }\n else {\n return null;\n }\n }\n if (!node.value) {\n // not a leaf node (partial match)\n if (node instanceof ParentSegmentNode) {\n if (node.wildcard instanceof WildcardSegmentNode) {\n node = node.wildcard.match('', params);\n if (!node.value) {\n throw new Error('Wildcard segment node `*` must have a value');\n }\n }\n }\n if (!node.value)\n return null; // not a leaf node, neither a trailing wildcard (partial match)\n }\n let matchedSegments, remainingSegments;\n if (params._ && params._.length > 0) {\n matchedSegments = segments.slice(0, -params._.length);\n remainingSegments = params._;\n }\n else {\n matchedSegments = segments;\n }\n return { params, matchedSegments, remainingSegments, value: node.value };\n }\n}\nclass ParentSegmentNode {\n name;\n value;\n children = {};\n wildcard;\n constructor(name, value) {\n this.name = name;\n this.value = value;\n }\n match(segment, params) {\n const node = this.children[segment];\n if (node) {\n return node;\n }\n else if (this.wildcard) {\n if (this.wildcard instanceof WildcardSegmentNode) {\n return this.wildcard.match(segment, params);\n }\n else if (this.wildcard instanceof VariableSegmentNode) {\n params[this.wildcard.paramName] = segment;\n return this.wildcard;\n }\n else {\n throw new Error(`Unknown wildcard segment node type: ${this.wildcard.constructor.name}`);\n }\n }\n else {\n return null;\n }\n }\n}\nclass RootSegmentNode extends ParentSegmentNode {\n constructor() {\n super('#root');\n }\n}\nclass LiteralSegmentNode extends ParentSegmentNode {\n}\nclass VariableSegmentNode extends ParentSegmentNode {\n paramName;\n constructor(name, value) {\n super(name, value);\n this.paramName = name.substring(1);\n }\n}\nclass WildcardSegmentNode {\n name;\n value;\n constructor(name, value) {\n this.name = name;\n this.value = value;\n }\n match(segment, params) {\n if (!params._) {\n params._ = segment ? [segment] : [];\n }\n else {\n if (segment)\n params._.push(segment);\n }\n return this;\n }\n}\n//# sourceMappingURL=PathMatcher.js.map","import { createContext, useContext, useEffect } from 'react';\nimport { HistoryNavigator } from './HistoryNavigator';\nimport { PathMatcher } from './PathMatcher';\nimport { getMountBasename, isRootPath, joinPath, stripMountBasename } from './path';\nexport class BaseRouter {\n // the path to use when navigating to the root of the router\n index;\n matcher = new PathMatcher();\n constructor(routes, index) {\n this.index = index;\n for (const route of routes) {\n this.matcher.addPath(route.path, route);\n }\n }\n match(path) {\n const useIndex = isRootPath(path) && this.index;\n return this.matcher.match(useIndex || path);\n }\n}\nexport class Router extends BaseRouter {\n prompt;\n observer;\n navigator = new HistoryNavigator();\n constructor(routes, updateState) {\n super(routes);\n this.navigator.addListener((event) => {\n if (event.isCancelable && this.prompt?.when) {\n if (!window.confirm(this.prompt.message))\n return;\n }\n if (this.observer) {\n this.observer(event);\n }\n // only process afterChange events\n if (event.name === 'afterChange') {\n // Match app-relative: strip the served `<base href>` mount prefix (no-op when origin-served).\n const match = this.match(stripMountBasename(event.location.pathname));\n if (match?.value) {\n updateState({\n ...match,\n state: event.state,\n });\n }\n else {\n updateState(null);\n }\n }\n });\n }\n getTopRouter() {\n return this;\n }\n /**\n * Subsequent navigations will preserve the given params in the query string.\n * Use null to clear the sticky params.\n * @param params\n */\n setStickyParams(params) {\n this.navigator.stickyParams = params != null ? params : undefined;\n // Reflect the params in the current URL immediately, so the address bar is correct on\n // load (not only after the first navigation).\n this.navigator.normalizeCurrentUrl();\n }\n withObserver(observer) {\n this.observer = observer;\n return this;\n }\n start() {\n this.navigator.start();\n // initialize with the current location\n this.navigator.firePageLoad();\n }\n stop() {\n this.navigator.stop();\n }\n navigate(path, options) {\n this.navigator.navigate(path, options);\n }\n}\nexport class NestedRouter extends BaseRouter {\n parent;\n basePath;\n constructor(parent, basePath, routes) {\n super(routes);\n this.parent = parent;\n this.basePath = basePath;\n }\n getTopRouter() {\n if (this.parent instanceof Router) {\n return this.parent;\n }\n else {\n return this.parent.getTopRouter();\n }\n }\n navigate(path, options) {\n // base path is nested by default in a NestedRouter unless explicitly set to false by caller\n const isBasePathNested = options?.isBasePathNested ?? true;\n let basePath;\n if (isBasePathNested) {\n const childBasePath = options?.basePath;\n // e.g. \"/store\" + \"/objects/123\" => \"/store/objects/123\"\n basePath = childBasePath ? joinPath(this.basePath, childBasePath) : this.basePath;\n this.parent.navigate(path, {\n ...options,\n basePath,\n });\n }\n else {\n // isBasePathNested === false: navigate to an absolute path without adding this router's basePath\n // Pass through to parent without adding our basePath prefix\n this.parent.navigate(path, {\n ...options,\n basePath: options?.basePath,\n });\n }\n }\n}\nconst ReactRouterContext = createContext(undefined);\nexport { ReactRouterContext };\nexport function useRouterContext() {\n const ctx = useContext(ReactRouterContext);\n if (!ctx) {\n throw new Error('useRouter must be used within a RouterProvider');\n }\n return ctx;\n}\nexport function useNavigate() {\n const { navigate } = useRouterContext();\n return navigate;\n}\nexport function useRouterBasePath() {\n const { router } = useRouterContext();\n // The base path apps prepend to build absolute in-app links (e.g. `${base}/items/${id}`). It MUST\n // include the served `<base href>` mount so links stay on the mount and match the full\n // `useLocation().pathname` (which carries the mount). `getMountBasename()` is '' for the\n // origin-served Studio UI and the mount (no trailing slash) for gateway-served apps.\n const mount = getMountBasename();\n if (router instanceof NestedRouter) {\n // NestedRouter.basePath is relative to the parent router, which sits at the mount, so it must\n // be mount-prefixed too. A bare nested base of '/' (an app whose NestedRouterProvider has no\n // basePath) would otherwise make `${base}/items` resolve to `//items` — a protocol-relative\n // URL that escapes the app. Origin-served Studio (mount '') keeps the raw nested basePath.\n if (!mount)\n return router.basePath;\n return router.basePath === '/' ? mount : joinPath(mount, router.basePath);\n }\n return mount;\n}\nexport function useParams(arg) {\n const { params } = useRouterContext();\n if (arg) {\n return params[arg];\n }\n else {\n return params;\n }\n}\nexport function useLocation() {\n const { location } = useRouterContext();\n return location;\n}\nexport function usePageTitle(title) {\n useEffect(() => {\n const prev = document.title;\n document.title = title;\n return () => {\n document.title = prev;\n };\n }, [title]);\n}\nexport function useNavigationPrompt(prompt) {\n const { router } = useRouterContext();\n const topRouter = router.getTopRouter();\n useEffect(() => {\n topRouter.prompt = prompt;\n return () => {\n topRouter.prompt = undefined;\n };\n }, [prompt, topRouter]);\n useEffect(() => {\n if (prompt.when) {\n const doBlock = prompt.when;\n const listener = (ev) => {\n if (doBlock) {\n ev.preventDefault();\n ev.returnValue = '';\n }\n };\n window.addEventListener('beforeunload', listener);\n return () => {\n window.removeEventListener('beforeunload', listener);\n };\n }\n }, [prompt.when]);\n}\n//# sourceMappingURL=Router.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { withMountBasename } from './path';\nimport { useNavigate, useRouterContext } from './Router';\n/**\n * True when the click carries a modifier the browser uses for alternate link behavior\n * (open in new tab/window, download) or is not a primary-button click. Such clicks must fall\n * through to the native anchor so the browser can handle them instead of the SPA router.\n */\nfunction isModifiedClick(ev) {\n return ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey || ev.button !== 0;\n}\nexport function Nav({ children, to, onClick, replace = true }) {\n const navigate = useNavigate();\n const _onClick = (ev) => {\n const link = ev.target.closest('a');\n const rawHref = link?.getAttribute('href');\n const target = to ?? rawHref;\n if (link && target) {\n ev.stopPropagation();\n ev.preventDefault();\n navigate(target, { replace });\n onClick?.(ev);\n }\n };\n return (_jsx(\"span\", { onClick: _onClick, children: children }));\n}\nexport function NavLink({ children, href, className, target, topLevelNav, clearBreadcrumbs = false, replace, skipStickyParams, }) {\n const { router } = useRouterContext();\n // Resolve internal URLs even when a target makes navigation native. New-tab links still need\n // the mount path and tenant sticky params, but must not be intercepted by the SPA router.\n const isAnchorOrEmpty = !href || href.startsWith('#');\n const isExternalUrl = /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith('//');\n const isInternalUrl = !isAnchorOrEmpty && !isExternalUrl;\n const isNativeNavigation = !!target && target !== '_self';\n // Keep the rendered href under the served `<base href>` mount (correct middle-click / hover /\n // open-in-new-tab); the onClick navigates via the router which applies the same rule. No-op when\n // origin-served (Studio UI). Click handler below passes the raw `href` — navigate() re-bases it.\n const resolvedHref = isInternalUrl\n ? withMountBasename(!skipStickyParams ? router.getTopRouter().navigator.addStickyParams(href) : href)\n : href;\n const _onClick = (ev) => {\n if (ev.defaultPrevented || !isInternalUrl || isNativeNavigation || isModifiedClick(ev)) {\n return;\n }\n ev.stopPropagation();\n ev.preventDefault();\n const actualRouter = topLevelNav ? router.getTopRouter() : router;\n actualRouter.navigate(href, { replace: replace ?? clearBreadcrumbs });\n };\n return (_jsx(\"a\", { href: resolvedHref, className: className, target: target, onClick: _onClick, children: children }));\n}\n//# sourceMappingURL=Nav.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { useEffect, useState } from 'react';\nimport { useRouterContext, } from './Router';\n// Resolved lazy modules, keyed by the route's LazyComponent function (a stable identity: route\n// tables are declared once). A revisited lazy route renders synchronously from this cache instead\n// of unmounting to the spinner for the async import() round-trip — without it, every navigation\n// between two lazy routes blanks the page for at least a frame, even when the module is loaded.\nconst resolvedComponents = new Map();\nfunction resolveLazyRoute(route) {\n return route.LazyComponent().then((module) => {\n if (!module.default) {\n throw new Error(`Lazy module for ${route.path} does not have a default export`);\n }\n resolvedComponents.set(route.LazyComponent, module.default);\n return module.default;\n });\n}\n/**\n * Start loading the given routes' lazy modules ahead of navigation (non-lazy routes are ignored).\n * Call it during idle time after first paint: the first navigation to a prefetched route then\n * renders synchronously instead of flashing the spinner while its chunk downloads. Failures are\n * swallowed — navigation retries the import and reports the error the normal way.\n */\nexport function prefetchLazyRoutes(routes) {\n return Promise.allSettled(routes\n .filter((route) => !!route.LazyComponent)\n .map((route) => resolveLazyRoute(route)));\n}\nexport function RouteComponent({ spinner }) {\n const ctx = useRouterContext();\n const route = ctx.route;\n if (route.Component) {\n const Component = route.Component;\n return _jsx(Component, { ...ctx.params });\n }\n else if (route.LazyComponent) {\n // Keyed per path so navigating between two lazy routes remounts: a reused instance would\n // keep rendering the previous route's component while the next import resolves.\n return _jsx(LazyRouteComponent, { route: route, spinner: spinner }, route.path);\n }\n else {\n throw new Error(`Invalid route for ${route.path}. Either Component or LazyCOmponent must be specified.`);\n }\n}\nfunction LazyRouteComponent({ route, spinner }) {\n const [Component, setComponent] = useState(() => resolvedComponents.get(route.LazyComponent) ?? null);\n useEffect(() => {\n // A slow import must not land after this instance moved on (unmount, or a route object\n // swap without a path change): the stale module would overwrite the current one.\n let stale = false;\n void resolveLazyRoute(route).then((component) => {\n if (!stale) {\n // we need to wrap the component type in an arrow function\n // otherwise the setState function will execute the function as a state update function\n setComponent(() => component);\n }\n });\n return () => {\n stale = true;\n };\n }, [route]);\n return Component ? _jsx(Component, {}) : spinner || null;\n}\n//# sourceMappingURL=RouteComponent.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { FixLinks } from './FixLinks';\nimport { joinPath } from './path';\nimport { RouteComponent } from './RouteComponent';\nimport { ReactRouterContext, useRouterContext } from './Router';\nexport function NestedNavigationContext({ basePath, isBasePathNested = true, fixLinks = false, children, }) {\n const ctx = useRouterContext();\n const wrapWithFixLinks = fixLinks\n ? (elem) => _jsx(FixLinks, { basePath: ctx.matchedRoutePath, children: elem })\n : (elem) => elem;\n return (_jsx(ReactRouterContext.Provider, { value: {\n ...ctx,\n navigate: (to, options) => {\n if (options?.isBasePathNested === false) {\n // Navigate to an absolute path without adding this context's basePath\n return ctx.navigate(to, options);\n }\n const actualBasePath = options?.basePath ? joinPath(basePath, options.basePath) : basePath;\n return ctx.navigate(to, { ...options, basePath: actualBasePath, isBasePathNested });\n },\n }, children: wrapWithFixLinks(children ? children : _jsx(RouteComponent, {})) }));\n}\n//# sourceMappingURL=NestedNavigationContext.js.map","import { jsx as _jsx, jsxs as _jsxs } from \"react/jsx-runtime\";\nimport { useRouterContext } from './Router';\nexport function Route404Component() {\n const ctx = useRouterContext();\n return _jsxs(\"div\", { children: [\"Route not found for path \", ctx.matchedRoutePath] });\n}\nexport function createRoute404() {\n return {\n params: {},\n matchedSegments: [],\n state: null,\n value: {\n path: 'virtual:404',\n Component: () => _jsx(Route404Component, {}),\n },\n };\n}\n//# sourceMappingURL=Route404.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { useMemo, useRef } from 'react';\nimport { FixLinks } from './FixLinks';\nimport { createRoute404 } from './Route404';\nimport { RouteComponent } from './RouteComponent';\nimport { NestedRouter, ReactRouterContext, useRouterContext } from './Router';\nexport function NestedRouterProvider({ routes, index, children, fixLinks = false }) {\n const ctx = useRouterContext();\n // Capture basePath once at mount. When the parent router switches to a different\n // top-level route (e.g. /store -> /studio), the lazy loader briefly keeps rendering\n // this (now-stale) module with a new ctx. Keeping basePath stable lets the\n // ctx.matchedRoutePath !== nestedRouter.basePath check below detect the mismatch and\n // skip rendering instead of running match() against the wrong route table.\n const basePathRef = useRef(ctx.matchedRoutePath);\n const nestedRouter = useMemo(() => {\n if (typeof window === 'undefined')\n return null;\n const nestedRouter = new NestedRouter(ctx.router, basePathRef.current, routes);\n nestedRouter.index = index;\n return nestedRouter;\n }, [ctx.router, index, routes]);\n const nestedRouteMatch = useMemo(() => {\n if (!nestedRouter) {\n return undefined;\n }\n if (ctx.matchedRoutePath !== nestedRouter.basePath) {\n // The change belongs to another router level and will be handled there.\n return undefined;\n }\n return nestedRouter.match(ctx.remainingPath || '/') || createRoute404();\n }, [ctx.matchedRoutePath, ctx.remainingPath, nestedRouter]);\n const wrapWithFixLinks = fixLinks\n ? (elem) => _jsx(FixLinks, { basePath: ctx.matchedRoutePath, children: elem })\n : (elem) => elem;\n return (nestedRouter &&\n nestedRouteMatch && (_jsx(ReactRouterContext.Provider, { value: {\n ...ctx,\n router: nestedRouter,\n route: nestedRouteMatch.value,\n params: nestedRouteMatch.params,\n matchedRoutePath: `/${nestedRouteMatch.matchedSegments.join('/')}`,\n remainingPath: nestedRouteMatch.remainingSegments\n ? `/${nestedRouteMatch.remainingSegments.join('/')}`\n : undefined,\n navigate: (to, options) => {\n if (nestedRouter) {\n return nestedRouter.navigate(to, options);\n }\n },\n }, children: wrapWithFixLinks(children ? children : _jsx(RouteComponent, {})) })));\n}\n//# sourceMappingURL=NestedRouterProvider.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { useEffect } from 'react';\nimport { useNavigate } from './Router';\nexport function Redirect({ to, replace = true, state }) {\n const navigate = useNavigate();\n useEffect(() => {\n navigate(to, { replace, state });\n }, [navigate, replace, state, to]);\n return null;\n}\n/**\n * Creates a route component that redirects to another route when rendered.\n * Useful in route tables, for example: `{ path: '/', Component: redirectTo('/content') }`.\n */\nexport function redirectTo(to, options = {}) {\n return function RedirectRoute() {\n return _jsx(Redirect, { to: to, replace: options.replace, state: options.state });\n };\n}\n//# sourceMappingURL=Redirect.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { useSafeLayoutEffect } from '@vertesia/ui/core';\nimport { useMemo, useState } from 'react';\nimport { createRoute404 } from './Route404';\nimport { RouteComponent } from './RouteComponent';\nimport { ReactRouterContext, Router } from './Router';\nexport function RouterProvider({ routes, index, onChange, children }) {\n const [state, setState] = useState(undefined);\n const router = useMemo(() => {\n if (typeof window === 'undefined')\n return null;\n const router = new Router(routes, (match) => {\n if (match === null) {\n match = createRoute404();\n }\n setState({\n location: window.location,\n route: match.value,\n params: match.params,\n state: match.state,\n router: router,\n matchedRoutePath: `/${match.matchedSegments.join('/')}`,\n remainingPath: match.remainingSegments ? `/${match.remainingSegments.join('/')}` : undefined,\n navigate: (to, options) => {\n return router.navigate(to, options);\n },\n });\n }).withObserver(onChange);\n router.index = index;\n return router;\n }, [index, onChange, routes]);\n useSafeLayoutEffect(() => {\n router?.start();\n return () => {\n router?.stop();\n };\n }, [router]);\n return (state && (_jsx(ReactRouterContext.Provider, { value: state, children: children ? children : _jsx(RouteComponent, {}) })));\n}\n//# sourceMappingURL=RouterProvider.js.map"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vertesia-ui-router.js","names":["_jsx","_jsx","_jsx","_jsx","_jsxs","_jsx","_jsx","_jsx","_jsx"],"sources":["router/PathWithParams.js","router/path.js","router/HistoryNavigator.js","router/FixLinks.js","router/PathMatcher.js","router/Router.js","router/Nav.js","router/RouteComponent.js","router/NestedNavigationContext.js","router/Route404.js","router/NestedRouterProvider.js","router/Redirect.js","router/RouterProvider.js"],"sourcesContent":["export class PathWithParams {\n path;\n params;\n hash;\n constructor(path) {\n let i = path.lastIndexOf('#');\n if (i > -1) {\n this.hash = path.substring(i);\n path = path.substring(0, i);\n }\n else {\n this.hash = '';\n }\n i = path.indexOf('?');\n if (i > -1) {\n this.path = path.substring(0, i);\n this.params = new URLSearchParams(path.substring(i + 1));\n }\n else {\n this.path = path;\n this.params = new URLSearchParams();\n }\n }\n add(params) {\n for (const [key, value] of Object.entries(params)) {\n this.params.set(key, value);\n }\n return this;\n }\n toString() {\n return `${this.path}?${this.params.toString()}${this.hash}`;\n }\n}\n//# sourceMappingURL=PathWithParams.js.map","export function isRootPath(path) {\n return path === '/' || path === '';\n}\nexport function joinPath(path1, path2) {\n if (path1.endsWith('/') && path2.startsWith('/')) {\n path2 = path1 + path2.substring(1);\n }\n else if (path1.endsWith('/')) {\n path2 = path1 + path2;\n }\n else if (path2.startsWith('/')) {\n path2 = path1 + path2;\n }\n else {\n path2 = `${path1}/${path2}`;\n }\n return path2;\n}\n/**\n * The mount base path from an explicit `<base href>` element, or `''` when there is none.\n *\n * Apps served under a deep gateway mount carry `<base href=\"/tenants/.../app/\">`, injected at serve\n * time. The router otherwise resolves absolute routes against the origin, which collapses the URL\n * off the mount (and breaks in-app links). This returns the base pathname without a trailing slash\n * so {@link withMountBasename} can keep navigation under the mount and {@link stripMountBasename}\n * can match routes app-relative. The Studio UI has no `<base>` element, so this returns `''` and\n * every caller becomes a no-op there.\n */\nexport function getMountBasename() {\n if (typeof document === 'undefined')\n return '';\n const base = document.querySelector('base[href]');\n if (!base)\n return '';\n try {\n return new URL(base.href, document.URL).pathname.replace(/\\/+$/, '');\n }\n catch {\n return '';\n }\n}\n/** Prepend the mount basename to an absolute app path (no-op when no mount, path is relative, or already prefixed). */\nexport function withMountBasename(path) {\n const base = getMountBasename();\n if (!base || !path.startsWith('/'))\n return path;\n if (path === base || path.startsWith(`${base}/`))\n return path;\n return joinPath(base, path);\n}\n/** Strip the mount basename from a browser path so route patterns match app-relative (no-op when no mount). */\nexport function stripMountBasename(path) {\n const base = getMountBasename();\n if (!base)\n return path;\n if (path === base)\n return '/';\n if (path.startsWith(`${base}/`))\n return path.slice(base.length) || '/';\n return path;\n}\nexport function getPathSegments(path) {\n if (path === '') {\n return [];\n }\n if (path === '/') {\n return [''];\n }\n let s = 0, e = path.length;\n if (path.startsWith('./')) {\n s = 2;\n }\n else if (path.startsWith('/')) {\n s = 1;\n }\n if (path.endsWith('/')) {\n e = path.length - 1;\n }\n return (s > 0 || e < path.length ? path.substring(s, e) : path).split('/');\n}\nexport function toSegments(path) {\n if (typeof path === 'string') {\n return getPathSegments(path);\n }\n else if (Array.isArray(path)) {\n return path;\n }\n else {\n throw new Error(`Unsupported path object: ${path}`);\n }\n}\n// export class Path {\n// static parse(path: string, abs = false) {\n// if (abs !== undefined) {\n// abs = path.startsWith('/');\n// }\n// return new Path(getPathSegments(path), abs);\n// }\n// constructor(public segments: string[], public isAbsolute = false) {\n// }\n// getParameters() {\n// const out = [];\n// for (const segment of this.segments) {\n// if (segment[0] === ':') {\n// out.push(segment.substring(1));\n// }\n// }\n// if (this.segments[this.segments.length - 1] === '*') {\n// out.push('_');\n// }\n// return out;\n// }\n// resolveParameters(path: string) {\n// const params: PathMatchParams = {};\n// const resolvedSegments = getPathSegments(path);\n// if (resolvedSegments.length < this.segments.length) {\n// return null;\n// }\n// const segments = this.segments;\n// for (let i = 0, l = segments.length; i < l; i++) {\n// const seg = segments[i];\n// if (seg[0] === ':') {\n// params[seg.substring(1)] = resolvedSegments[i];\n// }\n// }\n// if (resolvedSegments.length - this.segments.length) {\n// params._ = resolvedSegments.slice(this.segments.length);\n// }\n// return params;\n// }\n// match(path: string | string[] | Path) {\n// const segments = toSegments(path);\n// if (segments.length < this.segments.length) {\n// return false;\n// }\n// let params: PathMatchParams | undefined;\n// const mySegments = this.segments;\n// for (let i = 0, l = mySegments.length; i < l; i++) {\n// const segment = mySegments[i];\n// if (segment === ':') {\n// if (!params) params = {};\n// params[segment.substring(1)] = segment;\n// } else if (segment !== segments[i]) {\n// if (i === l - 1 && segment === '*') {\n// if (!params) params = {};\n// params._ = segments.slice(i);\n// return params;\n// }\n// return false;\n// }\n// }\n// return params ? params : false;\n// }\n// join(path: Path | string | string[]) {\n// const segments = toSegments(path);\n// return new Path(this.segments.concat(segments), this.isAbsolute);\n// }\n// getRelativePath(path: Path | string | string[], asAbsolute: boolean = false) {\n// const segments = toSegments(path);\n// const extraSegmentsCount = segments.length - this.segments.length;\n// if (extraSegmentsCount <= 0) {\n// return null;\n// }\n// return new Path(segments.slice(this.segments.length), asAbsolute);\n// }\n// prependSegments(segments: string[]) {\n// this.segments = segments.concat(this.segments);\n// }\n// appendSegments(segments: string[]) {\n// this.segments = this.segments.concat(segments);\n// }\n// toAbsolutePath() {\n// return '/' + this.segments.join('/');\n// }\n// toRelativePath() {\n// return this.segments.join('/');\n// }\n// toString() {\n// const path = this.segments.join('/');\n// return this.isAbsolute ? '/' + path : path;\n// }\n// }\n//# sourceMappingURL=path.js.map","import { PathWithParams } from './PathWithParams';\nimport { joinPath, withMountBasename } from './path';\nconst BASE_PATH = Symbol('BASE_PATH');\nexport { BASE_PATH };\nexport class LocationChangeEvent {\n name;\n type;\n location;\n state;\n _canceled = false;\n constructor(name, type, location, state) {\n this.name = name;\n this.type = type;\n this.location = location;\n this.state = state;\n }\n get isPageLoad() {\n return this.type === 'initial';\n }\n get isBackForward() {\n return this.type === 'popState';\n }\n get isLinkClick() {\n return this.type === 'linkClick';\n }\n get isNavigation() {\n return this.type === 'navigate';\n }\n get isCancelable() {\n return this.name === 'beforeChange';\n }\n cancel() {\n if (this.name === 'afterChange') {\n throw new Error('Cannot cancel afterChange event');\n }\n this._canceled = true;\n }\n}\nexport class BeforeLocationChangeEvent extends LocationChangeEvent {\n constructor(type, location, state) {\n super('beforeChange', type, location, state);\n }\n}\nexport class AfterLocationChangeEvent extends LocationChangeEvent {\n constructor(type, location, state) {\n super('afterChange', type, location, state);\n }\n}\nfunction getClosestAnchor(target) {\n if (!(target instanceof Element)) {\n return null;\n }\n return target.closest('a[href]');\n}\nfunction getElementHrefAsUrl(elem) {\n const href = elem.href.trim();\n return href.length > 0 ? new URL(href) : null;\n}\nexport class HistoryNavigator {\n // params to preserve in the query string when navigating\n stickyParams;\n _popStateListener;\n _linkNavListener;\n _listeners = [];\n addListener(listener) {\n this._listeners.push(listener);\n }\n fireLocationChange(event) {\n for (const listener of this._listeners) {\n listener(event);\n }\n }\n /**\n * Should be called when the page is first loaded.\n * It will fire a location change event with type `initial` and the current window.location as the location\n */\n firePageLoad() {\n this.fireLocationChange(new AfterLocationChangeEvent('initial', new URL(window.location.href)));\n }\n addStickyParams(path) {\n if (this.stickyParams) {\n return new PathWithParams(path).add(this.stickyParams).toString();\n }\n return path;\n }\n /**\n * Rewrite the current browser URL in place so that the sticky params are present, without\n * navigating. Used on load / when the params first become known so the address bar reflects\n * the active account/project before the first navigation.\n *\n * Idempotent and side-effect-light: only calls replaceState when the URL would actually\n * change, preserves the existing history state, and never fires a location-change event\n * (so there is no route re-match or component remount).\n */\n normalizeCurrentUrl() {\n if (typeof window === 'undefined' || !this.stickyParams) {\n return;\n }\n const current = window.location.pathname + window.location.search + window.location.hash;\n const normalized = this.addStickyParams(current);\n if (normalized !== current) {\n const to = new URL(normalized, window.location.href);\n window.history.replaceState(window.history.state, '', to.href);\n }\n }\n navigate(to, options = {}) {\n if (options.stepsBack && options.stepsBack > 0) {\n this.stepBack(options.stepsBack, options);\n return;\n }\n if (options.basePath) {\n let basePath = options.basePath;\n if (!basePath.startsWith('/')) {\n basePath = `/${basePath}`;\n }\n to = joinPath(basePath, to);\n }\n to = this.addStickyParams(to);\n // Keep absolute app routes under the served `<base href>` mount instead of resolving them\n // against the bare origin (which collapses the URL off the mount). No-op when origin-served.\n to = withMountBasename(to);\n this._navigate(new URL(to, window.location.href), 'navigate', options);\n }\n stepBack(steps, options = {}) {\n const historyChain = window.history.state.historyChain || [];\n const to = historyChain.length >= steps\n ? new URL(historyChain[historyChain.length - steps].href, window.location.href)\n : new URL(window.location.origin, window.location.href);\n this._navigate(to, 'popState', options);\n const stateToStore = {\n from: window.location.href,\n historyChain: historyChain.slice(0, -steps),\n data: options.state || undefined,\n title: options.title || document.title,\n };\n window.history.replaceState(stateToStore, '', to.href);\n this.fireLocationChange(new AfterLocationChangeEvent('popState', to, options.state));\n }\n _navigate(to, type, options) {\n const beforeEvent = new BeforeLocationChangeEvent(type, to, options.state);\n this.fireLocationChange(beforeEvent);\n if (beforeEvent._canceled) {\n return;\n }\n // Build navigation chain by preserving previous history\n const currentState = window.history.state;\n const currentTitle = options.title || document.title;\n // Create new history chain entry\n const newChainEntry = {\n title: currentTitle,\n href: window.location.pathname + window.location.search + window.location.hash,\n };\n // Build the history chain - clear if using replace\n let historyChain = [];\n if (!options.replace && currentState?.historyChain) {\n historyChain = [...currentState.historyChain];\n }\n // Only add to chain if not replacing\n if (!options.replace) {\n historyChain.push(newChainEntry);\n // Limit chain length to prevent memory issues (keep last 10 entries)\n if (historyChain.length > 10) {\n historyChain = historyChain.slice(-10);\n }\n }\n const stateToStore = {\n from: window.location.href,\n historyChain: historyChain,\n data: options.state || undefined,\n title: options.title || document.title,\n };\n window.history[options.replace ? 'replaceState' : 'pushState'](stateToStore, '', to.href);\n this.fireLocationChange(new AfterLocationChangeEvent(type, to, options.state));\n }\n start() {\n if (typeof window === 'undefined') {\n return;\n }\n const _popStateListener = (ev) => {\n let type;\n const to = new URL(window.location.href);\n let state;\n if (ev.state) {\n type = 'popState';\n state = ev.state.data;\n }\n else {\n type = 'linkClick';\n }\n this.fireLocationChange(new AfterLocationChangeEvent(type, to, state));\n };\n const _linkNavListener = (ev) => {\n // Let the browser handle modifier / non-primary clicks natively (open in new tab/window,\n // download). Intercepting them here would preventDefault those affordances.\n if (ev.defaultPrevented || ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey || ev.button !== 0) {\n return;\n }\n const anchor = getClosestAnchor(ev.target);\n if (!anchor) {\n return;\n }\n const anchorTarget = anchor.getAttribute('target');\n // Downloads, blob URLs, and explicitly targeted links require native browser navigation.\n if (anchor.hasAttribute('download') ||\n anchor.href.startsWith('blob:') ||\n (!!anchorTarget && anchorTarget.toLowerCase() !== '_self')) {\n return;\n }\n const url = getElementHrefAsUrl(anchor);\n if (url && url.origin === window.location.origin) {\n ev.preventDefault();\n const to = new URL(this.addStickyParams(url.href));\n const basePath = ev[BASE_PATH] ||\n ev.target[BASE_PATH];\n if (basePath) {\n to.pathname = joinPath(basePath, to.pathname);\n }\n this._navigate(to, 'linkClick', {});\n }\n };\n this._popStateListener = _popStateListener;\n this._linkNavListener = _linkNavListener;\n window.addEventListener('popstate', _popStateListener);\n document.body.addEventListener('click', this._linkNavListener);\n }\n stop() {\n if (this._popStateListener)\n window.removeEventListener('popstate', this._popStateListener);\n if (this._linkNavListener)\n document.body.removeEventListener('click', this._linkNavListener);\n }\n}\n//# sourceMappingURL=HistoryNavigator.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { useEffect, useRef } from 'react';\nimport { BASE_PATH } from './HistoryNavigator';\nexport function FixLinks({ basePath, children }) {\n const ref = useRef(null);\n useEffect(() => {\n if (ref.current) {\n const divElem = ref.current;\n const listener = (ev) => {\n const elem = ev.target;\n if (elem.tagName.toLowerCase() === 'a') {\n ev[BASE_PATH] = basePath;\n }\n };\n divElem.addEventListener('click', listener);\n return () => {\n divElem.removeEventListener('click', listener);\n };\n }\n }, [basePath]);\n return (_jsx(\"div\", { ref: ref, className: \"h-full w-full\", children: children }));\n}\n//# sourceMappingURL=FixLinks.js.map","import { getPathSegments, toSegments } from './path';\n/**\n * Path matcher which support :param and *wildcard segments.\n * The wildcard segment is only supported as the last segment\n */\nexport class PathMatcher {\n tree = new RootSegmentNode();\n loadMapping(mapping) {\n for (const [key, value] of Object.entries(mapping)) {\n this.addSegments(getPathSegments(key), value);\n }\n }\n addPath(path, value) {\n this.addSegments(getPathSegments(path), value);\n }\n addSegments(segments, value) {\n let node = this.tree;\n for (let i = 0, l = segments.length; i < l; i++) {\n const segment = segments[i];\n if (segment[0] === ':') {\n let childNode = node.wildcard;\n if (!childNode) {\n childNode = new VariableSegmentNode(segment);\n node.wildcard = childNode;\n }\n else if (!(childNode instanceof VariableSegmentNode)) {\n throw new Error(`Failed to index path segments: ${segments.join('/')}. A wildcard \":\" segment will overwrite an existing wildcard segment at path: ${`/${segments.slice(0, i).join('/')}`}`);\n }\n node = childNode;\n }\n else if (segment === '*') {\n if (node.wildcard) {\n throw new Error(`Failed to index path segments: ${segments.join('/')}. A wildcard \"*\" segment already exists at path: ${`/${segments.slice(0, i).join('/')}`}`);\n }\n node.wildcard = new WildcardSegmentNode(segment, value);\n if (i < l - 1) {\n throw new Error(`Failed to index path segments: ${segments.join('/')}. A wildcard segment must be the last segment`);\n }\n return;\n }\n else {\n let childNode = node.children[segment];\n if (!childNode) {\n childNode = new LiteralSegmentNode(segment);\n node.children[segment] = childNode;\n } // else // a literal segment already exists\n node = childNode;\n }\n }\n if (node.value !== undefined) {\n throw new Error(`Failed to index path segments: ${segments.join('/')}. A value already exists at path: ${`/${segments.join('/')}`}`);\n }\n node.value = value;\n }\n match(path) {\n const segments = toSegments(path);\n if (segments.length === 0) {\n return null;\n }\n const params = {};\n let node = this.tree;\n for (const segment of segments) {\n const match = node.match(segment, params);\n if (match) {\n node = match;\n }\n else {\n return null;\n }\n }\n if (!node.value) {\n // not a leaf node (partial match)\n if (node instanceof ParentSegmentNode) {\n if (node.wildcard instanceof WildcardSegmentNode) {\n node = node.wildcard.match('', params);\n if (!node.value) {\n throw new Error('Wildcard segment node `*` must have a value');\n }\n }\n }\n if (!node.value)\n return null; // not a leaf node, neither a trailing wildcard (partial match)\n }\n let matchedSegments, remainingSegments;\n if (params._ && params._.length > 0) {\n matchedSegments = segments.slice(0, -params._.length);\n remainingSegments = params._;\n }\n else {\n matchedSegments = segments;\n }\n return { params, matchedSegments, remainingSegments, value: node.value };\n }\n}\nclass ParentSegmentNode {\n name;\n value;\n children = {};\n wildcard;\n constructor(name, value) {\n this.name = name;\n this.value = value;\n }\n match(segment, params) {\n const node = this.children[segment];\n if (node) {\n return node;\n }\n else if (this.wildcard) {\n if (this.wildcard instanceof WildcardSegmentNode) {\n return this.wildcard.match(segment, params);\n }\n else if (this.wildcard instanceof VariableSegmentNode) {\n params[this.wildcard.paramName] = segment;\n return this.wildcard;\n }\n else {\n throw new Error(`Unknown wildcard segment node type: ${this.wildcard.constructor.name}`);\n }\n }\n else {\n return null;\n }\n }\n}\nclass RootSegmentNode extends ParentSegmentNode {\n constructor() {\n super('#root');\n }\n}\nclass LiteralSegmentNode extends ParentSegmentNode {\n}\nclass VariableSegmentNode extends ParentSegmentNode {\n paramName;\n constructor(name, value) {\n super(name, value);\n this.paramName = name.substring(1);\n }\n}\nclass WildcardSegmentNode {\n name;\n value;\n constructor(name, value) {\n this.name = name;\n this.value = value;\n }\n match(segment, params) {\n if (!params._) {\n params._ = segment ? [segment] : [];\n }\n else {\n if (segment)\n params._.push(segment);\n }\n return this;\n }\n}\n//# sourceMappingURL=PathMatcher.js.map","import { createContext, useContext, useEffect } from 'react';\nimport { HistoryNavigator } from './HistoryNavigator';\nimport { PathMatcher } from './PathMatcher';\nimport { getMountBasename, isRootPath, joinPath, stripMountBasename } from './path';\nexport class BaseRouter {\n // the path to use when navigating to the root of the router\n index;\n matcher = new PathMatcher();\n constructor(routes, index) {\n this.index = index;\n for (const route of routes) {\n this.matcher.addPath(route.path, route);\n }\n }\n match(path) {\n const useIndex = isRootPath(path) && this.index;\n return this.matcher.match(useIndex || path);\n }\n}\nexport class Router extends BaseRouter {\n prompt;\n observer;\n navigator = new HistoryNavigator();\n constructor(routes, updateState) {\n super(routes);\n this.navigator.addListener((event) => {\n if (event.isCancelable && this.prompt?.when) {\n if (!window.confirm(this.prompt.message))\n return;\n }\n if (this.observer) {\n this.observer(event);\n }\n // only process afterChange events\n if (event.name === 'afterChange') {\n // Match app-relative: strip the served `<base href>` mount prefix (no-op when origin-served).\n const match = this.match(stripMountBasename(event.location.pathname));\n if (match?.value) {\n updateState({\n ...match,\n state: event.state,\n });\n }\n else {\n updateState(null);\n }\n }\n });\n }\n getTopRouter() {\n return this;\n }\n /**\n * Subsequent navigations will preserve the given params in the query string.\n * Use null to clear the sticky params.\n * @param params\n */\n setStickyParams(params) {\n this.navigator.stickyParams = params != null ? params : undefined;\n // Reflect the params in the current URL immediately, so the address bar is correct on\n // load (not only after the first navigation).\n this.navigator.normalizeCurrentUrl();\n }\n withObserver(observer) {\n this.observer = observer;\n return this;\n }\n start() {\n this.navigator.start();\n // initialize with the current location\n this.navigator.firePageLoad();\n }\n stop() {\n this.navigator.stop();\n }\n navigate(path, options) {\n this.navigator.navigate(path, options);\n }\n}\nexport class NestedRouter extends BaseRouter {\n parent;\n basePath;\n constructor(parent, basePath, routes) {\n super(routes);\n this.parent = parent;\n this.basePath = basePath;\n }\n getTopRouter() {\n if (this.parent instanceof Router) {\n return this.parent;\n }\n else {\n return this.parent.getTopRouter();\n }\n }\n navigate(path, options) {\n // base path is nested by default in a NestedRouter unless explicitly set to false by caller\n const isBasePathNested = options?.isBasePathNested ?? true;\n let basePath;\n if (isBasePathNested) {\n const childBasePath = options?.basePath;\n // e.g. \"/store\" + \"/objects/123\" => \"/store/objects/123\"\n basePath = childBasePath ? joinPath(this.basePath, childBasePath) : this.basePath;\n this.parent.navigate(path, {\n ...options,\n basePath,\n });\n }\n else {\n // isBasePathNested === false: navigate to an absolute path without adding this router's basePath\n // Pass through to parent without adding our basePath prefix\n this.parent.navigate(path, {\n ...options,\n basePath: options?.basePath,\n });\n }\n }\n}\nconst ReactRouterContext = createContext(undefined);\nexport { ReactRouterContext };\nexport function useRouterContext() {\n const ctx = useContext(ReactRouterContext);\n if (!ctx) {\n throw new Error('useRouter must be used within a RouterProvider');\n }\n return ctx;\n}\nexport function useNavigate() {\n const { navigate } = useRouterContext();\n return navigate;\n}\nexport function useRouterBasePath() {\n const { router } = useRouterContext();\n // The base path apps prepend to build absolute in-app links (e.g. `${base}/items/${id}`). It MUST\n // include the served `<base href>` mount so links stay on the mount and match the full\n // `useLocation().pathname` (which carries the mount). `getMountBasename()` is '' for the\n // origin-served Studio UI and the mount (no trailing slash) for gateway-served apps.\n const mount = getMountBasename();\n if (router instanceof NestedRouter) {\n // NestedRouter.basePath is relative to the parent router, which sits at the mount, so it must\n // be mount-prefixed too. A bare nested base of '/' (an app whose NestedRouterProvider has no\n // basePath) would otherwise make `${base}/items` resolve to `//items` — a protocol-relative\n // URL that escapes the app. Origin-served Studio (mount '') keeps the raw nested basePath.\n if (!mount)\n return router.basePath;\n return router.basePath === '/' ? mount : joinPath(mount, router.basePath);\n }\n return mount;\n}\nexport function useParams(arg) {\n const { params } = useRouterContext();\n if (arg) {\n return params[arg];\n }\n else {\n return params;\n }\n}\nexport function useLocation() {\n const { location } = useRouterContext();\n return location;\n}\nexport function usePageTitle(title) {\n useEffect(() => {\n const prev = document.title;\n document.title = title;\n return () => {\n document.title = prev;\n };\n }, [title]);\n}\nexport function useNavigationPrompt(prompt) {\n const { router } = useRouterContext();\n const topRouter = router.getTopRouter();\n useEffect(() => {\n topRouter.prompt = prompt;\n return () => {\n topRouter.prompt = undefined;\n };\n }, [prompt, topRouter]);\n useEffect(() => {\n if (prompt.when) {\n const doBlock = prompt.when;\n const listener = (ev) => {\n if (doBlock) {\n ev.preventDefault();\n ev.returnValue = '';\n }\n };\n window.addEventListener('beforeunload', listener);\n return () => {\n window.removeEventListener('beforeunload', listener);\n };\n }\n }, [prompt.when]);\n}\n//# sourceMappingURL=Router.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { withMountBasename } from './path';\nimport { useNavigate, useRouterContext } from './Router';\n/**\n * True when the click carries a modifier the browser uses for alternate link behavior\n * (open in new tab/window, download) or is not a primary-button click. Such clicks must fall\n * through to the native anchor so the browser can handle them instead of the SPA router.\n */\nfunction isModifiedClick(ev) {\n return ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey || ev.button !== 0;\n}\nexport function Nav({ children, to, onClick, replace = true }) {\n const navigate = useNavigate();\n const _onClick = (ev) => {\n const link = ev.target.closest('a');\n const rawHref = link?.getAttribute('href');\n const target = to ?? rawHref;\n if (link && target) {\n ev.stopPropagation();\n ev.preventDefault();\n navigate(target, { replace });\n onClick?.(ev);\n }\n };\n return (_jsx(\"span\", { onClick: _onClick, children: children }));\n}\nexport function NavLink({ children, href, className, target, topLevelNav, clearBreadcrumbs = false, replace, skipStickyParams, }) {\n const { router } = useRouterContext();\n // Resolve internal URLs even when a target makes navigation native. New-tab links still need\n // the mount path and tenant sticky params, but must not be intercepted by the SPA router.\n const isAnchorOrEmpty = !href || href.startsWith('#');\n const isExternalUrl = /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith('//');\n const isInternalUrl = !isAnchorOrEmpty && !isExternalUrl;\n const isNativeNavigation = !!target && target !== '_self';\n // Keep the rendered href under the served `<base href>` mount (correct middle-click / hover /\n // open-in-new-tab); the onClick navigates via the router which applies the same rule. No-op when\n // origin-served (Studio UI). Click handler below passes the raw `href` — navigate() re-bases it.\n const resolvedHref = isInternalUrl\n ? withMountBasename(!skipStickyParams ? router.getTopRouter().navigator.addStickyParams(href) : href)\n : href;\n const _onClick = (ev) => {\n if (ev.defaultPrevented || !isInternalUrl || isNativeNavigation || isModifiedClick(ev)) {\n return;\n }\n ev.stopPropagation();\n ev.preventDefault();\n const actualRouter = topLevelNav ? router.getTopRouter() : router;\n actualRouter.navigate(href, { replace: replace ?? clearBreadcrumbs });\n };\n return (_jsx(\"a\", { href: resolvedHref, className: className, target: target, onClick: _onClick, children: children }));\n}\n//# sourceMappingURL=Nav.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { useEffect, useState } from 'react';\nimport { useRouterContext, } from './Router';\n// Resolved lazy modules, keyed by the route's LazyComponent function (a stable identity: route\n// tables are declared once). A revisited lazy route renders synchronously from this cache instead\n// of unmounting to the spinner for the async import() round-trip — without it, every navigation\n// between two lazy routes blanks the page for at least a frame, even when the module is loaded.\nconst resolvedComponents = new Map();\nasync function resolveLazyRoute(route) {\n const module = await route.LazyComponent();\n if (!module?.default) {\n throw new Error(`Lazy module for ${route.path} does not have a default export`);\n }\n resolvedComponents.set(route.LazyComponent, module.default);\n return module.default;\n}\n/**\n * Start loading the given routes' lazy modules ahead of navigation (non-lazy routes are ignored).\n * Call it during idle time after first paint: the first navigation to a prefetched route then\n * renders synchronously instead of flashing the spinner while its chunk downloads. Failures are\n * swallowed — navigation retries the import and reports the error the normal way.\n */\nexport function prefetchLazyRoutes(routes) {\n return Promise.allSettled(routes\n .filter((route) => !!route.LazyComponent)\n .map((route) => resolveLazyRoute(route)));\n}\nexport function RouteComponent({ spinner }) {\n const ctx = useRouterContext();\n const route = ctx.route;\n if (route.Component) {\n const Component = route.Component;\n return _jsx(Component, { ...ctx.params });\n }\n else if (route.LazyComponent) {\n // Keyed per path so navigating between two lazy routes remounts: a reused instance would\n // keep rendering the previous route's component while the next import resolves.\n return _jsx(LazyRouteComponent, { route: route, spinner: spinner }, route.path);\n }\n else {\n throw new Error(`Invalid route for ${route.path}. Either Component or LazyCOmponent must be specified.`);\n }\n}\nfunction LazyRouteComponent({ route, spinner }) {\n const [Component, setComponent] = useState(() => resolvedComponents.get(route.LazyComponent) ?? null);\n const [error, setError] = useState();\n useEffect(() => {\n // A slow import must not land after this instance moved on (unmount, or a route object\n // swap without a path change): the stale module would overwrite the current one.\n let stale = false;\n void resolveLazyRoute(route).then((component) => {\n if (!stale)\n setComponent(() => component);\n }, (error) => {\n // Render failures through the surrounding error boundary, including import failures.\n if (!stale)\n setError(() => error);\n });\n return () => {\n stale = true;\n };\n }, [route]);\n if (error !== undefined)\n throw error;\n return Component ? _jsx(Component, {}) : spinner || null;\n}\n//# sourceMappingURL=RouteComponent.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { FixLinks } from './FixLinks';\nimport { joinPath } from './path';\nimport { RouteComponent } from './RouteComponent';\nimport { ReactRouterContext, useRouterContext } from './Router';\nexport function NestedNavigationContext({ basePath, isBasePathNested = true, fixLinks = false, children, }) {\n const ctx = useRouterContext();\n const wrapWithFixLinks = fixLinks\n ? (elem) => _jsx(FixLinks, { basePath: ctx.matchedRoutePath, children: elem })\n : (elem) => elem;\n return (_jsx(ReactRouterContext.Provider, { value: {\n ...ctx,\n navigate: (to, options) => {\n if (options?.isBasePathNested === false) {\n // Navigate to an absolute path without adding this context's basePath\n return ctx.navigate(to, options);\n }\n const actualBasePath = options?.basePath ? joinPath(basePath, options.basePath) : basePath;\n return ctx.navigate(to, { ...options, basePath: actualBasePath, isBasePathNested });\n },\n }, children: wrapWithFixLinks(children ? children : _jsx(RouteComponent, {})) }));\n}\n//# sourceMappingURL=NestedNavigationContext.js.map","import { jsx as _jsx, jsxs as _jsxs } from \"react/jsx-runtime\";\nimport { useRouterContext } from './Router';\nexport function Route404Component() {\n const ctx = useRouterContext();\n return _jsxs(\"div\", { children: [\"Route not found for path \", ctx.matchedRoutePath] });\n}\nexport function createRoute404() {\n return {\n params: {},\n matchedSegments: [],\n state: null,\n value: {\n path: 'virtual:404',\n Component: () => _jsx(Route404Component, {}),\n },\n };\n}\n//# sourceMappingURL=Route404.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { useMemo, useRef } from 'react';\nimport { FixLinks } from './FixLinks';\nimport { createRoute404 } from './Route404';\nimport { RouteComponent } from './RouteComponent';\nimport { NestedRouter, ReactRouterContext, useRouterContext } from './Router';\nexport function NestedRouterProvider({ routes, index, children, fixLinks = false }) {\n const ctx = useRouterContext();\n // Capture basePath once at mount. When the parent router switches to a different\n // top-level route (e.g. /store -> /studio), the lazy loader briefly keeps rendering\n // this (now-stale) module with a new ctx. Keeping basePath stable lets the\n // ctx.matchedRoutePath !== nestedRouter.basePath check below detect the mismatch and\n // skip rendering instead of running match() against the wrong route table.\n const basePathRef = useRef(ctx.matchedRoutePath);\n const nestedRouter = useMemo(() => {\n if (typeof window === 'undefined')\n return null;\n const nestedRouter = new NestedRouter(ctx.router, basePathRef.current, routes);\n nestedRouter.index = index;\n return nestedRouter;\n }, [ctx.router, index, routes]);\n const nestedRouteMatch = useMemo(() => {\n if (!nestedRouter) {\n return undefined;\n }\n if (ctx.matchedRoutePath !== nestedRouter.basePath) {\n // The change belongs to another router level and will be handled there.\n return undefined;\n }\n return nestedRouter.match(ctx.remainingPath || '/') || createRoute404();\n }, [ctx.matchedRoutePath, ctx.remainingPath, nestedRouter]);\n const wrapWithFixLinks = fixLinks\n ? (elem) => _jsx(FixLinks, { basePath: ctx.matchedRoutePath, children: elem })\n : (elem) => elem;\n return (nestedRouter &&\n nestedRouteMatch && (_jsx(ReactRouterContext.Provider, { value: {\n ...ctx,\n router: nestedRouter,\n route: nestedRouteMatch.value,\n params: nestedRouteMatch.params,\n matchedRoutePath: `/${nestedRouteMatch.matchedSegments.join('/')}`,\n remainingPath: nestedRouteMatch.remainingSegments\n ? `/${nestedRouteMatch.remainingSegments.join('/')}`\n : undefined,\n navigate: (to, options) => {\n if (nestedRouter) {\n return nestedRouter.navigate(to, options);\n }\n },\n }, children: wrapWithFixLinks(children ? children : _jsx(RouteComponent, {})) })));\n}\n//# sourceMappingURL=NestedRouterProvider.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { useEffect } from 'react';\nimport { useNavigate } from './Router';\nexport function Redirect({ to, replace = true, state }) {\n const navigate = useNavigate();\n useEffect(() => {\n navigate(to, { replace, state });\n }, [navigate, replace, state, to]);\n return null;\n}\n/**\n * Creates a route component that redirects to another route when rendered.\n * Useful in route tables, for example: `{ path: '/', Component: redirectTo('/content') }`.\n */\nexport function redirectTo(to, options = {}) {\n return function RedirectRoute() {\n return _jsx(Redirect, { to: to, replace: options.replace, state: options.state });\n };\n}\n//# sourceMappingURL=Redirect.js.map","import { jsx as _jsx } from \"react/jsx-runtime\";\nimport { useSafeLayoutEffect } from '@vertesia/ui/core';\nimport { useMemo, useState } from 'react';\nimport { createRoute404 } from './Route404';\nimport { RouteComponent } from './RouteComponent';\nimport { ReactRouterContext, Router } from './Router';\nexport function RouterProvider({ routes, index, onChange, children }) {\n const [state, setState] = useState(undefined);\n const router = useMemo(() => {\n if (typeof window === 'undefined')\n return null;\n const router = new Router(routes, (match) => {\n if (match === null) {\n match = createRoute404();\n }\n setState({\n location: window.location,\n route: match.value,\n params: match.params,\n state: match.state,\n router: router,\n matchedRoutePath: `/${match.matchedSegments.join('/')}`,\n remainingPath: match.remainingSegments ? `/${match.remainingSegments.join('/')}` : undefined,\n navigate: (to, options) => {\n return router.navigate(to, options);\n },\n });\n }).withObserver(onChange);\n router.index = index;\n return router;\n }, [index, onChange, routes]);\n useSafeLayoutEffect(() => {\n router?.start();\n return () => {\n router?.stop();\n };\n }, [router]);\n return (state && (_jsx(ReactRouterContext.Provider, { value: state, children: children ? children : _jsx(RouteComponent, {}) })));\n}\n//# sourceMappingURL=RouterProvider.js.map"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{NO_ACCESSIBLE_ACCOUNT_ERROR_CODE as e,REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE as t,RESTRICTED_ENVIRONMENT_ERROR_CODE as n}from"@vertesia/common";import{Env as r}from"@vertesia/ui/env";import{jwtDecode as i}from"jwt-decode";import{getAnalytics as a,logEvent as o}from"firebase/analytics";import{initializeApp as s}from"firebase/app";import{getAuth as c,onAuthStateChanged as l}from"firebase/auth";import{createContext as u,useCallback as d,useContext as f,useEffect as p,useMemo as m,useRef as h,useState as g}from"react";import{NAMESPACE as ee,i18nInstance as _}from"@vertesia/ui/i18n";import{VertesiaClient as te}from"@vertesia/client";import{jsx as v}from"react/jsx-runtime";const y=`composableai.lastSelectedAccountId`,b=`composableai.lastSelectedProjectId`;let x=null,S=null,C=null;function w(){if(!x)try{if(!r.firebase)throw Error(`Firebase configuration is not available in the environment`);x=s(r.firebase)}catch(e){throw console.error(`Failed to initialize Firebase app:`,e),Error(`Firebase initialization failed - environment may not be properly initialized`,{cause:e})}return x}function ne(){return S||=a(w()),S}function T(){return C||=c(w()),C}async function re(e){if(!e){console.log(`No tenant name or email specified, skipping tenant setup`);return}if(!r.firebase){console.log(`Firebase configuration is not available in the environment`);return}try{e&&console.log(`Resolving tenant ID from email: ${e}`);let t=3,n=250;for(;t>0;)try{let t=await fetch(`/api/resolve-tenant`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({tenantEmail:e}),signal:AbortSignal.timeout(5e3)});if(!t)throw Error(`No response received from tenant API`);if(!t.ok){try{let e=await t.json();console.error(`Failed to resolve tenant ID:`,e.error)}catch{console.error(`Failed to resolve tenant ID: HTTP ${t.status}`)}if(t.status===404){console.warn(`Tenant not found for ${e}`);return}throw Error(`HTTP error ${t.status}`)}let n=await t.json();if(n?.firebaseTenantId){let e=T();return e.tenantId=n.firebaseTenantId,r.firebase.providerType=n.provider??`oidc`,console.log(`Tenant ID set to ${e.tenantId}`),n}console.error(`Invalid response format, missing tenantId for ${e}`);return}catch(e){if(t>1)console.warn(`Tenant resolution failed, retrying in ${n}ms...`,e),await new Promise(e=>setTimeout(e,n)),n*=2,t--;else throw e}}catch(e){console.error(`Error setting Firebase tenant:`,e instanceof Error?e.message:`Unknown error`)}}async function E(e){let t=T().currentUser;return t?t.getIdToken(e).then(n=>(r.logger.debug(`Got Firebase token`,{vertesia:{user_email:t.email,user_name:t.displayName,user_id:t.uid,refresh:e}}),n)).catch(n=>(r.logger.error(`Failed to get Firebase token`,{vertesia:{user_email:t.email,user_name:t.displayName,user_id:t.uid,refresh:e,error:n}}),console.error(`Failed to get access token`,n),null)):(r.logger.warn(`No user found`),Promise.resolve(null))}let D,O;function ie(e,t){if(!e)return;let n=`${b}-${e}`;if(t){let r=localStorage.getItem(n)===t;r&&localStorage.removeItem(n),r&&localStorage.getItem(`composableai.lastSelectedAccountId`)===e&&localStorage.removeItem(y);return}localStorage.getItem(`composableai.lastSelectedAccountId`)===e&&(localStorage.removeItem(y),localStorage.removeItem(n))}function ae(e){try{let t=i(e);return typeof t.email!=`string`||!t.email?void 0:{email:t.email,name:typeof t.name==`string`&&t.name?t.name:void 0}}catch{return}}function k(e){let t=e.searchParams.get(`a`)??void 0,n=e.searchParams.get(`p`)??void 0,r=t??(n===void 0?localStorage.getItem(`composableai.lastSelectedAccountId`)??void 0:void 0);return{accountId:r,projectId:n??localStorage.getItem(`composableai.lastSelectedProjectId-${r}`)??void 0}}function A(e){return e?.replace(/\/+$/,``)}function j(e){return i(e)}function M(e){if(!e)return!1;try{return A(j(e).iss)===A(r.endpoints.sts)}catch{return!1}}function N(e,t,n){let r=j(e);return!(!r.exp||r.exp<=Date.now()/1e3+300||!(r.permissions?.length||r.account_roles?.length||r.project_roles?.length||r.apps?.length)||t&&r.account?.id!==t||n&&r.project?.id!==n)}async function P(a,o,s,c,l=0){r.logger.debug(`Getting/refreshing composable token`,{vertesia:{account_id:o,project_id:s,retry_count:l}});let u=await a();if(!u)throw Error(`No id token found`);let d=r.endpoints.sts;r.logger.debug(`Using STS for token generation`,{vertesia:{account_id:o,project_id:s,sts_url:d}});try{let f=new URL(`${d}/token/issue`),p={type:`user`,account_id:o,project_id:s,expires_at:c?Math.floor(Date.now()/1e3)+c:void 0},m=await fetch(f,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${u}`},body:JSON.stringify(p)}).catch(e=>{throw console.error(`Failed to call STS endpoint`,e),r.logger.error(`Failed to call STS endpoint`,{vertesia:{account_id:o,project_id:s,error:e}}),new V(`The authentication service could not be reached`,d,{cause:e})});if(u&&m?.status===404){r.logger.debug(`404: User not found - calling ensure-user endpoint`,{vertesia:{account_id:o,project_id:s,status:m?.status}});let e=await fetch(`${r.endpoints.studio}/auth/ensure-user`,{method:`POST`,headers:{Authorization:`Bearer ${u}`,"Content-Type":`application/json`}});if(e.status===412){r.logger.debug(`412: No invite found - signup required`,{vertesia:{account_id:o,project_id:s}});let e=i(u);throw e?.email?new I(`User not found - signup required`,e.email):(r.logger.error(`No email found in id token`),Error(`No email found in id token`))}if(e.status===403)throw r.logger.warn(`403: Customer-domain user requires an invite to join`,{vertesia:{account_id:o,project_id:s}}),Error(`Customer-domain user requires an invite to join`);if(!e.ok)throw console.error(`Failed to ensure user exists`,e.status),r.logger.error(`Failed to ensure user exists`,{vertesia:{account_id:o,project_id:s,status:e.status}}),Error(`Failed to ensure user exists`);return r.logger.debug(`User ensured - retrying token generation`,{vertesia:{account_id:o,project_id:s}}),P(a,o,s,c,l)}if(u&&m?.status===412){r.logger.info(`412: auth succeeded but user doesn't exist - signup required`,{vertesia:{account_id:o,project_id:s,status:m?.status}});let e=i(u);throw e?.email?(r.logger.info(`User not found`,{vertesia:{account_id:o,project_id:s,email:e.email}}),new I(`User not found`,e.email)):(r.logger.error(`No email found in id token`),Error(`No email found in id token`))}if(m.status===401)throw r.logger.warn(`STS rejected the identity credential`,{vertesia:{account_id:o,project_id:s,status:m.status}}),new B(`Your sign-in credentials could not be verified`,d,{status:m.status});if(m.status===403){let i=await m.json().catch(()=>void 0),a=ae(u);throw i?.errorCode===n?(r.logger.warn(`403: User lacks early-access for this restricted environment`,{vertesia:{account_id:o,project_id:s}}),new H(i.message??`You don't have sufficient permission to visit unstable environments`)):i?.errorCode===e?(r.logger.warn(`STS identity has no accessible account`,{vertesia:{error_code:i.errorCode,status:m.status}}),new z(d,i.message,a)):!i?.errorCode||i.errorCode===t?(r.logger.warn(`STS rejected the requested account or project`,{vertesia:{account_id:o,project_id:s,status:m.status,error_code:i?.errorCode??t}}),new R(d,o,s,i?.errorCode?i.message:void 0,a)):(r.logger.error(`STS returned an unrecognized forbidden response`,{vertesia:{account_id:o,project_id:s,status:m.status,error_code:i.errorCode}}),new V(`The authentication service rejected the request`,d,{status:m.status,errorCode:i.errorCode,cause:i}))}if(!m.ok){let e=await m.text();throw console.error(`STS token generation failed:`,m.status,e),r.logger.error(`STS token generation failed`,{vertesia:{status:m.status,error:e,account_id:o,project_id:s}}),new V(`The authentication service could not complete sign-in`,d,{status:m.status,cause:e})}let{token:h}=await m.json();return r.logger.debug(`Successfully got token from STS`),h}catch(e){throw e instanceof I||e instanceof L||e instanceof H||e instanceof Error&&e.message===`Customer-domain user requires an invite to join`?e:(console.error(`Failed to get composable token from STS`,e),r.logger.error(`Failed to get composable token from STS`,{vertesia:{account_id:o,project_id:s,error:e}}),new V(`The authentication service could not complete sign-in`,d,{cause:e}))}}async function oe(e,t,n){return P(E,e,t,n)}async function se(e,t,n,r){return P(()=>Promise.resolve(e),t,n,r)}function ce(){return D}async function F(e,t,n,i=!1,a=!1){let o=e??(t===void 0?localStorage.getItem(`composableai.lastSelectedAccountId`)??void 0:void 0),s=t??localStorage.getItem(`composableai.lastSelectedProjectId-${o}`)??void 0,c=r.isLocalDev?r.devAuthToken:void 0,l=c??n??D,u=(!o||O?.account?.id===o)&&(!s||O?.project?.id===s);if(!i&&D&&O&&u&&O.exp>Date.now()/1e3+300)return{rawToken:D,token:O,error:!1};if(!i&&M(l)&&N(l,o,s)){if(D=l,O=j(D),!O.exp)throw Error(`Invalid composable token`);return{rawToken:D,token:O,error:!1}}try{if(!c&&!a&&T().currentUser)D=await oe(o,s);else if(c)c&&(D=c);else{let e=await r.authTokenProvider?.()??n??D;D=e&&M(e)&&N(e,o,s)?e:e?await P(()=>Promise.resolve(e),o,s):void 0}}catch(e){throw(e instanceof R||e instanceof z||e instanceof B)&&(D=void 0,O=void 0,e instanceof R&&ie(o,s)),e}if(!D)throw r.logger.error(`Cannot acquire a composable token`,{vertesia:{account_id:o,project_id:s}}),Error(`Cannot acquire a composable token`);if(O=j(D),!O?.exp||!D)throw console.error(`Invalid composable token`,O),r.logger.error(`Invalid composable token`,{vertesia:{account_id:o,project_id:s}}),Error(`Invalid composable token`);return{rawToken:D,token:O,error:!1}}var I=class extends Error{email;constructor(e,t){super(e),this.name=`UserNotFoundError`,this.email=t}},L=class extends Error{stsURL;status;errorCode;constructor(e,t,n){super(e,n),this.name=`STSError`,this.stsURL=t,this.status=n?.status,this.errorCode=n?.errorCode}},R=class extends L{accountId;projectId;identity;requestedScope;selectorSource;recoveryUrl;constructor(e,n,r,i,a){super(i??`The requested account or project is not available.`,e,{status:403,errorCode:t}),this.accountId=n,this.projectId=r,this.identity=a,this.name=`RequestedScopeUnavailableError`,this.requestedScope=r?`project`:`account`}};const le=R;var z=class extends L{identity;constructor(t,n,r){super(n??`No accessible account is available for this user.`,t,{status:403,errorCode:e}),this.identity=r,this.name=`NoAccessibleAccountError`}},B=class extends L{constructor(e,t,n){super(e,t,n),this.name=`CredentialError`}},V=class extends L{constructor(e,t,n){super(e,t,n),this.name=`AuthenticationServiceError`}};function ue(e){return e instanceof R}function de(e){return e instanceof z}function fe(e){return e instanceof B}function pe(e){return e instanceof V}var H=class extends Error{constructor(e){super(e),this.name=`RestrictedEnvironmentError`}};const U=`auth_state`,W=`auth_state_expiry`;function G(){let e=crypto.randomUUID();return sessionStorage.setItem(U,e),sessionStorage.setItem(W,String(Date.now()+3e5)),e}function me(e){if(!e)return`Missing state`;let t=sessionStorage.getItem(U),n=parseInt(sessionStorage.getItem(W)||`0`,10);if(t!==e)return`State mismatched (${t} !== ${e})`;if(Date.now()>n)return`State expired`}function he(){sessionStorage.removeItem(U),sessionStorage.removeItem(W)}function ge(e){return window.AUTH_MODE===`firebase`}function K(e){return!ge()}function _e({centralAuthUrl:e,stsEndpoint:t,returnUrl:n,state:r,accountId:i,projectId:a}){let o=new URL(n);a&&o.searchParams.set(`p`,a),i&&o.searchParams.set(`a`,i);let s=new URL(e);return s.searchParams.set(`sts`,t),s.searchParams.set(`redirect_uri`,o.toString()),s.searchParams.set(`state`,r),s}function q(){let e=new URL(document.baseURI),t=new URL(window.location.href),n=t.pathname.startsWith(e.pathname)?t:e;return n.hash=``,n}function J(){let e=new URL(document.baseURI);return e.hash=``,e.search=``,e}function ve(e,t){return`vtsauth=${e}; Path=/; Max-Age=${t}; SameSite=Lax${window.location.protocol===`https:`?`; Secure`:``}`}function ye(){document.cookie=ve(`1`,300)}function be(){document.cookie=ve(``,0)}function Y(e={}){let t=_e({centralAuthUrl:`https://internal-auth.vertesia.app/`,stsEndpoint:r.endpoints.sts??`https://sts.vertesia.io`,returnUrl:q(),state:G(),...e});ye(),location.replace(t.toString())}function xe(){if(!K()||r.authTokenProvider||r.isLocalDev&&r.devAuthToken)return!1;let e=new URLSearchParams(window.location.hash.substring(1));return!(e.get(`token`)&&e.get(`state`))}function X(){return{generateState:d(G,[]),verifyState:d(me,[]),clearState:d(he,[])}}var Z=class e{isLoading=!0;client;authError;authToken;setSession;lastSelectedAccount;lastSelectedProject;onboardingComplete;constructor(e,t){this.client=e||new te({serverUrl:r.endpoints.studio,storeUrl:r.endpoints.zeno,tokenServerUrl:r.endpoints.sts}),t&&(this.setSession=t),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(e=>{let t=e?.rawToken;if(!t)throw Error(`No token available`);return this.authToken=i(t),t})}async refreshAuthToken(){let e=(await F(void 0,void 0,void 0,!0))?.rawToken;if(!e)throw Error(`No token available`);return this.authToken=i(e),this.setSession?.(this.clone()),this.authToken}signOut(){this.logout()}getAccount(){return this.authToken?.account}async login(e,t={}){return this.authError=void 0,this.isLoading=!1,this.client.withAuthCallback(()=>this.authCallback),this.authToken=i(e),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(y,this.authToken.account.id),localStorage.setItem(`${b}-${this.authToken.account.id}`,this.authToken.project?.id??``),r.onLogin?.(this.authToken),(t.loadOnboardingStatus??!0)&&await this.fetchOnboardingStatus(),Promise.resolve()}isLoggedIn(){return!!this.authToken}logout(){if(console.log(`Logging out`),K()){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);let e=new URL(`https://internal-auth.vertesia.app/`),t=q();e.pathname=`/logout`,e.searchParams.set(`redirect_uri`,t.toString()),location.replace(e.toString())}else{console.log(`Using Firebase logout`);let e=!!this.authToken;this.authToken&&T().signOut(),this.authError=void 0,this.isLoading=!1,this.authToken=void 0,this.setSession=void 0,this.client.withAuthCallback(void 0),e&&location.replace(J().toString())}}async switchAccount(e){localStorage.setItem(y,e),this&&(this.account&&this.project?localStorage.setItem(`${b}-${this.account.id}`,this.project.id):this.account&&localStorage.removeItem(`${b}-${this.account.id}`));let t=J();t.searchParams.set(`a`,e),window.location.replace(t.toString())}async switchProject(e){this.account&&localStorage.setItem(`${b}-${this.account.id}`,e);let t=J();this.account?.id&&t.searchParams.set(`a`,this.account.id),t.searchParams.set(`p`,e),window.location.replace(t.toString())}async fetchAccounts(){return this.client.accounts.list().then(e=>{if(!this.authToken)throw Error(`No token available`);this.authToken.accounts=e,this.setSession?.(this.clone())}).catch(e=>{throw console.error(`Failed to fetch accounts`,e),e})}async fetchProjects(e){return this.client.projects.list([e]).then(t=>{if(!this.authToken)throw Error(`No token available`);this.authToken={...this.authToken,accounts:this.authToken.accounts?.map(n=>n.id===e?{...n,projects:t.filter(t=>t.account===e)}:n)},this.setSession?.(this.clone())}).catch(e=>{throw console.error(`Failed to fetch projects`,e),e})}async fetchOnboardingStatus(){if(this.onboardingComplete)return console.log(`Onboarding already completed`),!1;let e=this.onboardingComplete;try{let t=await this.client.account.onboardingProgress();return this.onboardingComplete=Object.values(t).every(e=>e===!0),e!==this.onboardingComplete&&this.setSession?.(this.clone()),e===!1&&this.onboardingComplete}catch(t){console.error(`Error fetching onboarding status:`,t),this.onboardingComplete=!1,e!==this.onboardingComplete&&this.setSession?.(this.clone())}return!1}clone(){let t=new e(this.client);return t.isLoading=this.isLoading,t.authError=this.authError,t.authToken=this.authToken,t.setSession=this.setSession,t.lastSelectedAccount=this.lastSelectedAccount,t.switchAccount=this.switchAccount,t.onboardingComplete=this.onboardingComplete,t}};const Q=u(void 0);function Se(){let e=f(Q);if(!e)throw Error(`useUserSession must be used within a UserSessionProvider`);return e}function Ce(){let{user:e}=Se(),[t,n]=g(null),[r,i]=g(!0),[a,o]=g(null);return p(()=>{(async()=>{if(!e?.email){n(null),i(!1);return}try{let t=await fetch(`/api/resolve-tenant`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({tenantEmail:e.email})});if(t.ok){let e=await t.json();n(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 n(null)}catch(e){console.error(`Error loading current tenant:`,e),o(_.getFixedT(null,ee)(`errors.failedToLoadTenantConfig`)),n(null)}finally{i(!1)}})()},[e?.email]),{currentTenant:t,isLoading:r,error:a}}function we(e){let[,t]=e.split(`.`);if(!t)throw Error(`Invalid Vertesia auth token`);let n=t.replace(/-/g,`+`).replace(/_/g,`/`).padEnd(Math.ceil(t.length/4)*4,`=`);return JSON.parse(atob(n))}function Te({children:e,token:t}){let n=m(()=>{let e=new Z;e.isLoading=!1;try{e.authToken=we(t),e.client.withAuthCallback(()=>Promise.resolve(`Bearer ${t}`)),localStorage.setItem(y,e.authToken.account.id),localStorage.setItem(`${b}-${e.authToken.account.id}`,e.authToken.project?.id??``)}catch(t){e.authError=t instanceof Error?t:Error(String(t))}return e.clone()},[t]);return v(Q.Provider,{value:n,children:e})}function Ee(){let e=new URL(window.location.href);e.hash=``,window.history.replaceState(window.history.state,``,`${e.pathname}${e.search}`)}function $(e,t,n=!1){let r=new URL(e),i=r.searchParams.get(`a`)??void 0,a=r.searchParams.get(`p`)??void 0;return i&&a&&i===t.accountId&&a===t.projectId?(r.searchParams.delete(`a`),r.searchParams.delete(`p`),t.requestedScope=`project`,t.selectorSource=`url`):a&&a===t.projectId?(r.searchParams.delete(`p`),t.requestedScope=`project`,t.selectorSource=`url`):i&&i===t.accountId?(r.searchParams.delete(`a`),t.requestedScope=`account`,t.selectorSource=`url`):(t.requestedScope=t.projectId?`project`:`account`,t.selectorSource=`persisted`),n&&(r.hash=``),t.recoveryUrl=r.toString(),r}function De({children:e,loadOnboardingStatus:n=!0}){let i=new URLSearchParams(location.hash.substring(1)),a=i.get(`token`),o=i.get(`state`),[s,c]=g(new Z),{verifyState:u,clearState:d}=X(),f=h(!1),m=h(void 0),ee=(e,n=!1)=>{let i=$(new URL(window.location.href),e,n);window.history.replaceState(window.history.state,``,i),r.logger.warn(`Sanitized an unavailable authentication scope`,{vertesia:{account_id:e.accountId,project_id:e.projectId,error_code:t,selector_source:e.selectorSource,requested_scope:e.requestedScope,recovery_action:`url_sanitized`}})},_=(e,t=!1)=>e instanceof R||e instanceof z||e instanceof B||e instanceof V||e instanceof I||e instanceof H?(e instanceof R?ee(e,t):t&&Ee(),s.isLoading=!1,s.authError=e,c(s.clone()),!0):!1;return m.current=()=>{if(f.current){r.logger.debug(`Skipping duplicate auth flow initiation`);return}f.current=!0,r.logger.debug(`Starting auth flow`);let{accountId:e,projectId:t}=k(new URL(window.location.href));if(r.logger.debug(`Selected account and project`,{vertesia:{account_id:e,project_id:t}}),r.isLocalDev&&r.devAuthToken){s.setSession=c,F(e,t,r.devAuthToken).then(e=>{s.login(e.rawToken).then(()=>c(s.clone()))}).catch(n=>{_(n)||(console.error(`Failed to initialize dev auth token`,n),r.logger.error(`Failed to initialize dev auth token`,{vertesia:{account_id:e,project_id:t,error:n}}),s.isLoading=!1,s.authError=n instanceof Error?n:Error(String(n)),c(s.clone()))});return}if(a&&o){s.setSession=c;let i=u(o);i?(console.error(`Auth: invalid state: ${i}`),r.logger.error(`Invalid state: ${i}`,{vertesia:{state:o}}),Y({accountId:e,projectId:t})):d(),F(e,t,a,!1,K()).then(e=>{s.login(e.rawToken,{loadOnboardingStatus:n}).then(()=>{c(s.clone()),Ee()})}).catch(n=>{_(n,!0)||(console.error(`Failed to fetch user token from studio, redirecting to central auth`,n),r.logger.error(`Failed to fetch user token from studio, redirecting to central auth`,{vertesia:{error:n}}),Y({accountId:e,projectId:t}))});return}let i=!1,p,m=()=>{if(!i){if(!s.isLoggedIn()){if(r.logger.debug(`Not logged in & no token/state`,{vertesia:{account_id:e,project_id:t}}),K()){r.logger.debug(`Redirecting to central auth with selection`,{vertesia:{account_id:e,project_id:t}}),Y({accountId:e,projectId:t});return}r.logger.debug(`Host is in Firebase auth allowlist`,{vertesia:{account_id:e,project_id:t}})}p=l(T(),async i=>{i?(r.logger.debug(`Successful login with firebase`,{vertesia:{account_id:e,project_id:t}}),s.setSession=c,await F(e,t,void 0,!1,K()).then(e=>{s.login(e.rawToken,{loadOnboardingStatus:n}).then(()=>c(s.clone()))}).catch(n=>{_(n)||(console.error(`Unexpected failure while fetching a user token`,n),r.logger.error(`Unexpected failure while fetching a user token`,{vertesia:{account_id:e,project_id:t,error:n}}),s.logout(),s.isLoading=!1,s.authError=n,c(s.clone()))})):(r.logger.debug(`Using anonymous user`,{vertesia:{account_id:e,project_id:t}}),s.client.withAuthCallback(void 0),s.logout(),c(s.clone()))})}};return r.authTokenProvider?(s.setSession=c,r.authTokenProvider().then(async r=>{if(!r){m();return}let a=await F(e,t,r,!1,K());await s.login(a.rawToken,{loadOnboardingStatus:n}),i||c(s.clone())}).catch(n=>{_(n)||(console.warn(`Auth: failed to initialize injected auth token`,n),r.logger.warn(`Failed to initialize injected auth token`,{vertesia:{account_id:e,project_id:t,error:n}}),s.isLoading=!1,s.authError=n instanceof Error?n:Error(String(n)),c(s.clone()))}),()=>{i=!0,p?.()}):(m(),()=>{i=!0,p?.()})},p(()=>m.current?.(),[]),v(Q.Provider,{value:s,children:e})}function Oe(){return{tagUserSession:async e=>{let t=window.localStorage.getItem(`composableSignupData`);if(!e){console.error(`No user found -- skipping tagging`);return}t&&window.localStorage.removeItem(`composableSignupData`)},trackEvent:(e,t)=>{r.isProd||console.debug(`track event`,e,t),r.firebase?.apiKey&&o(ne(),e,{...t,debug_mode:!r.isProd})}}}export{V as AuthenticationServiceError,B as CredentialError,Te as DevSessionProvider,y as LastSelectedAccountId_KEY,b as LastSelectedProjectId_KEY,z as NoAccessibleAccountError,R as RequestedScopeUnavailableError,H as RestrictedEnvironmentError,L as STSError,le as TokenAuthorizationError,I as UserNotFoundError,Z as UserSession,Q as UserSessionContext,De as UserSessionProvider,be as clearCentralAuthRoundTripMarker,P as fetchComposableToken,oe as fetchComposableTokenFromFirebaseToken,se as fetchComposableTokenFromVertesiaToken,F as getComposableToken,ce as getCurrentVertesiaToken,ne as getFirebaseAnalytics,w as getFirebaseApp,T as getFirebaseAuth,E as getFirebaseAuthToken,pe as isAuthenticationServiceError,xe as isCentralAuthRedirectPending,fe as isCredentialError,de as isNoAccessibleAccountError,ue as isRequestedScopeUnavailableError,Y as redirectToCentralAuth,k as resolveAuthSelection,$ as sanitizeRejectedScopeUrl,re as setFirebaseTenant,X as useAuthState,Ce as useCurrentTenant,Oe as useUXTracking,Se as useUserSession};
|
|
1
|
+
import{NO_ACCESSIBLE_ACCOUNT_ERROR_CODE as e,REQUESTED_SCOPE_UNAVAILABLE_ERROR_CODE as t,RESTRICTED_ENVIRONMENT_ERROR_CODE as n}from"@vertesia/common";import{Env as r}from"@vertesia/ui/env";import{jwtDecode as i}from"jwt-decode";import{getAnalytics as a,logEvent as o}from"firebase/analytics";import{initializeApp as s}from"firebase/app";import{getAuth as c,onAuthStateChanged as l}from"firebase/auth";import{createContext as u,useCallback as d,useContext as f,useEffect as p,useMemo as m,useRef as h,useState as g}from"react";import{NAMESPACE as ee,i18nInstance as _}from"@vertesia/ui/i18n";import{VertesiaClient as te}from"@vertesia/client";import{jsx as v}from"react/jsx-runtime";const y=`composableai.lastSelectedAccountId`,b=`composableai.lastSelectedProjectId`;let x=null,S=null,C=null;function w(){if(!x)try{if(!r.firebase)throw Error(`Firebase configuration is not available in the environment`);x=s(r.firebase)}catch(e){throw console.error(`Failed to initialize Firebase app:`,e),Error(`Firebase initialization failed - environment may not be properly initialized`,{cause:e})}return x}function ne(){return S||=a(w()),S}function T(){return C||=c(w()),C}async function re(e){if(!e){console.log(`No tenant name or email specified, skipping tenant setup`);return}if(!r.firebase){console.log(`Firebase configuration is not available in the environment`);return}try{e&&console.log(`Resolving tenant ID from email: ${e}`);let t=3,n=250;for(;t>0;)try{let t=await fetch(`/api/resolve-tenant`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({tenantEmail:e}),signal:AbortSignal.timeout(5e3)});if(!t)throw Error(`No response received from tenant API`);if(!t.ok){try{let e=await t.json();console.error(`Failed to resolve tenant ID:`,e.error)}catch{console.error(`Failed to resolve tenant ID: HTTP ${t.status}`)}if(t.status===404){console.warn(`Tenant not found for ${e}`);return}throw Error(`HTTP error ${t.status}`)}let n=await t.json();if(n?.firebaseTenantId){let e=T();return e.tenantId=n.firebaseTenantId,r.firebase.providerType=n.provider??`oidc`,console.log(`Tenant ID set to ${e.tenantId}`),n}console.error(`Invalid response format, missing tenantId for ${e}`);return}catch(e){if(t>1)console.warn(`Tenant resolution failed, retrying in ${n}ms...`,e),await new Promise(e=>setTimeout(e,n)),n*=2,t--;else throw e}}catch(e){console.error(`Error setting Firebase tenant:`,e instanceof Error?e.message:`Unknown error`)}}async function E(e){let t=T().currentUser;return t?t.getIdToken(e).then(n=>(r.logger.debug(`Got Firebase token`,{vertesia:{user_email:t.email,user_name:t.displayName,user_id:t.uid,refresh:e}}),n)).catch(n=>(r.logger.error(`Failed to get Firebase token`,{vertesia:{user_email:t.email,user_name:t.displayName,user_id:t.uid,refresh:e,error:n}}),console.error(`Failed to get access token`,n),null)):(r.logger.warn(`No user found`),Promise.resolve(null))}let D,O;function ie(e,t){if(!e)return;let n=`${b}-${e}`;if(t){let r=localStorage.getItem(n)===t;r&&localStorage.removeItem(n),r&&localStorage.getItem(`composableai.lastSelectedAccountId`)===e&&localStorage.removeItem(y);return}localStorage.getItem(`composableai.lastSelectedAccountId`)===e&&(localStorage.removeItem(y),localStorage.removeItem(n))}function ae(e){try{let t=i(e);return typeof t.email!=`string`||!t.email?void 0:{email:t.email,name:typeof t.name==`string`&&t.name?t.name:void 0}}catch{return}}function k(e){let t=e.searchParams.get(`a`)??void 0,n=e.searchParams.get(`p`)??void 0,r=t??(n===void 0?localStorage.getItem(`composableai.lastSelectedAccountId`)??void 0:void 0);return{accountId:r,projectId:n??localStorage.getItem(`composableai.lastSelectedProjectId-${r}`)??void 0}}function A(e){return e?.replace(/\/+$/,``)}function j(e){return i(e)}function M(e){if(!e)return!1;try{return A(j(e).iss)===A(r.endpoints.sts)}catch{return!1}}function N(e,t,n){let r=j(e);return!(!r.exp||r.exp<=Date.now()/1e3+300||!(r.permissions?.length||r.account_roles?.length||r.project_roles?.length||r.apps?.length)||t&&r.account?.id!==t||n&&r.project?.id!==n)}async function P(a,o,s,c,l=0){r.logger.debug(`Getting/refreshing composable token`,{vertesia:{account_id:o,project_id:s,retry_count:l}});let u=await a();if(!u)throw Error(`No id token found`);let d=r.endpoints.sts;r.logger.debug(`Using STS for token generation`,{vertesia:{account_id:o,project_id:s,sts_url:d}});try{let f=new URL(`${d}/token/issue`),p={type:`user`,account_id:o,project_id:s,expires_at:c?Math.floor(Date.now()/1e3)+c:void 0},m=await fetch(f,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${u}`},body:JSON.stringify(p)}).catch(e=>{throw console.error(`Failed to call STS endpoint`,e),r.logger.error(`Failed to call STS endpoint`,{vertesia:{account_id:o,project_id:s,error:e}}),new V(`The authentication service could not be reached`,d,{cause:e})});if(u&&m?.status===404){r.logger.debug(`404: User not found - calling ensure-user endpoint`,{vertesia:{account_id:o,project_id:s,status:m?.status}});let e=await fetch(`${r.endpoints.studio}/auth/ensure-user`,{method:`POST`,headers:{Authorization:`Bearer ${u}`,"Content-Type":`application/json`}});if(e.status===412){r.logger.debug(`412: No invite found - signup required`,{vertesia:{account_id:o,project_id:s}});let e=i(u);throw e?.email?new I(`User not found - signup required`,e.email):(r.logger.error(`No email found in id token`),Error(`No email found in id token`))}if(e.status===403)throw r.logger.warn(`403: Customer-domain user requires an invite to join`,{vertesia:{account_id:o,project_id:s}}),Error(`Customer-domain user requires an invite to join`);if(!e.ok)throw console.error(`Failed to ensure user exists`,e.status),r.logger.error(`Failed to ensure user exists`,{vertesia:{account_id:o,project_id:s,status:e.status}}),Error(`Failed to ensure user exists`);return r.logger.debug(`User ensured - retrying token generation`,{vertesia:{account_id:o,project_id:s}}),P(a,o,s,c,l)}if(u&&m?.status===412){r.logger.info(`412: auth succeeded but user doesn't exist - signup required`,{vertesia:{account_id:o,project_id:s,status:m?.status}});let e=i(u);throw e?.email?(r.logger.info(`User not found`,{vertesia:{account_id:o,project_id:s,email:e.email}}),new I(`User not found`,e.email)):(r.logger.error(`No email found in id token`),Error(`No email found in id token`))}if(m.status===401)throw r.logger.warn(`STS rejected the identity credential`,{vertesia:{account_id:o,project_id:s,status:m.status}}),new B(`Your sign-in credentials could not be verified`,d,{status:m.status});if(m.status===403){let i=await m.json().catch(()=>void 0),a=ae(u);throw i?.errorCode===n?(r.logger.warn(`403: User lacks early-access for this restricted environment`,{vertesia:{account_id:o,project_id:s}}),new H(i.message??`You don't have sufficient permission to visit unstable environments`)):i?.errorCode===e?(r.logger.warn(`STS identity has no accessible account`,{vertesia:{error_code:i.errorCode,status:m.status}}),new z(d,i.message,a)):!i?.errorCode||i.errorCode===t?(r.logger.warn(`STS rejected the requested account or project`,{vertesia:{account_id:o,project_id:s,status:m.status,error_code:i?.errorCode??t}}),new R(d,o,s,i?.errorCode?i.message:void 0,a)):(r.logger.error(`STS returned an unrecognized forbidden response`,{vertesia:{account_id:o,project_id:s,status:m.status,error_code:i.errorCode}}),new V(`The authentication service rejected the request`,d,{status:m.status,errorCode:i.errorCode,cause:i}))}if(!m.ok){let e=await m.text();throw console.error(`STS token generation failed:`,m.status,e),r.logger.error(`STS token generation failed`,{vertesia:{status:m.status,error:e,account_id:o,project_id:s}}),new V(`The authentication service could not complete sign-in`,d,{status:m.status,cause:e})}let{token:h}=await m.json();return r.logger.debug(`Successfully got token from STS`),h}catch(e){throw e instanceof I||e instanceof L||e instanceof H||e instanceof Error&&e.message===`Customer-domain user requires an invite to join`?e:(console.error(`Failed to get composable token from STS`,e),r.logger.error(`Failed to get composable token from STS`,{vertesia:{account_id:o,project_id:s,error:e}}),new V(`The authentication service could not complete sign-in`,d,{cause:e}))}}async function oe(e,t,n){return P(E,e,t,n)}async function se(e,t,n,r){return P(()=>Promise.resolve(e),t,n,r)}function ce(){return D}async function F(e,t,n,i=!1,a=!1){let o=e??(t===void 0?localStorage.getItem(`composableai.lastSelectedAccountId`)??void 0:void 0),s=t??localStorage.getItem(`composableai.lastSelectedProjectId-${o}`)??void 0,c=r.isLocalDev?r.devAuthToken:void 0,l=c??n??D,u=(!o||O?.account?.id===o)&&(!s||O?.project?.id===s);if(!i&&D&&O&&u&&O.exp>Date.now()/1e3+300)return{rawToken:D,token:O,error:!1};if(!i&&M(l)&&N(l,o,s)){if(D=l,O=j(D),!O.exp)throw Error(`Invalid composable token`);return{rawToken:D,token:O,error:!1}}try{if(!c&&!a&&T().currentUser)D=await oe(o,s);else if(c)c&&(D=c);else{let e=await r.authTokenProvider?.()??n??D;D=e&&M(e)&&N(e,o,s)?e:e?await P(()=>Promise.resolve(e),o,s):void 0}}catch(e){throw(e instanceof R||e instanceof z||e instanceof B)&&(D=void 0,O=void 0,e instanceof R&&ie(o,s)),e}if(!D)throw r.logger.error(`Cannot acquire a composable token`,{vertesia:{account_id:o,project_id:s}}),Error(`Cannot acquire a composable token`);if(O=j(D),!O?.exp||!D)throw console.error(`Invalid composable token`,O),r.logger.error(`Invalid composable token`,{vertesia:{account_id:o,project_id:s}}),Error(`Invalid composable token`);return{rawToken:D,token:O,error:!1}}var I=class extends Error{email;constructor(e,t){super(e),this.name=`UserNotFoundError`,this.email=t}},L=class extends Error{stsURL;status;errorCode;constructor(e,t,n){super(e,n),this.name=`STSError`,this.stsURL=t,this.status=n?.status,this.errorCode=n?.errorCode}},R=class extends L{accountId;projectId;identity;requestedScope;selectorSource;recoveryUrl;constructor(e,n,r,i,a){super(i??`The requested account or project is not available.`,e,{status:403,errorCode:t}),this.accountId=n,this.projectId=r,this.identity=a,this.name=`RequestedScopeUnavailableError`,this.requestedScope=r?`project`:`account`}};const le=R;var z=class extends L{identity;constructor(t,n,r){super(n??`No accessible account is available for this user.`,t,{status:403,errorCode:e}),this.identity=r,this.name=`NoAccessibleAccountError`}},B=class extends L{constructor(e,t,n){super(e,t,n),this.name=`CredentialError`}},V=class extends L{constructor(e,t,n){super(e,t,n),this.name=`AuthenticationServiceError`}};function ue(e){return e instanceof R}function de(e){return e instanceof z}function fe(e){return e instanceof B}function pe(e){return e instanceof V}var H=class extends Error{constructor(e){super(e),this.name=`RestrictedEnvironmentError`}};const U=`auth_state`,W=`auth_state_expiry`;function G(){let e=crypto.randomUUID();return sessionStorage.setItem(U,e),sessionStorage.setItem(W,String(Date.now()+3e5)),e}function me(e){if(!e)return`Missing state`;let t=sessionStorage.getItem(U),n=parseInt(sessionStorage.getItem(W)||`0`,10);if(t!==e)return`State mismatched (${t} !== ${e})`;if(Date.now()>n)return`State expired`}function he(){sessionStorage.removeItem(U),sessionStorage.removeItem(W)}function ge(e){return window.AUTH_MODE===`firebase`}function K(e){return!ge()}function _e({centralAuthUrl:e,stsEndpoint:t,returnUrl:n,state:r,accountId:i,projectId:a}){let o=new URL(n);a&&o.searchParams.set(`p`,a),i&&o.searchParams.set(`a`,i);let s=new URL(e);return s.searchParams.set(`sts`,t),s.searchParams.set(`redirect_uri`,o.toString()),s.searchParams.set(`state`,r),s}function q(){let e=new URL(document.baseURI),t=new URL(window.location.href),n=t.pathname.startsWith(e.pathname)?t:e;return n.hash=``,n}function J(){let e=new URL(document.baseURI);return e.hash=``,e.search=``,e}function ve(e,t){return`vtsauth=${e}; Path=/; Max-Age=${t}; SameSite=Lax${window.location.protocol===`https:`?`; Secure`:``}`}function ye(){document.cookie=ve(`1`,300)}function be(){document.cookie=ve(``,0)}function Y(e={}){let t=_e({centralAuthUrl:`https://internal-auth.vertesia.app/`,stsEndpoint:r.endpoints.sts??`https://sts.vertesia.io`,returnUrl:q(),state:G(),...e});ye(),location.replace(t.toString())}function xe(){if(!K()||r.authTokenProvider||r.isLocalDev&&r.devAuthToken)return!1;let e=new URLSearchParams(window.location.hash.substring(1));return!(e.get(`token`)&&e.get(`state`))}function X(){return{generateState:d(G,[]),verifyState:d(me,[]),clearState:d(he,[])}}var Z=class e{isLoading=!0;client;authError;authToken;setSession;lastSelectedAccount;lastSelectedProject;onboardingComplete;constructor(e,t){this.client=e||new te({serverUrl:r.endpoints.studio,storeUrl:r.endpoints.zeno,tokenServerUrl:r.endpoints.sts}),t&&(this.setSession=t),this.logout=this.logout.bind(this),this.signOut=this.signOut.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(e=>{let t=e?.rawToken;if(!t)throw Error(`No token available`);return this.authToken=i(t),t})}async refreshAuthToken(){let e=(await F(void 0,void 0,void 0,!0))?.rawToken;if(!e)throw Error(`No token available`);return this.authToken=i(e),this.setSession?.(this.clone()),this.authToken}signOut(){this.logout()}getAccount(){return this.authToken?.account}async login(e,t={}){return this.authError=void 0,this.isLoading=!1,this.client.withAuthCallback(()=>this.authCallback),this.authToken=i(e),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(y,this.authToken.account.id),localStorage.setItem(`${b}-${this.authToken.account.id}`,this.authToken.project?.id??``),r.onLogin?.(this.authToken),(t.loadOnboardingStatus??!0)&&await this.fetchOnboardingStatus(),Promise.resolve()}isLoggedIn(){return!!this.authToken}logout(){if(console.log(`Logging out`),K()){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);let e=new URL(`https://internal-auth.vertesia.app/`),t=q();e.pathname=`/logout`,e.searchParams.set(`redirect_uri`,t.toString()),location.replace(e.toString())}else{console.log(`Using Firebase logout`);let e=!!this.authToken;this.authToken&&T().signOut(),this.authError=void 0,this.isLoading=!1,this.authToken=void 0,this.setSession=void 0,this.client.withAuthCallback(void 0),e&&location.replace(J().toString())}}async switchAccount(e){localStorage.setItem(y,e),this&&(this.account&&this.project?localStorage.setItem(`${b}-${this.account.id}`,this.project.id):this.account&&localStorage.removeItem(`${b}-${this.account.id}`));let t=J();t.searchParams.set(`a`,e),window.location.replace(t.toString())}async switchProject(e){this.account&&localStorage.setItem(`${b}-${this.account.id}`,e);let t=J();this.account?.id&&t.searchParams.set(`a`,this.account.id),t.searchParams.set(`p`,e),window.location.replace(t.toString())}async fetchAccounts(){return this.client.accounts.list().then(e=>{if(!this.authToken)throw Error(`No token available`);this.authToken.accounts=e,this.setSession?.(this.clone())}).catch(e=>{throw console.error(`Failed to fetch accounts`,e),e})}async fetchProjects(e){return this.client.projects.list([e]).then(t=>{if(!this.authToken)throw Error(`No token available`);this.authToken={...this.authToken,accounts:this.authToken.accounts?.map(n=>n.id===e?{...n,projects:t.filter(t=>t.account===e)}:n)},this.setSession?.(this.clone())}).catch(e=>{throw console.error(`Failed to fetch projects`,e),e})}async fetchOnboardingStatus(){if(!this.project?.id)return!1;if(this.onboardingComplete)return console.log(`Onboarding already completed`),!1;let e=this.onboardingComplete;try{let t=await this.client.account.onboardingProgress();return this.onboardingComplete=Object.values(t).every(e=>e===!0),e!==this.onboardingComplete&&this.setSession?.(this.clone()),e===!1&&this.onboardingComplete}catch(t){console.error(`Error fetching onboarding status:`,t),this.onboardingComplete=!1,e!==this.onboardingComplete&&this.setSession?.(this.clone())}return!1}clone(){let t=new e(this.client);return t.isLoading=this.isLoading,t.authError=this.authError,t.authToken=this.authToken,t.setSession=this.setSession,t.lastSelectedAccount=this.lastSelectedAccount,t.switchAccount=this.switchAccount,t.onboardingComplete=this.onboardingComplete,t}};const Q=u(void 0);function Se(){let e=f(Q);if(!e)throw Error(`useUserSession must be used within a UserSessionProvider`);return e}function Ce(){let{user:e}=Se(),[t,n]=g(null),[r,i]=g(!0),[a,o]=g(null);return p(()=>{(async()=>{if(!e?.email){n(null),i(!1);return}try{let t=await fetch(`/api/resolve-tenant`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({tenantEmail:e.email})});if(t.ok){let e=await t.json();n(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 n(null)}catch(e){console.error(`Error loading current tenant:`,e),o(_.getFixedT(null,ee)(`errors.failedToLoadTenantConfig`)),n(null)}finally{i(!1)}})()},[e?.email]),{currentTenant:t,isLoading:r,error:a}}function we(e){let[,t]=e.split(`.`);if(!t)throw Error(`Invalid Vertesia auth token`);let n=t.replace(/-/g,`+`).replace(/_/g,`/`).padEnd(Math.ceil(t.length/4)*4,`=`);return JSON.parse(atob(n))}function Te({children:e,token:t}){let n=m(()=>{let e=new Z;e.isLoading=!1;try{e.authToken=we(t),e.client.withAuthCallback(()=>Promise.resolve(`Bearer ${t}`)),localStorage.setItem(y,e.authToken.account.id),localStorage.setItem(`${b}-${e.authToken.account.id}`,e.authToken.project?.id??``)}catch(t){e.authError=t instanceof Error?t:Error(String(t))}return e.clone()},[t]);return v(Q.Provider,{value:n,children:e})}function Ee(){let e=new URL(window.location.href);e.hash=``,window.history.replaceState(window.history.state,``,`${e.pathname}${e.search}`)}function $(e,t,n=!1){let r=new URL(e),i=r.searchParams.get(`a`)??void 0,a=r.searchParams.get(`p`)??void 0;return i&&a&&i===t.accountId&&a===t.projectId?(r.searchParams.delete(`a`),r.searchParams.delete(`p`),t.requestedScope=`project`,t.selectorSource=`url`):a&&a===t.projectId?(r.searchParams.delete(`p`),t.requestedScope=`project`,t.selectorSource=`url`):i&&i===t.accountId?(r.searchParams.delete(`a`),t.requestedScope=`account`,t.selectorSource=`url`):(t.requestedScope=t.projectId?`project`:`account`,t.selectorSource=`persisted`),n&&(r.hash=``),t.recoveryUrl=r.toString(),r}function De({children:e,loadOnboardingStatus:n=!0}){let i=new URLSearchParams(location.hash.substring(1)),a=i.get(`token`),o=i.get(`state`),[s,c]=g(new Z),{verifyState:u,clearState:d}=X(),f=h(!1),m=h(void 0),ee=(e,n=!1)=>{let i=$(new URL(window.location.href),e,n);window.history.replaceState(window.history.state,``,i),r.logger.warn(`Sanitized an unavailable authentication scope`,{vertesia:{account_id:e.accountId,project_id:e.projectId,error_code:t,selector_source:e.selectorSource,requested_scope:e.requestedScope,recovery_action:`url_sanitized`}})},_=(e,t=!1)=>e instanceof R||e instanceof z||e instanceof B||e instanceof V||e instanceof I||e instanceof H?(e instanceof R?ee(e,t):t&&Ee(),s.isLoading=!1,s.authError=e,c(s.clone()),!0):!1;return m.current=()=>{if(f.current){r.logger.debug(`Skipping duplicate auth flow initiation`);return}f.current=!0,r.logger.debug(`Starting auth flow`);let{accountId:e,projectId:t}=k(new URL(window.location.href));if(r.logger.debug(`Selected account and project`,{vertesia:{account_id:e,project_id:t}}),r.isLocalDev&&r.devAuthToken){s.setSession=c,F(e,t,r.devAuthToken).then(e=>{s.login(e.rawToken).then(()=>c(s.clone()))}).catch(n=>{_(n)||(console.error(`Failed to initialize dev auth token`,n),r.logger.error(`Failed to initialize dev auth token`,{vertesia:{account_id:e,project_id:t,error:n}}),s.isLoading=!1,s.authError=n instanceof Error?n:Error(String(n)),c(s.clone()))});return}if(a&&o){s.setSession=c;let i=u(o);i?(console.error(`Auth: invalid state: ${i}`),r.logger.error(`Invalid state: ${i}`,{vertesia:{state:o}}),Y({accountId:e,projectId:t})):d(),F(e,t,a,!1,K()).then(e=>{s.login(e.rawToken,{loadOnboardingStatus:n}).then(()=>{c(s.clone()),Ee()})}).catch(n=>{_(n,!0)||(console.error(`Failed to fetch user token from studio, redirecting to central auth`,n),r.logger.error(`Failed to fetch user token from studio, redirecting to central auth`,{vertesia:{error:n}}),Y({accountId:e,projectId:t}))});return}let i=!1,p,m=()=>{if(!i){if(!s.isLoggedIn()){if(r.logger.debug(`Not logged in & no token/state`,{vertesia:{account_id:e,project_id:t}}),K()){r.logger.debug(`Redirecting to central auth with selection`,{vertesia:{account_id:e,project_id:t}}),Y({accountId:e,projectId:t});return}r.logger.debug(`Host is in Firebase auth allowlist`,{vertesia:{account_id:e,project_id:t}})}p=l(T(),async i=>{i?(r.logger.debug(`Successful login with firebase`,{vertesia:{account_id:e,project_id:t}}),s.setSession=c,await F(e,t,void 0,!1,K()).then(e=>{s.login(e.rawToken,{loadOnboardingStatus:n}).then(()=>c(s.clone()))}).catch(n=>{_(n)||(console.error(`Unexpected failure while fetching a user token`,n),r.logger.error(`Unexpected failure while fetching a user token`,{vertesia:{account_id:e,project_id:t,error:n}}),s.logout(),s.isLoading=!1,s.authError=n,c(s.clone()))})):(r.logger.debug(`Using anonymous user`,{vertesia:{account_id:e,project_id:t}}),s.client.withAuthCallback(void 0),s.logout(),c(s.clone()))})}};return r.authTokenProvider?(s.setSession=c,r.authTokenProvider().then(async r=>{if(!r){m();return}let a=await F(e,t,r,!1,K());await s.login(a.rawToken,{loadOnboardingStatus:n}),i||c(s.clone())}).catch(n=>{_(n)||(console.warn(`Auth: failed to initialize injected auth token`,n),r.logger.warn(`Failed to initialize injected auth token`,{vertesia:{account_id:e,project_id:t,error:n}}),s.isLoading=!1,s.authError=n instanceof Error?n:Error(String(n)),c(s.clone()))}),()=>{i=!0,p?.()}):(m(),()=>{i=!0,p?.()})},p(()=>m.current?.(),[]),v(Q.Provider,{value:s,children:e})}function Oe(){return{tagUserSession:async e=>{let t=window.localStorage.getItem(`composableSignupData`);if(!e){console.error(`No user found -- skipping tagging`);return}t&&window.localStorage.removeItem(`composableSignupData`)},trackEvent:(e,t)=>{r.isProd||console.debug(`track event`,e,t),r.firebase?.apiKey&&o(ne(),e,{...t,debug_mode:!r.isProd})}}}export{V as AuthenticationServiceError,B as CredentialError,Te as DevSessionProvider,y as LastSelectedAccountId_KEY,b as LastSelectedProjectId_KEY,z as NoAccessibleAccountError,R as RequestedScopeUnavailableError,H as RestrictedEnvironmentError,L as STSError,le as TokenAuthorizationError,I as UserNotFoundError,Z as UserSession,Q as UserSessionContext,De as UserSessionProvider,be as clearCentralAuthRoundTripMarker,P as fetchComposableToken,oe as fetchComposableTokenFromFirebaseToken,se as fetchComposableTokenFromVertesiaToken,F as getComposableToken,ce as getCurrentVertesiaToken,ne as getFirebaseAnalytics,w as getFirebaseApp,T as getFirebaseAuth,E as getFirebaseAuthToken,pe as isAuthenticationServiceError,xe as isCentralAuthRedirectPending,fe as isCredentialError,de as isNoAccessibleAccountError,ue as isRequestedScopeUnavailableError,Y as redirectToCentralAuth,k as resolveAuthSelection,$ as sanitizeRejectedScopeUrl,re as setFirebaseTenant,X as useAuthState,Ce as useCurrentTenant,Oe as useUXTracking,Se as useUserSession};
|
|
2
2
|
//# sourceMappingURL=vertesia-ui-session.js.map
|