@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.
@@ -1,4 +1,8 @@
1
- export declare function useCsrf(): {
2
- fetchCSRF: () => Promise<string>;
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;
@@ -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] Clearing CSRF token...");
68
+ console.log("%c[useCsrf] ๐Ÿงน CLEARING: Token and promises wiped.", "color: #ff69b4");
13
69
  csrfRef.current = null;
14
- if (typeof window !== "undefined") {
70
+ csrfPromiseRef.current = null;
71
+ if (typeof window !== "undefined")
15
72
  localStorage.removeItem("csrf_token");
16
- }
17
73
  }, [debug, disableCsrf]);
18
- const csrfPromiseRef = useRef(null);
19
- const fetchCSRF = useCallback(async () => {
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
- console.log(`[useCsrf] Skipping CSRF. Platform: ${isNative() ? "Native" : "Web"}, Disabled: ${disableCsrf}`);
77
+ if (debug)
78
+ console.log("[useCsrf] โญ๏ธ Skipped (Native or Disabled)");
23
79
  return "";
24
80
  }
25
- if (csrfRef.current)
26
- return csrfRef.current;
27
- if (csrfPromiseRef.current)
28
- return csrfPromiseRef.current;
29
- csrfPromiseRef.current = (async () => {
30
- // Try to get from storage first
31
- if (typeof window !== "undefined") {
32
- const storedCsrf = localStorage.getItem("csrf_token");
33
- if (storedCsrf) {
34
- if (debug)
35
- console.log("[useCsrf] Retrieved from localStorage.");
36
- csrfRef.current = storedCsrf;
37
- return storedCsrf;
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
- // Construct CSRF URL using the context's baseUrl
96
+ }
97
+ else {
41
98
  if (debug)
42
- console.log("[useCsrf] Fetching new CSRF token from server...");
43
- const csrfUrl = apiUrl("auth/csrf-token");
44
- const CSRF_TIMEOUT_MS = 15000;
45
- const controller = new AbortController();
46
- const timeoutId = setTimeout(() => controller.abort(), CSRF_TIMEOUT_MS);
47
- try {
48
- const res = await fetch(csrfUrl, {
49
- method: "GET",
50
- headers: INTERNAL_HEADER,
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
- finally {
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
- return csrfPromiseRef.current;
122
+ });
123
+ csrfPromiseRef.current = chained;
124
+ return chained;
79
125
  }, [apiUrl, debug, disableCsrf]);
80
126
  return { fetchCSRF, clearCsrf };
81
127
  }
@@ -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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kawaiininja/fetch",
3
- "version": "1.0.59",
3
+ "version": "1.0.61",
4
4
  "description": "Core fetch utility for Onyx Framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",