@kawaiininja/fetch 1.0.59 โ 1.0.61
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/hooks/useCsrf.d.ts +7 -3
- package/dist/hooks/useCsrf.js +101 -55
- package/dist/hooks/useFetch.js +1 -1
- package/package.json +1 -1
package/dist/hooks/useCsrf.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
1
|
+
export interface CsrfOptions {
|
|
2
|
+
force?: boolean;
|
|
3
|
+
}
|
|
4
|
+
export interface CsrfResult {
|
|
5
|
+
fetchCSRF: (options?: CsrfOptions) => Promise<string>;
|
|
3
6
|
clearCsrf: () => Promise<void>;
|
|
4
|
-
}
|
|
7
|
+
}
|
|
8
|
+
export declare function useCsrf(): CsrfResult;
|
package/dist/hooks/useCsrf.js
CHANGED
|
@@ -2,80 +2,126 @@ import { useCallback, useRef } from "react";
|
|
|
2
2
|
import { INTERNAL_HEADER } from "../context/ApiContext";
|
|
3
3
|
import { isNative } from "./platform";
|
|
4
4
|
import { useApiConfig } from "./useApiConfig";
|
|
5
|
+
// --- Helpers ---
|
|
6
|
+
const getFromStorage = (debug) => {
|
|
7
|
+
if (typeof window === "undefined")
|
|
8
|
+
return null;
|
|
9
|
+
const stored = localStorage.getItem("csrf_token");
|
|
10
|
+
if (stored && debug) {
|
|
11
|
+
console.log("%c[useCsrf] ๐พ SUCCESS: Found token in localStorage", "color: #add8e6");
|
|
12
|
+
}
|
|
13
|
+
return stored || null;
|
|
14
|
+
};
|
|
15
|
+
const saveToStorage = (token) => {
|
|
16
|
+
if (typeof window !== "undefined") {
|
|
17
|
+
localStorage.setItem("csrf_token", token);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const fetchFromNetwork = async (apiUrl, debug) => {
|
|
21
|
+
if (debug)
|
|
22
|
+
console.log("%c[useCsrf] ๐ NETWORK: Requesting fresh token from server...", "color: #ffa500");
|
|
23
|
+
const csrfUrl = apiUrl("auth/csrf-token");
|
|
24
|
+
const controller = new AbortController();
|
|
25
|
+
const timeoutId = setTimeout(() => controller.abort(), 15000);
|
|
26
|
+
try {
|
|
27
|
+
const res = await fetch(csrfUrl, {
|
|
28
|
+
method: "GET",
|
|
29
|
+
headers: INTERNAL_HEADER,
|
|
30
|
+
credentials: "include",
|
|
31
|
+
signal: controller.signal,
|
|
32
|
+
});
|
|
33
|
+
if (!res.ok) {
|
|
34
|
+
const errText = `CSRF HTTP Error: ${res.status}`;
|
|
35
|
+
if (debug)
|
|
36
|
+
console.error(`%c[useCsrf] โ NETWORK FAIL: ${errText}`, "color: #ff0000");
|
|
37
|
+
throw new Error(errText);
|
|
38
|
+
}
|
|
39
|
+
const json = await res.json();
|
|
40
|
+
const token = json?.csrfToken;
|
|
41
|
+
if (!token) {
|
|
42
|
+
if (debug)
|
|
43
|
+
console.error("%c[useCsrf] โ RESPONSE FAIL: No token in JSON", "color: #ff0000");
|
|
44
|
+
throw new Error("Missing CSRF token in response");
|
|
45
|
+
}
|
|
46
|
+
if (debug)
|
|
47
|
+
console.log("%c[useCsrf] โ
NETWORK SUCCESS: Received new token", "color: #00ff00");
|
|
48
|
+
return token;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (debug)
|
|
52
|
+
console.error("%c[useCsrf] โ FATAL ERROR:", "color: #ff0000", error);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
clearTimeout(timeoutId);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
// --- Hook ---
|
|
5
60
|
export function useCsrf() {
|
|
6
61
|
const { apiUrl, debug, disableCsrf } = useApiConfig();
|
|
7
62
|
const csrfRef = useRef(null);
|
|
63
|
+
const csrfPromiseRef = useRef(null);
|
|
8
64
|
const clearCsrf = useCallback(async () => {
|
|
9
65
|
if (isNative() || disableCsrf)
|
|
10
66
|
return;
|
|
11
67
|
if (debug)
|
|
12
|
-
console.log("[useCsrf]
|
|
68
|
+
console.log("%c[useCsrf] ๐งน CLEARING: Token and promises wiped.", "color: #ff69b4");
|
|
13
69
|
csrfRef.current = null;
|
|
14
|
-
|
|
70
|
+
csrfPromiseRef.current = null;
|
|
71
|
+
if (typeof window !== "undefined")
|
|
15
72
|
localStorage.removeItem("csrf_token");
|
|
16
|
-
}
|
|
17
73
|
}, [debug, disableCsrf]);
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
console.log(`[useCsrf] fetchCSRF invoked. disableCsrf: ${disableCsrf}`);
|
|
74
|
+
const fetchCSRF = useCallback(async (options) => {
|
|
75
|
+
const { force = false } = options || {};
|
|
21
76
|
if (isNative() || disableCsrf) {
|
|
22
|
-
|
|
77
|
+
if (debug)
|
|
78
|
+
console.log("[useCsrf] โญ๏ธ Skipped (Native or Disabled)");
|
|
23
79
|
return "";
|
|
24
80
|
}
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
81
|
+
if (debug) {
|
|
82
|
+
console.log(`%c[useCsrf] ๐ FETCH: Force=${force} | HasMemory=${!!csrfRef.current} | HasPromise=${!!csrfPromiseRef.current}`, "font-weight: bold; color: #888");
|
|
83
|
+
}
|
|
84
|
+
// 1. Return Memory/Pending (ONLY IF NOT FORCED)
|
|
85
|
+
if (!force) {
|
|
86
|
+
if (csrfRef.current) {
|
|
87
|
+
if (debug)
|
|
88
|
+
console.log("%c[useCsrf] ๐ง CACHE: Using Memory Token", "color: #add8e6");
|
|
89
|
+
return csrfRef.current;
|
|
90
|
+
}
|
|
91
|
+
if (csrfPromiseRef.current) {
|
|
92
|
+
if (debug)
|
|
93
|
+
console.log("%c[useCsrf] โณ PENDING: Joining existing fetch...", "color: #add8e6");
|
|
94
|
+
return csrfPromiseRef.current;
|
|
39
95
|
}
|
|
40
|
-
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
41
98
|
if (debug)
|
|
42
|
-
console.log("[useCsrf]
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
credentials: "include",
|
|
52
|
-
signal: controller.signal,
|
|
53
|
-
});
|
|
54
|
-
clearTimeout(timeoutId);
|
|
55
|
-
if (!res.ok) {
|
|
56
|
-
throw new Error(`CSRF Fetch failed with status: ${res.status}`);
|
|
57
|
-
}
|
|
58
|
-
const json = await res.json();
|
|
59
|
-
const token = json?.csrfToken;
|
|
60
|
-
if (!token)
|
|
61
|
-
throw new Error("Missing CSRF token in response");
|
|
62
|
-
csrfRef.current = token;
|
|
63
|
-
// Store in storage
|
|
64
|
-
if (typeof window !== "undefined") {
|
|
65
|
-
localStorage.setItem("csrf_token", token);
|
|
99
|
+
console.log("%c[useCsrf] ๐ FORCED: Bypassing ALL caches", "font-weight: bold; color: #ffa500");
|
|
100
|
+
}
|
|
101
|
+
const task = async () => {
|
|
102
|
+
// 2. Check Storage (ONLY IF NOT FORCED)
|
|
103
|
+
if (!force) {
|
|
104
|
+
const stored = getFromStorage(debug);
|
|
105
|
+
if (stored) {
|
|
106
|
+
csrfRef.current = stored;
|
|
107
|
+
return stored;
|
|
66
108
|
}
|
|
67
|
-
return token;
|
|
68
|
-
}
|
|
69
|
-
catch (error) {
|
|
70
|
-
clearTimeout(timeoutId);
|
|
71
|
-
console.error("[useCsrf] CSRF Fetch Failed/Timed-out:", error);
|
|
72
|
-
throw error;
|
|
73
109
|
}
|
|
74
|
-
|
|
110
|
+
// 3. Network Fetch
|
|
111
|
+
const newToken = await fetchFromNetwork(apiUrl, debug);
|
|
112
|
+
csrfRef.current = newToken;
|
|
113
|
+
saveToStorage(newToken);
|
|
114
|
+
return newToken;
|
|
115
|
+
};
|
|
116
|
+
const promise = task();
|
|
117
|
+
// Reset promise ref once done so next call starts fresh or uses memory
|
|
118
|
+
const chained = promise.finally(() => {
|
|
119
|
+
if (csrfPromiseRef.current === chained) {
|
|
75
120
|
csrfPromiseRef.current = null;
|
|
76
121
|
}
|
|
77
|
-
})
|
|
78
|
-
|
|
122
|
+
});
|
|
123
|
+
csrfPromiseRef.current = chained;
|
|
124
|
+
return chained;
|
|
79
125
|
}, [apiUrl, debug, disableCsrf]);
|
|
80
126
|
return { fetchCSRF, clearCsrf };
|
|
81
127
|
}
|
package/dist/hooks/useFetch.js
CHANGED
|
@@ -70,7 +70,7 @@ export const useFetch = (endpoint, baseOptions = {}) => {
|
|
|
70
70
|
if (debug)
|
|
71
71
|
console.log(`[useFetch] ๐ CSRF Error on ${reqId}. Retrying with fresh token...`);
|
|
72
72
|
await clearCsrf();
|
|
73
|
-
token = await fetchCSRF();
|
|
73
|
+
token = await fetchCSRF({ force: true });
|
|
74
74
|
res = await performFetch(finalUrl, params.method || "GET", token, params.body, params.headers, { ...stableOptions, ...params }, debug, abortRef.current.signal, apiUrl);
|
|
75
75
|
parsed = await parseResponse(res, params.parseAs || "auto");
|
|
76
76
|
}
|