@fluid-app/rep-sdk 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -23,6 +23,45 @@ var HTTP_METHODS = {
23
23
  DELETE: "DELETE"
24
24
  };
25
25
 
26
+ // src/auth/auth-redirect.ts
27
+ var DEFAULT_AUTH_URL = "https://auth.fluid.app";
28
+ var AUTH_REDIRECT_TOKEN_KEY = "jwt";
29
+ var REDIRECT_TIMESTAMP_KEY = "__fluid_auth_redirect_ts";
30
+ var REDIRECT_COOLDOWN_S = 10;
31
+ function isRedirectLoop() {
32
+ try {
33
+ const ts = sessionStorage.getItem(REDIRECT_TIMESTAMP_KEY);
34
+ if (!ts) return false;
35
+ const elapsed = (Date.now() - Number(ts)) / 1e3;
36
+ return elapsed < REDIRECT_COOLDOWN_S;
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+ function markRedirect() {
42
+ try {
43
+ sessionStorage.setItem(REDIRECT_TIMESTAMP_KEY, String(Date.now()));
44
+ } catch {
45
+ }
46
+ }
47
+ function createDefaultAuthRedirect(authUrl) {
48
+ return () => {
49
+ if (isRedirectLoop()) {
50
+ console.warn(
51
+ "[FluidAuth] Auth redirect suppressed \u2014 possible redirect loop. Check that your auth server returns a token accepted by the API."
52
+ );
53
+ return;
54
+ }
55
+ markRedirect();
56
+ const base = authUrl ?? DEFAULT_AUTH_URL;
57
+ const currentUrl = encodeURIComponent(window.location.href);
58
+ window.location.href = `${base}/?redirect_url=${currentUrl}`;
59
+ };
60
+ }
61
+ function resolveAuthFailureHandler(onAuthFailure, authUrl) {
62
+ return onAuthFailure ?? createDefaultAuthRedirect(authUrl);
63
+ }
64
+
26
65
  // src/client/fluid-client.ts
27
66
  var ApiError = class _ApiError extends Error {
28
67
  status;
@@ -66,6 +105,7 @@ function extractErrorMessage(data, fallback) {
66
105
  }
67
106
  function createFluidClient(config) {
68
107
  const { baseUrl, getAuthToken, onAuthError, defaultHeaders = {} } = config;
108
+ const effectiveOnAuthError = onAuthError ?? createDefaultAuthRedirect();
69
109
  async function buildHeaders(customHeaders) {
70
110
  const headers = {
71
111
  "Content-Type": "application/json",
@@ -151,8 +191,9 @@ function createFluidClient(config) {
151
191
  null
152
192
  );
153
193
  }
154
- if (response.status === 401 && onAuthError) {
155
- onAuthError();
194
+ if (response.status === 401) {
195
+ effectiveOnAuthError();
196
+ return null;
156
197
  }
157
198
  if (!response.ok) {
158
199
  try {
@@ -313,6 +354,16 @@ function createFluidClient(config) {
313
354
  var ThemeContext = createContext(null);
314
355
  function applyThemeVariables(theme, container) {
315
356
  const target = container ?? document.documentElement;
357
+ const toRemove = [];
358
+ for (let i = 0; i < target.style.length; i++) {
359
+ const prop = target.style[i];
360
+ if (prop.startsWith("--fluid-")) {
361
+ toRemove.push(prop);
362
+ }
363
+ }
364
+ for (const prop of toRemove) {
365
+ target.style.removeProperty(prop);
366
+ }
316
367
  for (const [key, value] of Object.entries(theme.config)) {
317
368
  target.style.setProperty(`--fluid-${key}`, value);
318
369
  }
@@ -379,7 +430,11 @@ function FluidProvider({
379
430
  const configRef = useRef(config);
380
431
  configRef.current = config;
381
432
  const client = useMemo(
382
- () => createFluidClient(configRef.current),
433
+ () => createFluidClient({
434
+ ...configRef.current,
435
+ getAuthToken: () => configRef.current.getAuthToken?.() ?? null,
436
+ onAuthError: () => configRef.current.onAuthError?.()
437
+ }),
383
438
  // eslint-disable-next-line react-hooks/exhaustive-deps
384
439
  [config.baseUrl]
385
440
  );
@@ -432,10 +487,12 @@ var URL_PARAMS = {
432
487
  COMPANY_TOKEN: "fluidCompanyToken"
433
488
  };
434
489
 
435
- // src/auth/url-token.ts
490
+ // src/auth/browser-utils.ts
436
491
  function isBrowser() {
437
- return typeof window !== "undefined";
492
+ return typeof window !== "undefined" && typeof document !== "undefined";
438
493
  }
494
+
495
+ // src/auth/url-token.ts
439
496
  function extractTokenFromUrl(tokenKey = URL_PARAMS.USER_TOKEN) {
440
497
  if (!isBrowser()) {
441
498
  return null;
@@ -506,11 +563,8 @@ function extractAllTokensFromUrl(userTokenKey = URL_PARAMS.USER_TOKEN, companyTo
506
563
  }
507
564
 
508
565
  // src/auth/token-storage.ts
509
- function isBrowser2() {
510
- return typeof window !== "undefined" && typeof document !== "undefined";
511
- }
512
566
  function parseCookies() {
513
- if (!isBrowser2()) {
567
+ if (!isBrowser()) {
514
568
  return {};
515
569
  }
516
570
  const cookies = {};
@@ -527,7 +581,7 @@ function parseCookies() {
527
581
  return cookies;
528
582
  }
529
583
  function setCookie(name, value, options = {}) {
530
- if (!isBrowser2()) {
584
+ if (!isBrowser()) {
531
585
  return;
532
586
  }
533
587
  const {
@@ -546,13 +600,13 @@ function setCookie(name, value, options = {}) {
546
600
  document.cookie = cookieString;
547
601
  }
548
602
  function deleteCookie(name, path = "/") {
549
- if (!isBrowser2()) {
603
+ if (!isBrowser()) {
550
604
  return;
551
605
  }
552
606
  document.cookie = `${name}=; path=${path}; max-age=0`;
553
607
  }
554
608
  function getStoredToken(config) {
555
- if (!isBrowser2()) {
609
+ if (!isBrowser()) {
556
610
  return null;
557
611
  }
558
612
  const cookieKey = config?.cookieKey ?? STORAGE_KEYS.AUTH_COOKIE;
@@ -569,7 +623,7 @@ function getStoredToken(config) {
569
623
  }
570
624
  }
571
625
  function storeToken(token, config) {
572
- if (!isBrowser2()) {
626
+ if (!isBrowser()) {
573
627
  return;
574
628
  }
575
629
  const cookieKey = config?.cookieKey ?? STORAGE_KEYS.AUTH_COOKIE;
@@ -592,7 +646,7 @@ function storeToken(token, config) {
592
646
  }
593
647
  }
594
648
  function clearTokens(config) {
595
- if (!isBrowser2()) {
649
+ if (!isBrowser()) {
596
650
  return;
597
651
  }
598
652
  const cookieKey = config?.cookieKey ?? STORAGE_KEYS.AUTH_COOKIE;
@@ -609,16 +663,31 @@ function clearTokens(config) {
609
663
  function hasStoredToken(config) {
610
664
  return getStoredToken(config) !== null;
611
665
  }
666
+
667
+ // src/auth/types.ts
668
+ var USER_TYPES = {
669
+ admin: "admin",
670
+ rep: "rep",
671
+ root_admin: "root_admin",
672
+ customer: "customer"
673
+ };
674
+ function isUserType(value) {
675
+ return Object.values(USER_TYPES).includes(value);
676
+ }
677
+
678
+ // src/auth/token-utils.ts
612
679
  function extractPayloadFromJose(decoded) {
680
+ const rawUserType = decoded.user_type;
681
+ const rawOgUserType = decoded.og_user_type;
613
682
  return {
614
- id: decoded.id,
615
- email: decoded.email,
616
- full_name: decoded.full_name,
617
- user_type: decoded.user_type ?? "rep",
618
- og_user_type: decoded.og_user_type,
619
- company_id: decoded.company_id,
683
+ id: typeof decoded.id === "number" ? decoded.id : void 0,
684
+ email: typeof decoded.email === "string" ? decoded.email : void 0,
685
+ full_name: typeof decoded.full_name === "string" ? decoded.full_name : void 0,
686
+ user_type: typeof rawUserType === "string" && isUserType(rawUserType) ? rawUserType : "rep",
687
+ og_user_type: typeof rawOgUserType === "string" && isUserType(rawOgUserType) ? rawOgUserType : void 0,
688
+ company_id: typeof decoded.company_id === "number" ? decoded.company_id : void 0,
620
689
  exp: decoded.exp,
621
- auth_type: decoded.auth_type
690
+ auth_type: typeof decoded.auth_type === "string" ? decoded.auth_type : void 0
622
691
  };
623
692
  }
624
693
  function decodeToken(token) {
@@ -708,17 +777,6 @@ async function verifyToken(token, jwksUrl) {
708
777
  }
709
778
  }
710
779
 
711
- // src/auth/types.ts
712
- var USER_TYPES = {
713
- admin: "admin",
714
- rep: "rep",
715
- root_admin: "root_admin",
716
- customer: "customer"
717
- };
718
- function isUserType(value) {
719
- return Object.values(USER_TYPES).includes(value);
720
- }
721
-
722
780
  // src/auth/dev-utils.ts
723
781
  function isDevBypassActive(devBypass) {
724
782
  if (!devBypass) return false;
@@ -730,12 +788,14 @@ function isDevBypassActive(devBypass) {
730
788
  }
731
789
  function createDevUser() {
732
790
  return {
733
- id: 0,
791
+ id: 99999,
792
+ // Dev placeholder — avoids falsy 0
734
793
  email: "dev@localhost",
735
794
  full_name: "Dev User",
736
795
  user_type: USER_TYPES.rep,
737
796
  og_user_type: void 0,
738
- company_id: 0,
797
+ company_id: 99999,
798
+ // Dev placeholder — avoids falsy 0
739
799
  exp: void 0,
740
800
  // Never expires
741
801
  auth_type: "dev_bypass"
@@ -754,6 +814,11 @@ function FluidAuthProvider({
754
814
  const [error, setError] = useState(null);
755
815
  useEffect(() => {
756
816
  const initializeAuth = async () => {
817
+ const handleAuthFailure = () => {
818
+ const current = configRef.current;
819
+ const handler = resolveAuthFailureHandler(current?.onAuthFailure, current?.authUrl);
820
+ handler();
821
+ };
757
822
  try {
758
823
  if (isDevBypassActive(config?.devBypass)) {
759
824
  const envToken = import.meta.env.VITE_DEV_TOKEN;
@@ -781,7 +846,11 @@ function FluidAuthProvider({
781
846
  }
782
847
  const tokenKey = config?.tokenKey ?? "fluidUserToken";
783
848
  let candidateToken = extractTokenFromUrl(tokenKey);
849
+ if (!candidateToken && tokenKey !== AUTH_REDIRECT_TOKEN_KEY) {
850
+ candidateToken = extractTokenFromUrl(AUTH_REDIRECT_TOKEN_KEY);
851
+ }
784
852
  cleanTokenFromUrl(tokenKey);
853
+ cleanTokenFromUrl(AUTH_REDIRECT_TOKEN_KEY);
785
854
  if (!candidateToken) {
786
855
  candidateToken = getStoredToken(config);
787
856
  }
@@ -794,7 +863,7 @@ function FluidAuthProvider({
794
863
  setToken(null);
795
864
  setUser(null);
796
865
  setError(new Error("JWT signature verification failed"));
797
- config?.onAuthFailure?.();
866
+ handleAuthFailure();
798
867
  return;
799
868
  }
800
869
  if (isTokenExpired(candidateToken, config?.gracePeriodMs)) {
@@ -802,7 +871,7 @@ function FluidAuthProvider({
802
871
  setToken(null);
803
872
  setUser(null);
804
873
  setError(new Error("Token has expired"));
805
- config?.onAuthFailure?.();
874
+ handleAuthFailure();
806
875
  return;
807
876
  }
808
877
  } else {
@@ -817,7 +886,7 @@ function FluidAuthProvider({
817
886
  setToken(null);
818
887
  setUser(null);
819
888
  setError(new Error(validation.error ?? "Invalid token"));
820
- config?.onAuthFailure?.();
889
+ handleAuthFailure();
821
890
  return;
822
891
  }
823
892
  }
@@ -829,14 +898,14 @@ function FluidAuthProvider({
829
898
  setToken(null);
830
899
  setUser(null);
831
900
  setError(new Error("No authentication token found"));
832
- config?.onAuthFailure?.();
901
+ handleAuthFailure();
833
902
  }
834
903
  } catch (err) {
835
904
  const error2 = err instanceof Error ? err : new Error("Authentication error");
836
905
  setError(error2);
837
906
  setToken(null);
838
907
  setUser(null);
839
- config?.onAuthFailure?.();
908
+ handleAuthFailure();
840
909
  } finally {
841
910
  setIsLoading(false);
842
911
  }
@@ -1872,7 +1941,19 @@ function AuthError({
1872
1941
  }
1873
1942
  );
1874
1943
  }
1944
+ var SPIN_STYLE_ID = "fluid-auth-loading-spin";
1945
+ function ensureSpinStyle() {
1946
+ if (typeof document === "undefined") return;
1947
+ if (document.getElementById(SPIN_STYLE_ID)) return;
1948
+ const style = document.createElement("style");
1949
+ style.id = SPIN_STYLE_ID;
1950
+ style.textContent = `@keyframes spin { to { transform: rotate(360deg); } }`;
1951
+ document.head.appendChild(style);
1952
+ }
1875
1953
  function AuthLoading() {
1954
+ useEffect(() => {
1955
+ ensureSpinStyle();
1956
+ }, []);
1876
1957
  return /* @__PURE__ */ jsxs(
1877
1958
  "div",
1878
1959
  {
@@ -1909,12 +1990,7 @@ function AuthLoading() {
1909
1990
  },
1910
1991
  children: "Authenticating..."
1911
1992
  }
1912
- ),
1913
- /* @__PURE__ */ jsx("style", { children: `
1914
- @keyframes spin {
1915
- to { transform: rotate(360deg); }
1916
- }
1917
- ` })
1993
+ )
1918
1994
  ]
1919
1995
  }
1920
1996
  );
@@ -2074,6 +2150,6 @@ var export_toDarkMode = themes_exports.toDarkMode;
2074
2150
  var export_toLightMode = themes_exports.toLightMode;
2075
2151
  var export_tokyoNight = themes_exports.tokyoNight;
2076
2152
 
2077
- export { ACTIVITY_SLUGS, AUTH_CONSTANTS, ApiError, AuthError, AuthLoading, export_BUILT_IN_THEMES as BUILT_IN_THEMES, export_CORE_COLOR_KEYS as CORE_COLOR_KEYS, CORE_PAGE_IDS, CURRENT_REP_QUERY_KEY, export_DEFAULT_CORE_COLORS as DEFAULT_CORE_COLORS, FluidAuthProvider, FluidProvider, FluidThemeProvider, PAGE_CATEGORIES, PERMISSIONS_QUERY_KEY, PROFILE_QUERY_KEY, PageTemplateProvider, PageTemplateRegistry, RequireAuth, STORAGE_KEYS, URL_PARAMS, USER_TYPES, export_catppuccinMocha as catppuccinMocha, export_clampChroma as clampChroma, cleanTokenFromUrl, clearTokens, createFluidClient, decodeToken, export_defaultTheme as defaultTheme, export_detectThemeMode as detectThemeMode, export_dracula as dracula, export_everforest as everforest, extractAllTokensFromUrl, extractCompanyTokenFromUrl, extractTokenFromUrl, export_generateColorSwatches as generateColorSwatches, export_generateDualTheme as generateDualTheme, export_generateSmartDualTheme as generateSmartDualTheme, export_generateTheme as generateTheme, export_generateThemeCssVars as generateThemeCssVars, getAvailablePageTemplates, getCorePageTemplates, getOptionalPageTemplates, getProperty, getStoredToken, getTokenExpiration, getTokenTimeRemaining, export_gruvbox as gruvbox, hasData, hasStoredToken, hasTokenInUrl, isActivitySlug, isApiError, isContactStatus, isErrorResult, isIdle, isLoading, isTokenExpired, isUserType, isValidToken, export_mod as mod, export_monokai as monokai, export_nord as nord, export_oklchString as oklchString, export_parseOklch as parseOklch, resolveNavigationPages, export_rosePine as rosePine, export_rotateHue as rotateHue, export_rotateSoft as rotateSoft, screenPropertySchemas, selectProperty, export_solarized as solarized, storeToken, export_toDarkMode as toDarkMode, export_toLightMode as toLightMode, export_tokyoNight as tokyoNight, useActivities, useCalendarEvents, useCatchUps, useContact, useContacts, useConversationMessages, useConversations, useCurrentRep, useFluidApi, useFluidAuth, useFluidAuthContext, useFluidContext, useFluidPermissions, useFluidProfile, useFluidTheme, useMySite, usePageTemplates, useResolvedPages, useThemeContext, useTodos, validateNavigationPages, validateToken };
2153
+ export { ACTIVITY_SLUGS, AUTH_CONSTANTS, ApiError, AuthError, AuthLoading, export_BUILT_IN_THEMES as BUILT_IN_THEMES, export_CORE_COLOR_KEYS as CORE_COLOR_KEYS, CORE_PAGE_IDS, CURRENT_REP_QUERY_KEY, DEFAULT_AUTH_URL, export_DEFAULT_CORE_COLORS as DEFAULT_CORE_COLORS, FluidAuthProvider, FluidProvider, FluidThemeProvider, PAGE_CATEGORIES, PERMISSIONS_QUERY_KEY, PROFILE_QUERY_KEY, PageTemplateProvider, PageTemplateRegistry, RequireAuth, STORAGE_KEYS, URL_PARAMS, USER_TYPES, export_catppuccinMocha as catppuccinMocha, export_clampChroma as clampChroma, cleanTokenFromUrl, clearTokens, createDefaultAuthRedirect, createFluidClient, decodeToken, export_defaultTheme as defaultTheme, export_detectThemeMode as detectThemeMode, export_dracula as dracula, export_everforest as everforest, extractAllTokensFromUrl, extractCompanyTokenFromUrl, extractTokenFromUrl, export_generateColorSwatches as generateColorSwatches, export_generateDualTheme as generateDualTheme, export_generateSmartDualTheme as generateSmartDualTheme, export_generateTheme as generateTheme, export_generateThemeCssVars as generateThemeCssVars, getAvailablePageTemplates, getCorePageTemplates, getOptionalPageTemplates, getProperty, getStoredToken, getTokenExpiration, getTokenTimeRemaining, export_gruvbox as gruvbox, hasData, hasStoredToken, hasTokenInUrl, isActivitySlug, isApiError, isContactStatus, isErrorResult, isIdle, isLoading, isTokenExpired, isUserType, isValidToken, export_mod as mod, export_monokai as monokai, export_nord as nord, export_oklchString as oklchString, export_parseOklch as parseOklch, resolveNavigationPages, export_rosePine as rosePine, export_rotateHue as rotateHue, export_rotateSoft as rotateSoft, screenPropertySchemas, selectProperty, export_solarized as solarized, storeToken, export_toDarkMode as toDarkMode, export_toLightMode as toLightMode, export_tokyoNight as tokyoNight, useActivities, useCalendarEvents, useCatchUps, useContact, useContacts, useConversationMessages, useConversations, useCurrentRep, useFluidApi, useFluidAuth, useFluidAuthContext, useFluidContext, useFluidPermissions, useFluidProfile, useFluidTheme, useMySite, usePageTemplates, useResolvedPages, useThemeContext, useTodos, validateNavigationPages, validateToken };
2078
2154
  //# sourceMappingURL=index.js.map
2079
2155
  //# sourceMappingURL=index.js.map