@kawaiininja/fetch 1.0.59 → 1.0.60

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,100 @@ 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("[useCsrf] Retrieved from localStorage.");
12
+ return stored || null;
13
+ };
14
+ const saveToStorage = (token) => {
15
+ if (typeof window !== "undefined") {
16
+ localStorage.setItem("csrf_token", token);
17
+ }
18
+ };
19
+ const fetchFromNetwork = async (apiUrl, debug) => {
20
+ if (debug)
21
+ console.log("[useCsrf] Fetching new CSRF token from server...");
22
+ const csrfUrl = apiUrl("auth/csrf-token");
23
+ const controller = new AbortController();
24
+ const timeoutId = setTimeout(() => controller.abort(), 15000);
25
+ try {
26
+ const res = await fetch(csrfUrl, {
27
+ method: "GET",
28
+ headers: INTERNAL_HEADER,
29
+ credentials: "include",
30
+ signal: controller.signal,
31
+ });
32
+ if (!res.ok)
33
+ throw new Error(`CSRF HTTP Error: ${res.status}`);
34
+ const json = await res.json();
35
+ const token = json?.csrfToken;
36
+ if (!token)
37
+ throw new Error("Missing CSRF token in response");
38
+ return token;
39
+ }
40
+ catch (error) {
41
+ if (debug)
42
+ console.error("[useCsrf] Network Error:", error);
43
+ throw error;
44
+ }
45
+ finally {
46
+ clearTimeout(timeoutId);
47
+ }
48
+ };
49
+ // --- Hook ---
5
50
  export function useCsrf() {
6
51
  const { apiUrl, debug, disableCsrf } = useApiConfig();
7
52
  const csrfRef = useRef(null);
53
+ const csrfPromiseRef = useRef(null);
8
54
  const clearCsrf = useCallback(async () => {
9
55
  if (isNative() || disableCsrf)
10
56
  return;
11
57
  if (debug)
12
58
  console.log("[useCsrf] Clearing CSRF token...");
13
59
  csrfRef.current = null;
14
- if (typeof window !== "undefined") {
60
+ if (typeof window !== "undefined")
15
61
  localStorage.removeItem("csrf_token");
16
- }
17
62
  }, [debug, disableCsrf]);
18
- const csrfPromiseRef = useRef(null);
19
- const fetchCSRF = useCallback(async () => {
20
- console.log(`[useCsrf] fetchCSRF invoked. disableCsrf: ${disableCsrf}`);
63
+ const fetchCSRF = useCallback(async (options) => {
64
+ const { force = false } = options || {};
21
65
  if (isNative() || disableCsrf) {
22
- console.log(`[useCsrf] Skipping CSRF. Platform: ${isNative() ? "Native" : "Web"}, Disabled: ${disableCsrf}`);
66
+ if (debug)
67
+ console.log(`[useCsrf] Skipped (Platform/Config)`);
23
68
  return "";
24
69
  }
25
- if (csrfRef.current)
70
+ // 1. Memory Cache
71
+ if (!force && csrfRef.current)
26
72
  return csrfRef.current;
27
- if (csrfPromiseRef.current)
73
+ if (!force && csrfPromiseRef.current)
28
74
  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;
75
+ const task = async () => {
76
+ // 2. Storage Cache
77
+ if (!force) {
78
+ const stored = getFromStorage(debug);
79
+ if (stored) {
80
+ csrfRef.current = stored;
81
+ return stored;
38
82
  }
39
83
  }
40
- // Construct CSRF URL using the context's baseUrl
41
- 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);
66
- }
67
- return token;
68
- }
69
- catch (error) {
70
- clearTimeout(timeoutId);
71
- console.error("[useCsrf] CSRF Fetch Failed/Timed-out:", error);
72
- throw error;
73
- }
74
- finally {
84
+ // 3. Network Fetch
85
+ const newToken = await fetchFromNetwork(apiUrl, debug);
86
+ csrfRef.current = newToken;
87
+ saveToStorage(newToken);
88
+ return newToken;
89
+ };
90
+ const promise = task();
91
+ // Cleanup ref on completion
92
+ const chained = promise.finally(() => {
93
+ if (csrfPromiseRef.current === chained) {
75
94
  csrfPromiseRef.current = null;
76
95
  }
77
- })();
78
- return csrfPromiseRef.current;
96
+ });
97
+ csrfPromiseRef.current = chained;
98
+ return chained;
79
99
  }, [apiUrl, debug, disableCsrf]);
80
100
  return { fetchCSRF, clearCsrf };
81
101
  }
@@ -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.60",
4
4
  "description": "Core fetch utility for Onyx Framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",