@enterprisestandard/react 0.0.18-beta.20260504.2 → 0.0.18-beta.20260506.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -19,13 +19,7 @@ interface SSOProviderProps {
19
19
  log?: Logger;
20
20
  children: ReactNode2;
21
21
  }
22
- interface SSOContext {
23
- user: WorkforceUser | null;
24
- setUser: (user: WorkforceUser | null) => void;
25
- isLoading: boolean;
26
- }
27
22
  declare function SSOProvider({ storageKey: storageKeyProp, userUrl, storage, disableListener, log, children }: SSOProviderProps): React.ReactNode2;
28
- declare function useUser(): SSOContext;
29
23
  declare function getUser(userUrl: string, storageKey?: string): Promise<WorkforceUser | undefined>;
30
24
  declare function logout(logoutUrl: string): Promise<{
31
25
  success: boolean;
@@ -104,4 +98,4 @@ declare function useTenantDirectory(options?: UseTenantDirectoryOptions): {
104
98
  error: Error | null;
105
99
  refresh: () => Promise<void>;
106
100
  };
107
- export { useUser, useTenantDirectory, useTenant, useSessions, useActiveUser, logout, getUser, createTenantUrls, UseTenantDirectoryOptions, TenantSwitcher, TenantProvider, SignedOut, SignedIn, SignInLoading, SessionInfo, SSOProvider };
101
+ export { useTenantDirectory, useTenant, useSessions, useActiveUser, logout, getUser, createTenantUrls, UseTenantDirectoryOptions, TenantSwitcher, TenantProvider, SignedOut, SignedIn, SignInLoading, SessionInfo, SSOProvider };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export*from"@enterprisestandard/core";import{silentLogger as R}from"@enterprisestandard/core";import{createContext as L,useCallback as P,useContext as H,useEffect as O,useState as $}from"react";import{jsx as X}from"react/jsx-runtime";var D=L(void 0),Q=/^[a-zA-Z0-9._-]{1,1024}$/;function Z(n){if(!n.trim())throw Error("SSOProvider storageKey must be a non-empty string.");if(!Q.test(n))throw Error("SSOProvider storageKey must be 1-1024 characters and contain only letters, numbers, dots, underscores, and hyphens.")}var M=(n)=>{if(n===void 0||n==="")return"es.sso.user";return Z(n),n};function b(n,i,t=R){if(typeof window>"u")return null;try{let s=(n==="local"?localStorage:sessionStorage).getItem(i);if(!s)return null;let o=JSON.parse(s);if(o.sso?.expires)o.sso.expires=new Date(o.sso.expires);return o}catch(e){return t.error("Error loading user from storage:",e),null}}async function W(n,i=R){try{let t=await fetch(n);if(t.status===401)return null;if(!t.ok)throw Error(`Failed to fetch user: ${t.status} ${t.statusText}`);let e=await t.json();if(e.sso?.expires&&typeof e.sso.expires==="string")e.sso.expires=new Date(e.sso.expires);return e}catch(t){return i.error("Error fetching user from URL:",t),null}}function G({storageKey:n,userUrl:i,storage:t="memory",disableListener:e=!1,log:s=R,children:o}){let[m,c]=$(null),[y,d]=$(!!i),u=M(n),p=P(()=>{if(t==="memory")return null;return b(t,u,s)},[t,u,s]),l=P((a)=>{if(t==="memory"||typeof window>"u")return;try{let T=t==="local"?localStorage:sessionStorage;if(a===null)T.removeItem(u);else T.setItem(u,JSON.stringify(a))}catch(T){s.error("Error saving user to storage:",T)}},[t,u,s]),r=P((a)=>{c(a),l(a)},[l]),N=P(async()=>{if(!i)return;d(!0);try{let a=await W(i,s);c(a),l(a)}finally{d(!1)}},[i,l,s]);O(()=>{let a=p();if(a)c(a);if(i)N();else d(!1)},[p,i,N]),O(()=>{if(e||t==="memory")return;let a=(x)=>{if(x.key!==u)return;if(x.newValue===null)c(null);else try{let v=JSON.parse(x.newValue);if(v.sso?.expires)v.sso.expires=new Date(v.sso.expires);c(v)}catch(v){s.error("Error parsing user from storage event:",v)}},T=()=>{c(null)};return window.addEventListener("storage",a),window.addEventListener("es-sso-logout",T),()=>{window.removeEventListener("storage",a),window.removeEventListener("es-sso-logout",T)}},[e,t,u,s]);let I={user:m,setUser:r,isLoading:y};return X(D.Provider,{value:I,children:o})}function h(){let n=H(D);if(n===void 0)throw Error("useUser must be used within a SSOProvider");return n}async function Y(n,i){let t=M(i),e=b("local",t);if(e)return e;let s=b("session",t);if(s)return s;if(n){let o=await W(n);if(o)return o}return}async function g(n){try{let i=await fetch(n,{headers:{Accept:"application/json"}});if(!i.ok)return{success:!1,error:`HTTP ${i.status}`};let t=await i.json();if(!t.success)return{success:!1,error:t.message||"Logout failed"};if(typeof window<"u")localStorage.removeItem("es.sso.user"),sessionStorage.removeItem("es.sso.user"),window.dispatchEvent(new CustomEvent("es-sso-logout"));return{success:!0}}catch(i){return{success:!1,error:i instanceof Error?i.message:"Network error"}}}import{jsx as j,Fragment as U}from"react/jsx-runtime";function F({complete:n=!1,children:i}){let{isLoading:t}=h();if(t&&!n)return j(U,{children:i});return null}import{jsx as tn,Fragment as nn}from"react/jsx-runtime";function K({children:n}){let{user:i}=h();if(i)return tn(nn,{children:n});return null}import{jsx as sn,Fragment as on}from"react/jsx-runtime";function en({children:n}){let{user:i,isLoading:t}=h();if(i||t)return null;return sn(on,{children:n})}import{buildTenantPath as _,DEFAULT_TENANT_API_NAMESPACE as rn,DEFAULT_TENANT_UI_NAMESPACE as cn,normalizeTenantRoutingStrategy as B}from"@enterprisestandard/core";import{createContext as an,useContext as dn,useMemo as pn}from"react";import{jsx as mn}from"react/jsx-runtime";var k=an(null);function q(n,i){let t=B(i);if(t.type==="jwt"){let o=t.ui?.segments??[],m=t.api?.segments??["api"];return{ui:(c="/")=>z(o,c),api:(c="/")=>z(m,c)}}let e=t.ui??cn,s=t.api??rn;return{ui:(o="/")=>_(n,o,e),api:(o="/")=>_(n,o,s)}}function z(n,i="/"){let t=n.join("/"),e=i.trim().replace(/^\/+/,"");if(!t&&!e)return"/";if(!t)return`/${e}`;if(!e)return`/${t}`;return`/${t}/${e}`}function un({id:n,strategy:i,children:t}){let e=pn(()=>{let s=B(i);return{id:n,strategy:s,urls:q(n,s)}},[i,n]);return mn(k.Provider,{value:e,children:t})}function ln(){let n=dn(k);if(!n)throw Error("useTenant() must be used within a TenantProvider");return n}import{jsx as f,jsxs as w,Fragment as yn}from"react/jsx-runtime";function S(n){if(!n)return"Unknown user";return n.name??n.email??n.id??"Unknown user"}function A(n){return n.tenantName??n.companyName??n.id??n.clientId}function fn({activeSession:n,sessions:i,onSwitch:t,availableTenants:e=[],onLogin:s}){return w("details",{className:"relative inline-block text-left",children:[w("summary",{className:"cursor-pointer select-none rounded-md border px-3 py-2 text-sm",children:[S(n?.user)," · ",n?A(n):"No active session"]}),w("div",{className:"absolute left-0 mt-2 w-64 rounded-md border bg-white p-3 text-sm shadow",children:[f("div",{className:"mb-2 text-gray-500 text-xs uppercase",children:"Active"}),f("div",{className:"mb-4",children:n?w("div",{className:"rounded-md border px-2 py-1",children:[A(n)," · ",S(n.user)]}):f("div",{className:"text-gray-500",children:"No active session"})}),f("div",{className:"mb-2 text-gray-500 text-xs uppercase",children:"Sessions"}),w("div",{className:"space-y-2",children:[i.length===0&&f("div",{className:"text-gray-500",children:"No sessions"}),i.map((o)=>w("button",{type:"button",className:"w-full rounded-md border px-2 py-1 text-left hover:bg-gray-50",onClick:()=>t(o.clientId),children:[A(o)," · ",S(o.user)]},o.clientId))]}),e.length>0&&w(yn,{children:[f("div",{className:"mt-4 mb-2 text-gray-500 text-xs uppercase",children:"Login to another tenant"}),f("div",{className:"space-y-2",children:e.map((o)=>f("button",{type:"button",className:"w-full rounded-md border px-2 py-1 text-left hover:bg-gray-50",onClick:()=>s?.(o.clientId),children:o.tenantName??o.companyName??o.id??o.clientId},o.clientId))})]})]})]})}import{useCallback as V,useEffect as Tn,useState as E}from"react";function J(n={}){let i=n.sessionsUrl??"/api/sessions",t=n.switchUrl??"/api/sessions/switch",[e,s]=E([]),[o,m]=E(null),[c,y]=E(!0),d=V(async()=>{y(!0);try{let p=await fetch(i,{headers:{Accept:"application/json"}});if(!p.ok||p.redirected){s([]),m(null);return}if(!(p.headers.get("content-type")||"").includes("application/json")){s([]),m(null);return}let r=await p.json();s(r.sessions??[]);let N=r.sessions?.find((I)=>I.clientId===r.activeSession)??null;m(N)}catch{s([]),m(null)}finally{y(!1)}},[i]),u=V(async(p,l)=>{let r=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({clientId:p,redirectTo:l})});if(r.redirected&&typeof window<"u"){window.location.assign(r.url);return}if(!r.ok){if(typeof window<"u")window.location.assign(`/api/auth/login?clientId=${p}`);return}if(await d(),l&&typeof window<"u")window.location.assign(l)},[d,t]);return Tn(()=>{d()},[d]),{activeSession:o,sessions:e,isLoading:c,refresh:d,switchSession:u}}function hn(n){let{activeSession:i,isLoading:t}=J({sessionsUrl:n});return{user:i?.user??null,isLoading:t}}import{useCallback as wn,useEffect as Nn,useMemo as vn,useState as C}from"react";function Pn(n={}){let i=n.tenantManagerBaseUrl??"/api/tenants",[t,e]=C(null),[s,o]=C(!0),[m,c]=C(null),y=vn(()=>{let r=new URL(i,"http://localhost");if(n.start!=null)r.searchParams.set("start",String(n.start));if(n.limit!=null)r.searchParams.set("limit",String(n.limit));if(n.order)r.searchParams.set("order",n.order);return`${r.pathname}${r.search}`},[n.limit,n.order,n.start,i]),d=wn(async()=>{o(!0),c(null);try{let r=await fetch(y);if(!r.ok){e(null),c(Error(`Tenant directory request failed with status ${r.status}`));return}let N=await r.json();e(N)}catch(r){e(null),c(r instanceof Error?r:Error("Failed to load tenant directory"))}finally{o(!1)}},[y]);Nn(()=>{d()},[d]);let u=t?.tenants??{},p=t?.ids??[],l=p.map((r)=>u[r]).filter((r)=>!!r);return{directory:t,tenants:u,ids:p,items:l,isLoading:s,error:m,refresh:d}}export{h as useUser,Pn as useTenantDirectory,ln as useTenant,J as useSessions,hn as useActiveUser,g as logout,Y as getUser,q as createTenantUrls,fn as TenantSwitcher,un as TenantProvider,en as SignedOut,K as SignedIn,F as SignInLoading,G as SSOProvider};
1
+ export*from"@enterprisestandard/core";import{silentLogger as b}from"@enterprisestandard/core";import{createContext as L,useCallback as P,useContext as H,useEffect as O,useState as $}from"react";import{jsx as X}from"react/jsx-runtime";var D=L(void 0),Q=/^[a-zA-Z0-9._-]{1,1024}$/;function Z(n){if(!n.trim())throw Error("SSOProvider storageKey must be a non-empty string.");if(!Q.test(n))throw Error("SSOProvider storageKey must be 1-1024 characters and contain only letters, numbers, dots, underscores, and hyphens.")}var M=(n)=>{if(n===void 0||n==="")return"es.sso.user";return Z(n),n};function I(n,i,t=b){if(typeof window>"u")return null;try{let s=(n==="local"?localStorage:sessionStorage).getItem(i);if(!s)return null;let o=JSON.parse(s);if(o.sso?.expires)o.sso.expires=new Date(o.sso.expires);return o}catch(e){return t.error("Error loading user from storage:",e),null}}async function W(n,i=b){try{let t=await fetch(n);if(t.status===401)return null;if(!t.ok)throw Error(`Failed to fetch user: ${t.status} ${t.statusText}`);let e=await t.json();if(e.sso?.expires&&typeof e.sso.expires==="string")e.sso.expires=new Date(e.sso.expires);return e}catch(t){return i.error("Error fetching user from URL:",t),null}}function G({storageKey:n,userUrl:i,storage:t="memory",disableListener:e=!1,log:s=b,children:o}){let[m,c]=$(null),[y,d]=$(!!i),u=M(n),p=P(()=>{if(t==="memory")return null;return I(t,u,s)},[t,u,s]),l=P((a)=>{if(t==="memory"||typeof window>"u")return;try{let T=t==="local"?localStorage:sessionStorage;if(a===null)T.removeItem(u);else T.setItem(u,JSON.stringify(a))}catch(T){s.error("Error saving user to storage:",T)}},[t,u,s]),r=P((a)=>{c(a),l(a)},[l]),w=P(async()=>{if(!i)return;d(!0);try{let a=await W(i,s);c(a),l(a)}finally{d(!1)}},[i,l,s]);O(()=>{let a=p();if(a)c(a);if(i)w();else d(!1)},[p,i,w]),O(()=>{if(e||t==="memory")return;let a=(x)=>{if(x.key!==u)return;if(x.newValue===null)c(null);else try{let N=JSON.parse(x.newValue);if(N.sso?.expires)N.sso.expires=new Date(N.sso.expires);c(N)}catch(N){s.error("Error parsing user from storage event:",N)}},T=()=>{c(null)};return window.addEventListener("storage",a),window.addEventListener("es-sso-logout",T),()=>{window.removeEventListener("storage",a),window.removeEventListener("es-sso-logout",T)}},[e,t,u,s]);let S={user:m,setUser:r,isLoading:y};return X(D.Provider,{value:S,children:o})}function v(){let n=H(D);if(n===void 0)throw Error("useSSOContext must be used within a SSOProvider");return n}async function Y(n,i){let t=M(i),e=I("local",t);if(e)return e;let s=I("session",t);if(s)return s;if(n){let o=await W(n);if(o)return o}return}async function g(n){try{let i=await fetch(n,{headers:{Accept:"application/json"}});if(!i.ok)return{success:!1,error:`HTTP ${i.status}`};let t=await i.json();if(!t.success)return{success:!1,error:t.message||"Logout failed"};if(typeof window<"u")localStorage.removeItem("es.sso.user"),sessionStorage.removeItem("es.sso.user"),window.dispatchEvent(new CustomEvent("es-sso-logout"));return{success:!0}}catch(i){return{success:!1,error:i instanceof Error?i.message:"Network error"}}}import{jsx as U,Fragment as j}from"react/jsx-runtime";function F({complete:n=!1,children:i}){let{isLoading:t}=v();if(t&&!n)return U(j,{children:i});return null}import{jsx as tn,Fragment as nn}from"react/jsx-runtime";function K({children:n}){let{user:i}=v();if(i)return tn(nn,{children:n});return null}import{jsx as sn,Fragment as on}from"react/jsx-runtime";function en({children:n}){let{user:i,isLoading:t}=v();if(i||t)return null;return sn(on,{children:n})}import{buildTenantPath as _,DEFAULT_TENANT_API_NAMESPACE as rn,DEFAULT_TENANT_UI_NAMESPACE as cn,normalizeTenantRoutingStrategy as B}from"@enterprisestandard/core";import{createContext as an,useContext as dn,useMemo as pn}from"react";import{jsx as mn}from"react/jsx-runtime";var k=an(null);function q(n,i){let t=B(i);if(t.type==="jwt"){let o=t.ui?.segments??[],m=t.api?.segments??["api"];return{ui:(c="/")=>z(o,c),api:(c="/")=>z(m,c)}}let e=t.ui??cn,s=t.api??rn;return{ui:(o="/")=>_(n,o,e),api:(o="/")=>_(n,o,s)}}function z(n,i="/"){let t=n.join("/"),e=i.trim().replace(/^\/+/,"");if(!t&&!e)return"/";if(!t)return`/${e}`;if(!e)return`/${t}`;return`/${t}/${e}`}function un({id:n,strategy:i,children:t}){let e=pn(()=>{let s=B(i);return{id:n,strategy:s,urls:q(n,s)}},[i,n]);return mn(k.Provider,{value:e,children:t})}function ln(){let n=dn(k);if(!n)throw Error("useTenant() must be used within a TenantProvider");return n}import{jsx as f,jsxs as h,Fragment as yn}from"react/jsx-runtime";function R(n){if(!n)return"Unknown user";return n.name??n.email??n.id??"Unknown user"}function A(n){return n.tenantName??n.companyName??n.id??n.clientId}function fn({activeSession:n,sessions:i,onSwitch:t,availableTenants:e=[],onLogin:s}){return h("details",{className:"relative inline-block text-left",children:[h("summary",{className:"cursor-pointer select-none rounded-md border px-3 py-2 text-sm",children:[R(n?.user)," · ",n?A(n):"No active session"]}),h("div",{className:"absolute left-0 mt-2 w-64 rounded-md border bg-white p-3 text-sm shadow",children:[f("div",{className:"mb-2 text-gray-500 text-xs uppercase",children:"Active"}),f("div",{className:"mb-4",children:n?h("div",{className:"rounded-md border px-2 py-1",children:[A(n)," · ",R(n.user)]}):f("div",{className:"text-gray-500",children:"No active session"})}),f("div",{className:"mb-2 text-gray-500 text-xs uppercase",children:"Sessions"}),h("div",{className:"space-y-2",children:[i.length===0&&f("div",{className:"text-gray-500",children:"No sessions"}),i.map((o)=>h("button",{type:"button",className:"w-full rounded-md border px-2 py-1 text-left hover:bg-gray-50",onClick:()=>t(o.clientId),children:[A(o)," · ",R(o.user)]},o.clientId))]}),e.length>0&&h(yn,{children:[f("div",{className:"mt-4 mb-2 text-gray-500 text-xs uppercase",children:"Login to another tenant"}),f("div",{className:"space-y-2",children:e.map((o)=>f("button",{type:"button",className:"w-full rounded-md border px-2 py-1 text-left hover:bg-gray-50",onClick:()=>s?.(o.clientId),children:o.tenantName??o.companyName??o.id??o.clientId},o.clientId))})]})]})]})}import{useCallback as V,useEffect as Tn,useState as C}from"react";function J(n={}){let i=n.sessionsUrl??"/api/sessions",t=n.switchUrl??"/api/sessions/switch",[e,s]=C([]),[o,m]=C(null),[c,y]=C(!0),d=V(async()=>{y(!0);try{let p=await fetch(i,{headers:{Accept:"application/json"}});if(!p.ok||p.redirected){s([]),m(null);return}if(!(p.headers.get("content-type")||"").includes("application/json")){s([]),m(null);return}let r=await p.json();s(r.sessions??[]);let w=r.sessions?.find((S)=>S.clientId===r.activeSession)??null;m(w)}catch{s([]),m(null)}finally{y(!1)}},[i]),u=V(async(p,l)=>{let r=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({clientId:p,redirectTo:l})});if(r.redirected&&typeof window<"u"){window.location.assign(r.url);return}if(!r.ok){if(typeof window<"u")window.location.assign(`/api/auth/login?clientId=${p}`);return}if(await d(),l&&typeof window<"u")window.location.assign(l)},[d,t]);return Tn(()=>{d()},[d]),{activeSession:o,sessions:e,isLoading:c,refresh:d,switchSession:u}}function hn(n){let{activeSession:i,isLoading:t}=J({sessionsUrl:n});return{user:i?.user??null,isLoading:t}}import{useCallback as wn,useEffect as Nn,useMemo as vn,useState as E}from"react";function Pn(n={}){let i=n.tenantManagerBaseUrl??"/api/tenants",[t,e]=E(null),[s,o]=E(!0),[m,c]=E(null),y=vn(()=>{let r=new URL(i,"http://localhost");if(n.start!=null)r.searchParams.set("start",String(n.start));if(n.limit!=null)r.searchParams.set("limit",String(n.limit));if(n.order)r.searchParams.set("order",n.order);return`${r.pathname}${r.search}`},[n.limit,n.order,n.start,i]),d=wn(async()=>{o(!0),c(null);try{let r=await fetch(y);if(!r.ok){e(null),c(Error(`Tenant directory request failed with status ${r.status}`));return}let w=await r.json();e(w)}catch(r){e(null),c(r instanceof Error?r:Error("Failed to load tenant directory"))}finally{o(!1)}},[y]);Nn(()=>{d()},[d]);let u=t?.tenants??{},p=t?.ids??[],l=p.map((r)=>u[r]).filter((r)=>!!r);return{directory:t,tenants:u,ids:p,items:l,isLoading:s,error:m,refresh:d}}export{Pn as useTenantDirectory,ln as useTenant,J as useSessions,hn as useActiveUser,g as logout,Y as getUser,q as createTenantUrls,fn as TenantSwitcher,un as TenantProvider,en as SignedOut,K as SignedIn,F as SignInLoading,G as SSOProvider};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enterprisestandard/react",
3
- "version": "0.0.18-beta.20260504.2",
3
+ "version": "0.0.18-beta.20260506.1",
4
4
  "description": "Enterprise Standard React Components",
5
5
  "private": false,
6
6
  "author": "enterprisestandard",
@@ -22,7 +22,7 @@
22
22
  "./package.json": "./package.json"
23
23
  },
24
24
  "dependencies": {
25
- "@enterprisestandard/core": "0.0.18-beta.20260504.2"
25
+ "@enterprisestandard/core": "0.0.18-beta.20260506.1"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "react": "^18.0.0 || ^19.0.0",