@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.cjs +123 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +122 -46
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/index.d.cts +0 -2164
- package/dist/index.d.ts +0 -2164
package/dist/index.cjs
CHANGED
|
@@ -45,6 +45,45 @@ var HTTP_METHODS = {
|
|
|
45
45
|
DELETE: "DELETE"
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
+
// src/auth/auth-redirect.ts
|
|
49
|
+
var DEFAULT_AUTH_URL = "https://auth.fluid.app";
|
|
50
|
+
var AUTH_REDIRECT_TOKEN_KEY = "jwt";
|
|
51
|
+
var REDIRECT_TIMESTAMP_KEY = "__fluid_auth_redirect_ts";
|
|
52
|
+
var REDIRECT_COOLDOWN_S = 10;
|
|
53
|
+
function isRedirectLoop() {
|
|
54
|
+
try {
|
|
55
|
+
const ts = sessionStorage.getItem(REDIRECT_TIMESTAMP_KEY);
|
|
56
|
+
if (!ts) return false;
|
|
57
|
+
const elapsed = (Date.now() - Number(ts)) / 1e3;
|
|
58
|
+
return elapsed < REDIRECT_COOLDOWN_S;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function markRedirect() {
|
|
64
|
+
try {
|
|
65
|
+
sessionStorage.setItem(REDIRECT_TIMESTAMP_KEY, String(Date.now()));
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function createDefaultAuthRedirect(authUrl) {
|
|
70
|
+
return () => {
|
|
71
|
+
if (isRedirectLoop()) {
|
|
72
|
+
console.warn(
|
|
73
|
+
"[FluidAuth] Auth redirect suppressed \u2014 possible redirect loop. Check that your auth server returns a token accepted by the API."
|
|
74
|
+
);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
markRedirect();
|
|
78
|
+
const base = authUrl ?? DEFAULT_AUTH_URL;
|
|
79
|
+
const currentUrl = encodeURIComponent(window.location.href);
|
|
80
|
+
window.location.href = `${base}/?redirect_url=${currentUrl}`;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function resolveAuthFailureHandler(onAuthFailure, authUrl) {
|
|
84
|
+
return onAuthFailure ?? createDefaultAuthRedirect(authUrl);
|
|
85
|
+
}
|
|
86
|
+
|
|
48
87
|
// src/client/fluid-client.ts
|
|
49
88
|
var ApiError = class _ApiError extends Error {
|
|
50
89
|
status;
|
|
@@ -88,6 +127,7 @@ function extractErrorMessage(data, fallback) {
|
|
|
88
127
|
}
|
|
89
128
|
function createFluidClient(config) {
|
|
90
129
|
const { baseUrl, getAuthToken, onAuthError, defaultHeaders = {} } = config;
|
|
130
|
+
const effectiveOnAuthError = onAuthError ?? createDefaultAuthRedirect();
|
|
91
131
|
async function buildHeaders(customHeaders) {
|
|
92
132
|
const headers = {
|
|
93
133
|
"Content-Type": "application/json",
|
|
@@ -173,8 +213,9 @@ function createFluidClient(config) {
|
|
|
173
213
|
null
|
|
174
214
|
);
|
|
175
215
|
}
|
|
176
|
-
if (response.status === 401
|
|
177
|
-
|
|
216
|
+
if (response.status === 401) {
|
|
217
|
+
effectiveOnAuthError();
|
|
218
|
+
return null;
|
|
178
219
|
}
|
|
179
220
|
if (!response.ok) {
|
|
180
221
|
try {
|
|
@@ -335,6 +376,16 @@ function createFluidClient(config) {
|
|
|
335
376
|
var ThemeContext = react.createContext(null);
|
|
336
377
|
function applyThemeVariables(theme, container) {
|
|
337
378
|
const target = container ?? document.documentElement;
|
|
379
|
+
const toRemove = [];
|
|
380
|
+
for (let i = 0; i < target.style.length; i++) {
|
|
381
|
+
const prop = target.style[i];
|
|
382
|
+
if (prop.startsWith("--fluid-")) {
|
|
383
|
+
toRemove.push(prop);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
for (const prop of toRemove) {
|
|
387
|
+
target.style.removeProperty(prop);
|
|
388
|
+
}
|
|
338
389
|
for (const [key, value] of Object.entries(theme.config)) {
|
|
339
390
|
target.style.setProperty(`--fluid-${key}`, value);
|
|
340
391
|
}
|
|
@@ -401,7 +452,11 @@ function FluidProvider({
|
|
|
401
452
|
const configRef = react.useRef(config);
|
|
402
453
|
configRef.current = config;
|
|
403
454
|
const client = react.useMemo(
|
|
404
|
-
() => createFluidClient(
|
|
455
|
+
() => createFluidClient({
|
|
456
|
+
...configRef.current,
|
|
457
|
+
getAuthToken: () => configRef.current.getAuthToken?.() ?? null,
|
|
458
|
+
onAuthError: () => configRef.current.onAuthError?.()
|
|
459
|
+
}),
|
|
405
460
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
406
461
|
[config.baseUrl]
|
|
407
462
|
);
|
|
@@ -454,10 +509,12 @@ var URL_PARAMS = {
|
|
|
454
509
|
COMPANY_TOKEN: "fluidCompanyToken"
|
|
455
510
|
};
|
|
456
511
|
|
|
457
|
-
// src/auth/
|
|
512
|
+
// src/auth/browser-utils.ts
|
|
458
513
|
function isBrowser() {
|
|
459
|
-
return typeof window !== "undefined";
|
|
514
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
460
515
|
}
|
|
516
|
+
|
|
517
|
+
// src/auth/url-token.ts
|
|
461
518
|
function extractTokenFromUrl(tokenKey = URL_PARAMS.USER_TOKEN) {
|
|
462
519
|
if (!isBrowser()) {
|
|
463
520
|
return null;
|
|
@@ -528,11 +585,8 @@ function extractAllTokensFromUrl(userTokenKey = URL_PARAMS.USER_TOKEN, companyTo
|
|
|
528
585
|
}
|
|
529
586
|
|
|
530
587
|
// src/auth/token-storage.ts
|
|
531
|
-
function isBrowser2() {
|
|
532
|
-
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
533
|
-
}
|
|
534
588
|
function parseCookies() {
|
|
535
|
-
if (!
|
|
589
|
+
if (!isBrowser()) {
|
|
536
590
|
return {};
|
|
537
591
|
}
|
|
538
592
|
const cookies = {};
|
|
@@ -549,7 +603,7 @@ function parseCookies() {
|
|
|
549
603
|
return cookies;
|
|
550
604
|
}
|
|
551
605
|
function setCookie(name, value, options = {}) {
|
|
552
|
-
if (!
|
|
606
|
+
if (!isBrowser()) {
|
|
553
607
|
return;
|
|
554
608
|
}
|
|
555
609
|
const {
|
|
@@ -568,13 +622,13 @@ function setCookie(name, value, options = {}) {
|
|
|
568
622
|
document.cookie = cookieString;
|
|
569
623
|
}
|
|
570
624
|
function deleteCookie(name, path = "/") {
|
|
571
|
-
if (!
|
|
625
|
+
if (!isBrowser()) {
|
|
572
626
|
return;
|
|
573
627
|
}
|
|
574
628
|
document.cookie = `${name}=; path=${path}; max-age=0`;
|
|
575
629
|
}
|
|
576
630
|
function getStoredToken(config) {
|
|
577
|
-
if (!
|
|
631
|
+
if (!isBrowser()) {
|
|
578
632
|
return null;
|
|
579
633
|
}
|
|
580
634
|
const cookieKey = config?.cookieKey ?? STORAGE_KEYS.AUTH_COOKIE;
|
|
@@ -591,7 +645,7 @@ function getStoredToken(config) {
|
|
|
591
645
|
}
|
|
592
646
|
}
|
|
593
647
|
function storeToken(token, config) {
|
|
594
|
-
if (!
|
|
648
|
+
if (!isBrowser()) {
|
|
595
649
|
return;
|
|
596
650
|
}
|
|
597
651
|
const cookieKey = config?.cookieKey ?? STORAGE_KEYS.AUTH_COOKIE;
|
|
@@ -614,7 +668,7 @@ function storeToken(token, config) {
|
|
|
614
668
|
}
|
|
615
669
|
}
|
|
616
670
|
function clearTokens(config) {
|
|
617
|
-
if (!
|
|
671
|
+
if (!isBrowser()) {
|
|
618
672
|
return;
|
|
619
673
|
}
|
|
620
674
|
const cookieKey = config?.cookieKey ?? STORAGE_KEYS.AUTH_COOKIE;
|
|
@@ -631,16 +685,31 @@ function clearTokens(config) {
|
|
|
631
685
|
function hasStoredToken(config) {
|
|
632
686
|
return getStoredToken(config) !== null;
|
|
633
687
|
}
|
|
688
|
+
|
|
689
|
+
// src/auth/types.ts
|
|
690
|
+
var USER_TYPES = {
|
|
691
|
+
admin: "admin",
|
|
692
|
+
rep: "rep",
|
|
693
|
+
root_admin: "root_admin",
|
|
694
|
+
customer: "customer"
|
|
695
|
+
};
|
|
696
|
+
function isUserType(value) {
|
|
697
|
+
return Object.values(USER_TYPES).includes(value);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// src/auth/token-utils.ts
|
|
634
701
|
function extractPayloadFromJose(decoded) {
|
|
702
|
+
const rawUserType = decoded.user_type;
|
|
703
|
+
const rawOgUserType = decoded.og_user_type;
|
|
635
704
|
return {
|
|
636
|
-
id: decoded.id,
|
|
637
|
-
email: decoded.email,
|
|
638
|
-
full_name: decoded.full_name,
|
|
639
|
-
user_type:
|
|
640
|
-
og_user_type:
|
|
641
|
-
company_id: decoded.company_id,
|
|
705
|
+
id: typeof decoded.id === "number" ? decoded.id : void 0,
|
|
706
|
+
email: typeof decoded.email === "string" ? decoded.email : void 0,
|
|
707
|
+
full_name: typeof decoded.full_name === "string" ? decoded.full_name : void 0,
|
|
708
|
+
user_type: typeof rawUserType === "string" && isUserType(rawUserType) ? rawUserType : "rep",
|
|
709
|
+
og_user_type: typeof rawOgUserType === "string" && isUserType(rawOgUserType) ? rawOgUserType : void 0,
|
|
710
|
+
company_id: typeof decoded.company_id === "number" ? decoded.company_id : void 0,
|
|
642
711
|
exp: decoded.exp,
|
|
643
|
-
auth_type: decoded.auth_type
|
|
712
|
+
auth_type: typeof decoded.auth_type === "string" ? decoded.auth_type : void 0
|
|
644
713
|
};
|
|
645
714
|
}
|
|
646
715
|
function decodeToken(token) {
|
|
@@ -730,17 +799,6 @@ async function verifyToken(token, jwksUrl) {
|
|
|
730
799
|
}
|
|
731
800
|
}
|
|
732
801
|
|
|
733
|
-
// src/auth/types.ts
|
|
734
|
-
var USER_TYPES = {
|
|
735
|
-
admin: "admin",
|
|
736
|
-
rep: "rep",
|
|
737
|
-
root_admin: "root_admin",
|
|
738
|
-
customer: "customer"
|
|
739
|
-
};
|
|
740
|
-
function isUserType(value) {
|
|
741
|
-
return Object.values(USER_TYPES).includes(value);
|
|
742
|
-
}
|
|
743
|
-
|
|
744
802
|
// src/auth/dev-utils.ts
|
|
745
803
|
function isDevBypassActive(devBypass) {
|
|
746
804
|
if (!devBypass) return false;
|
|
@@ -752,12 +810,14 @@ function isDevBypassActive(devBypass) {
|
|
|
752
810
|
}
|
|
753
811
|
function createDevUser() {
|
|
754
812
|
return {
|
|
755
|
-
id:
|
|
813
|
+
id: 99999,
|
|
814
|
+
// Dev placeholder — avoids falsy 0
|
|
756
815
|
email: "dev@localhost",
|
|
757
816
|
full_name: "Dev User",
|
|
758
817
|
user_type: USER_TYPES.rep,
|
|
759
818
|
og_user_type: void 0,
|
|
760
|
-
company_id:
|
|
819
|
+
company_id: 99999,
|
|
820
|
+
// Dev placeholder — avoids falsy 0
|
|
761
821
|
exp: void 0,
|
|
762
822
|
// Never expires
|
|
763
823
|
auth_type: "dev_bypass"
|
|
@@ -776,6 +836,11 @@ function FluidAuthProvider({
|
|
|
776
836
|
const [error, setError] = react.useState(null);
|
|
777
837
|
react.useEffect(() => {
|
|
778
838
|
const initializeAuth = async () => {
|
|
839
|
+
const handleAuthFailure = () => {
|
|
840
|
+
const current = configRef.current;
|
|
841
|
+
const handler = resolveAuthFailureHandler(current?.onAuthFailure, current?.authUrl);
|
|
842
|
+
handler();
|
|
843
|
+
};
|
|
779
844
|
try {
|
|
780
845
|
if (isDevBypassActive(config?.devBypass)) {
|
|
781
846
|
const envToken = undefined.VITE_DEV_TOKEN;
|
|
@@ -803,7 +868,11 @@ function FluidAuthProvider({
|
|
|
803
868
|
}
|
|
804
869
|
const tokenKey = config?.tokenKey ?? "fluidUserToken";
|
|
805
870
|
let candidateToken = extractTokenFromUrl(tokenKey);
|
|
871
|
+
if (!candidateToken && tokenKey !== AUTH_REDIRECT_TOKEN_KEY) {
|
|
872
|
+
candidateToken = extractTokenFromUrl(AUTH_REDIRECT_TOKEN_KEY);
|
|
873
|
+
}
|
|
806
874
|
cleanTokenFromUrl(tokenKey);
|
|
875
|
+
cleanTokenFromUrl(AUTH_REDIRECT_TOKEN_KEY);
|
|
807
876
|
if (!candidateToken) {
|
|
808
877
|
candidateToken = getStoredToken(config);
|
|
809
878
|
}
|
|
@@ -816,7 +885,7 @@ function FluidAuthProvider({
|
|
|
816
885
|
setToken(null);
|
|
817
886
|
setUser(null);
|
|
818
887
|
setError(new Error("JWT signature verification failed"));
|
|
819
|
-
|
|
888
|
+
handleAuthFailure();
|
|
820
889
|
return;
|
|
821
890
|
}
|
|
822
891
|
if (isTokenExpired(candidateToken, config?.gracePeriodMs)) {
|
|
@@ -824,7 +893,7 @@ function FluidAuthProvider({
|
|
|
824
893
|
setToken(null);
|
|
825
894
|
setUser(null);
|
|
826
895
|
setError(new Error("Token has expired"));
|
|
827
|
-
|
|
896
|
+
handleAuthFailure();
|
|
828
897
|
return;
|
|
829
898
|
}
|
|
830
899
|
} else {
|
|
@@ -839,7 +908,7 @@ function FluidAuthProvider({
|
|
|
839
908
|
setToken(null);
|
|
840
909
|
setUser(null);
|
|
841
910
|
setError(new Error(validation.error ?? "Invalid token"));
|
|
842
|
-
|
|
911
|
+
handleAuthFailure();
|
|
843
912
|
return;
|
|
844
913
|
}
|
|
845
914
|
}
|
|
@@ -851,14 +920,14 @@ function FluidAuthProvider({
|
|
|
851
920
|
setToken(null);
|
|
852
921
|
setUser(null);
|
|
853
922
|
setError(new Error("No authentication token found"));
|
|
854
|
-
|
|
923
|
+
handleAuthFailure();
|
|
855
924
|
}
|
|
856
925
|
} catch (err) {
|
|
857
926
|
const error2 = err instanceof Error ? err : new Error("Authentication error");
|
|
858
927
|
setError(error2);
|
|
859
928
|
setToken(null);
|
|
860
929
|
setUser(null);
|
|
861
|
-
|
|
930
|
+
handleAuthFailure();
|
|
862
931
|
} finally {
|
|
863
932
|
setIsLoading(false);
|
|
864
933
|
}
|
|
@@ -1894,7 +1963,19 @@ function AuthError({
|
|
|
1894
1963
|
}
|
|
1895
1964
|
);
|
|
1896
1965
|
}
|
|
1966
|
+
var SPIN_STYLE_ID = "fluid-auth-loading-spin";
|
|
1967
|
+
function ensureSpinStyle() {
|
|
1968
|
+
if (typeof document === "undefined") return;
|
|
1969
|
+
if (document.getElementById(SPIN_STYLE_ID)) return;
|
|
1970
|
+
const style = document.createElement("style");
|
|
1971
|
+
style.id = SPIN_STYLE_ID;
|
|
1972
|
+
style.textContent = `@keyframes spin { to { transform: rotate(360deg); } }`;
|
|
1973
|
+
document.head.appendChild(style);
|
|
1974
|
+
}
|
|
1897
1975
|
function AuthLoading() {
|
|
1976
|
+
react.useEffect(() => {
|
|
1977
|
+
ensureSpinStyle();
|
|
1978
|
+
}, []);
|
|
1898
1979
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1899
1980
|
"div",
|
|
1900
1981
|
{
|
|
@@ -1931,12 +2012,7 @@ function AuthLoading() {
|
|
|
1931
2012
|
},
|
|
1932
2013
|
children: "Authenticating..."
|
|
1933
2014
|
}
|
|
1934
|
-
)
|
|
1935
|
-
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
1936
|
-
@keyframes spin {
|
|
1937
|
-
to { transform: rotate(360deg); }
|
|
1938
|
-
}
|
|
1939
|
-
` })
|
|
2015
|
+
)
|
|
1940
2016
|
]
|
|
1941
2017
|
}
|
|
1942
2018
|
);
|
|
@@ -2353,6 +2429,7 @@ exports.BUILT_IN_THEMES = export_BUILT_IN_THEMES;
|
|
|
2353
2429
|
exports.CORE_COLOR_KEYS = export_CORE_COLOR_KEYS;
|
|
2354
2430
|
exports.CORE_PAGE_IDS = CORE_PAGE_IDS;
|
|
2355
2431
|
exports.CURRENT_REP_QUERY_KEY = CURRENT_REP_QUERY_KEY;
|
|
2432
|
+
exports.DEFAULT_AUTH_URL = DEFAULT_AUTH_URL;
|
|
2356
2433
|
exports.DEFAULT_CORE_COLORS = export_DEFAULT_CORE_COLORS;
|
|
2357
2434
|
exports.FluidAuthProvider = FluidAuthProvider;
|
|
2358
2435
|
exports.FluidProvider = FluidProvider;
|
|
@@ -2370,6 +2447,7 @@ exports.catppuccinMocha = export_catppuccinMocha;
|
|
|
2370
2447
|
exports.clampChroma = export_clampChroma;
|
|
2371
2448
|
exports.cleanTokenFromUrl = cleanTokenFromUrl;
|
|
2372
2449
|
exports.clearTokens = clearTokens;
|
|
2450
|
+
exports.createDefaultAuthRedirect = createDefaultAuthRedirect;
|
|
2373
2451
|
exports.createFluidClient = createFluidClient;
|
|
2374
2452
|
exports.decodeToken = decodeToken;
|
|
2375
2453
|
exports.defaultTheme = export_defaultTheme;
|